diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/BatchLoaderRegistry.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/BatchLoaderRegistry.java
new file mode 100644
index 000000000..31cc31de2
--- /dev/null
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/BatchLoaderRegistry.java
@@ -0,0 +1,50 @@
+package com.introproventures.graphql.jpa.query.schema.impl;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.dataloader.DataLoader;
+import org.dataloader.DataLoaderOptions;
+import org.dataloader.DataLoaderRegistry;
+import org.dataloader.MappedBatchLoaderWithContext;
+
+public class BatchLoaderRegistry {
+    private final static Map<String, MappedBatchLoaderWithContext<Object, List<Object>>> mappedToManyBatchLoaders = new LinkedHashMap<>();
+    private final static Map<String, MappedBatchLoaderWithContext<Object, Object>> mappedToOneBatchLoaders = new LinkedHashMap<>();
+    private static BatchLoaderRegistry instance = new BatchLoaderRegistry();
+
+    public static BatchLoaderRegistry getInstance() {
+        return instance;
+    }
+
+    public static void registerToMany(String batchLoaderKey, MappedBatchLoaderWithContext<Object, List<Object>> mappedBatchLoader) {
+        mappedToManyBatchLoaders.putIfAbsent(batchLoaderKey, mappedBatchLoader);
+    }
+
+    public static void registerToOne(String batchLoaderKey, MappedBatchLoaderWithContext<Object, Object> mappedBatchLoader) {
+        mappedToOneBatchLoaders.putIfAbsent(batchLoaderKey, mappedBatchLoader);
+    }
+
+    public static DataLoaderRegistry newDataLoaderRegistry(DataLoaderOptions dataLoaderOptions) {
+        DataLoaderRegistry dataLoaderRegistry = new DataLoaderRegistry();
+
+        mappedToManyBatchLoaders.entrySet()
+                                .forEach(entry -> {
+                                    DataLoader<Object, List<Object>> dataLoader = DataLoader.newMappedDataLoader(entry.getValue(),
+                                                                                                                 dataLoaderOptions);
+                                    dataLoaderRegistry.register(entry.getKey(), dataLoader);
+                                });
+
+        mappedToOneBatchLoaders.entrySet()
+                               .forEach(entry -> {
+                                   DataLoader<Object, Object> dataLoader = DataLoader.newMappedDataLoader(entry.getValue(),
+                                                                                                          dataLoaderOptions);
+                                   dataLoaderRegistry.register(entry.getKey(), dataLoader);
+                               });
+
+        return dataLoaderRegistry;
+
+    }
+
+}
diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContext.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContext.java
index d1232f789..dae052159 100644
--- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContext.java
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContext.java
@@ -16,26 +16,38 @@
 
 package com.introproventures.graphql.jpa.query.schema.impl;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.function.Supplier;
 
+import org.dataloader.DataLoaderRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.introproventures.graphql.jpa.query.schema.GraphQLExecutionInputFactory;
 import com.introproventures.graphql.jpa.query.schema.GraphQLExecutorContext;
-
 import graphql.ExecutionInput;
 import graphql.GraphQL;
 import graphql.GraphQLContext;
+import graphql.execution.instrumentation.ChainedInstrumentation;
 import graphql.execution.instrumentation.Instrumentation;
+import graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation;
+import graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentationOptions;
 import graphql.schema.GraphQLCodeRegistry;
 import graphql.schema.GraphQLSchema;
 import graphql.schema.visibility.GraphqlFieldVisibility;
 
 public class GraphQLJpaExecutorContext implements GraphQLExecutorContext {
-    
+
+    private final static Logger logger = LoggerFactory.getLogger(GraphQLJpaExecutorContext.class);
+
     private final GraphQLSchema graphQLSchema;
     private final GraphQLExecutionInputFactory executionInputFactory;
     private final Supplier<GraphqlFieldVisibility> graphqlFieldVisibility;
     private final Supplier<Instrumentation> instrumentation;
     private final Supplier<GraphQLContext> graphqlContext;
+    private final Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions;
+    private final Supplier<DataLoaderRegistry> dataLoaderRegistry;
 
     private GraphQLJpaExecutorContext(Builder builder) {
         this.graphQLSchema = builder.graphQLSchema;
@@ -43,28 +55,59 @@ private GraphQLJpaExecutorContext(Builder builder) {
         this.graphqlFieldVisibility = builder.graphqlFieldVisibility;
         this.instrumentation = builder.instrumentation;
         this.graphqlContext = builder.graphqlContext;
+        this.dataLoaderDispatcherInstrumentationOptions = builder.dataLoaderDispatcherInstrumentationOptions;
+        this.dataLoaderRegistry = builder.dataLoaderRegistry;
     }
-    
+
     @Override
     public ExecutionInput.Builder newExecutionInput() {
+        DataLoaderRegistry dataLoaderRegistry = newDataLoaderRegistry();
+
+        GraphQLContext context = graphqlContext.get();
+
+        context.put("dataLoaderRegistry", dataLoaderRegistry);
+
         return executionInputFactory.create()
-                                    .context(graphqlContext.get());
+                                    .dataLoaderRegistry(dataLoaderRegistry)
+                                    .context(context);
     }
 
     @Override
     public GraphQL.Builder newGraphQL() {
+        Instrumentation instrumentation = newIstrumentation();
+
         return GraphQL.newGraphQL(getGraphQLSchema())
-                      .instrumentation(instrumentation.get());
+                      .instrumentation(instrumentation);
+    }
+
+    public DataLoaderRegistry newDataLoaderRegistry() {
+        return dataLoaderRegistry.get();
+    }
+
+    public Instrumentation newIstrumentation() {
+        DataLoaderDispatcherInstrumentationOptions options = dataLoaderDispatcherInstrumentationOptions.get();
+
+        if (logger.isDebugEnabled()) {
+            options.includeStatistics(true);
+        }
+
+        DataLoaderDispatcherInstrumentation dispatcherInstrumentation = new DataLoaderDispatcherInstrumentation(options);
+
+        List<Instrumentation> list = Arrays.asList(dispatcherInstrumentation,
+                                                   instrumentation.get());
+
+        return new ChainedInstrumentation(list);
+
     }
-    
+
     @Override
     public GraphQLSchema getGraphQLSchema() {
         GraphQLCodeRegistry codeRegistry = graphQLSchema.getCodeRegistry()
                                                         .transform(builder -> builder.fieldVisibility(graphqlFieldVisibility.get()));
-                
+
         return graphQLSchema.transform(builder -> builder.codeRegistry(codeRegistry));
     }
-    
+
     /**
      * Creates builder to build {@link GraphQLJpaExecutorContext}.
      * @return created builder
@@ -85,9 +128,13 @@ public interface IBuildStage {
         public IBuildStage graphqlFieldVisibility(Supplier<GraphqlFieldVisibility> graphqlFieldVisibility);
 
         public IBuildStage instrumentation(Supplier<Instrumentation> instrumentation);
-        
+
         public IBuildStage graphqlContext(Supplier<GraphQLContext> graphqlContext);
 
+        public IBuildStage dataLoaderDispatcherInstrumentationOptions(Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions);
+
+        public IBuildStage dataLoaderRegistry(Supplier<DataLoaderRegistry> dataLoaderRegistry);
+
         public GraphQLJpaExecutorContext build();
     }
 
@@ -101,6 +148,8 @@ public static final class Builder implements IGraphQLSchemaStage, IBuildStage {
         private Supplier<GraphqlFieldVisibility> graphqlFieldVisibility;
         private Supplier<Instrumentation> instrumentation;
         private Supplier<GraphQLContext> graphqlContext;
+        private Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions;
+        private Supplier<DataLoaderRegistry> dataLoaderRegistry;
 
         private Builder() {
         }
@@ -135,10 +184,23 @@ public IBuildStage graphqlContext(Supplier<GraphQLContext> graphqlContext) {
             return this;
         }
 
+        @Override
+        public IBuildStage dataLoaderDispatcherInstrumentationOptions(Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions) {
+            this.dataLoaderDispatcherInstrumentationOptions = dataLoaderDispatcherInstrumentationOptions;
+
+            return this;
+        }
+
+        @Override
+        public IBuildStage dataLoaderRegistry(Supplier<DataLoaderRegistry> dataLoaderRegistry) {
+            this.dataLoaderRegistry = dataLoaderRegistry;
+
+            return this;
+        }
+
         @Override
         public GraphQLJpaExecutorContext build() {
             return new GraphQLJpaExecutorContext(this);
         }
-
     }
 }
diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContextFactory.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContextFactory.java
index fa6aead17..dfc609f85 100644
--- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContextFactory.java
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaExecutorContextFactory.java
@@ -18,27 +18,46 @@
 
 import java.util.function.Supplier;
 
+import org.dataloader.DataLoaderOptions;
+import org.dataloader.DataLoaderRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.introproventures.graphql.jpa.query.schema.GraphQLExecutionInputFactory;
 import com.introproventures.graphql.jpa.query.schema.GraphQLExecutorContext;
 import com.introproventures.graphql.jpa.query.schema.GraphQLExecutorContextFactory;
-
 import graphql.GraphQLContext;
 import graphql.execution.instrumentation.Instrumentation;
 import graphql.execution.instrumentation.SimpleInstrumentation;
+import graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentationOptions;
 import graphql.schema.GraphQLSchema;
 import graphql.schema.visibility.DefaultGraphqlFieldVisibility;
 import graphql.schema.visibility.GraphqlFieldVisibility;
 
 public class GraphQLJpaExecutorContextFactory implements GraphQLExecutorContextFactory {
-    
+    private final static Logger logger = LoggerFactory.getLogger(GraphQLJpaExecutorContext.class);
+
     private GraphQLExecutionInputFactory executionInputFactory = new GraphQLExecutionInputFactory() {};
     private Supplier<GraphqlFieldVisibility> graphqlFieldVisibility = () -> DefaultGraphqlFieldVisibility.DEFAULT_FIELD_VISIBILITY;
     private Supplier<Instrumentation> instrumentation = () -> new SimpleInstrumentation();
     private Supplier<GraphQLContext> graphqlContext = () -> GraphQLContext.newContext().build();
-    
+    private Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions = () -> {
+        return DataLoaderDispatcherInstrumentationOptions.newOptions();
+    };
+
+    private Supplier<DataLoaderOptions> dataLoaderOptions = () -> DataLoaderOptions.newOptions();
+
+    private Supplier<DataLoaderRegistry> dataLoaderRegistry = () -> {
+        DataLoaderOptions options = dataLoaderOptions.get()
+                                                     .setCachingEnabled(false);
+
+        return BatchLoaderRegistry.newDataLoaderRegistry(options);
+    };
+
+
     public GraphQLJpaExecutorContextFactory() {
     }
-    
+
     @Override
     public GraphQLExecutorContext newExecutorContext(GraphQLSchema graphQLSchema) {
         return GraphQLJpaExecutorContext.builder()
@@ -47,6 +66,8 @@ public GraphQLExecutorContext newExecutorContext(GraphQLSchema graphQLSchema) {
                                         .graphqlFieldVisibility(graphqlFieldVisibility)
                                         .instrumentation(instrumentation)
                                         .graphqlContext(graphqlContext)
+                                        .dataLoaderDispatcherInstrumentationOptions(dataLoaderDispatcherInstrumentationOptions)
+                                        .dataLoaderRegistry(dataLoaderRegistry)
                                         .build();
     }
 
@@ -55,13 +76,13 @@ public GraphQLJpaExecutorContextFactory withGraphqlFieldVisibility(Supplier<Grap
         return this;
     }
 
-    
+
     public GraphQLJpaExecutorContextFactory withInstrumentation(Supplier<Instrumentation> instrumentation) {
         this.instrumentation = instrumentation;
         return this;
     }
 
-    
+
     public GraphQLJpaExecutorContextFactory withExecutionInputFactory(GraphQLExecutionInputFactory executionInputFactory) {
         this.executionInputFactory = executionInputFactory;
         return this;
@@ -71,11 +92,16 @@ public GraphQLJpaExecutorContextFactory withGraphqlContext(Supplier<GraphQLConte
         this.graphqlContext = graphqlContext;
         return this;
     };
-    
+
+    public GraphQLJpaExecutorContextFactory withDataLoaderDispatcherInstrumentationOptions(Supplier<DataLoaderDispatcherInstrumentationOptions> dataLoaderDispatcherInstrumentationOptions) {
+        this.dataLoaderDispatcherInstrumentationOptions = dataLoaderDispatcherInstrumentationOptions;
+        return this;
+    }
+
     public GraphQLExecutionInputFactory getExecutionInputFactory() {
         return executionInputFactory;
     }
-    
+
     public Supplier<GraphqlFieldVisibility> getGraphqlFieldVisibility() {
         return graphqlFieldVisibility;
     }
@@ -84,9 +110,13 @@ public Supplier<Instrumentation> getInstrumentation() {
         return instrumentation;
     }
 
-    
+
     public Supplier<GraphQLContext> getGraphqlContext() {
         return graphqlContext;
     }
 
+    public Supplier<DataLoaderDispatcherInstrumentationOptions> getDataLoaderDispatcherInstrumentationOptions() {
+        return dataLoaderDispatcherInstrumentationOptions;
+    }
+
 }
diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaQueryFactory.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaQueryFactory.java
index bc70cea21..118b9c4b4 100644
--- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaQueryFactory.java
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaQueryFactory.java
@@ -26,6 +26,7 @@
 import static graphql.introspection.Introspection.SchemaMetaFieldDef;
 import static graphql.introspection.Introspection.TypeMetaFieldDef;
 import static graphql.introspection.Introspection.TypeNameMetaFieldDef;
+import static java.util.stream.Collectors.groupingBy;
 
 import java.beans.BeanInfo;
 import java.beans.Introspector;
@@ -45,7 +46,7 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Optional;
-import java.util.function.BiFunction;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -80,7 +81,6 @@
 import com.introproventures.graphql.jpa.query.schema.impl.EntityIntrospector.EntityIntrospectionResult.AttributePropertyDescriptor;
 import com.introproventures.graphql.jpa.query.schema.impl.PredicateFilter.Criteria;
 import com.introproventures.graphql.jpa.query.support.GraphQLSupport;
-
 import graphql.GraphQLException;
 import graphql.execution.MergedField;
 import graphql.execution.ValuesResolver;
@@ -115,11 +115,13 @@
  */
 public final class GraphQLJpaQueryFactory {
 
+    private static final String DESC = "DESC";
+
     private final static Logger logger = LoggerFactory.getLogger(GraphQLJpaQueryFactory.class);
-    
+
     protected static final String WHERE = "where";
     protected static final String OPTIONAL = "optional";
-    
+
     protected static final String HIBERNATE_QUERY_PASS_DISTINCT_THROUGH = "hibernate.query.passDistinctThrough";
     protected static final String ORG_HIBERNATE_CACHEABLE = "org.hibernate.cacheable";
     protected static final String ORG_HIBERNATE_FETCH_SIZE = "org.hibernate.fetchSize";
@@ -146,33 +148,33 @@ private GraphQLJpaQueryFactory(Builder builder) {
 
     public DataFetchingEnvironment getQueryEnvironment(DataFetchingEnvironment environment,
                                                        MergedField queryField) {
-        
-        // Override query environment with associated entity object type and select field 
+
+        // Override query environment with associated entity object type and select field
         return DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment)
                                              .fieldType(getEntityObjectType())
                                              .mergedField(queryField)
                                              .build();
     }
-    
-    public List<Object> queryKeys(DataFetchingEnvironment environment, 
-                                     int firstResult, 
-                                     int maxResults) {
+
+    public List<Object> queryKeys(DataFetchingEnvironment environment,
+                                  int firstResult,
+                                  int maxResults) {
         MergedField queryField = resolveQueryField(environment.getField());
 
-        // Override query environment with associated entity object type and 
+        // Override query environment with associated entity object type and
         final DataFetchingEnvironment queryEnvironment = getQueryEnvironment(environment,
                                                                              queryField);
-        
-        TypedQuery<Object> keysQuery = getKeysQuery(queryEnvironment, 
+
+        TypedQuery<Object> keysQuery = getKeysQuery(queryEnvironment,
                                                     queryEnvironment.getField());
-        
+
         keysQuery.setFirstResult(firstResult)
                  .setMaxResults(maxResults);
 
         if (logger.isDebugEnabled()) {
             logger.info("\nGraphQL JPQL Keys Query String:\n    {}", getJPQLQueryString(keysQuery));
         }
-        
+
         return keysQuery.getResultList();
     }
 
@@ -186,27 +188,34 @@ public List<Object> queryResultList(DataFetchingEnvironment environment,
         // Let's wrap stream into lazy list to pass it downstream
         return ResultStreamWrapper.wrap(resultStream,
                                         maxResults);
-
     }
-    
+
     protected Stream<Object> queryResultStream(DataFetchingEnvironment environment,
                                                int maxResults,
                                                List<Object> keys) {
         MergedField queryField = resolveQueryField(environment.getField());
-        
-        // Override query environment with associated entity object type and 
+
+        // Override query environment with associated entity object type and
         final DataFetchingEnvironment queryEnvironment = getQueryEnvironment(environment,
                                                                              queryField);
         final int fetchSize = Integer.min(maxResults, defaultFetchSize);
         final boolean isDistinct = resolveDistinctArgument(queryEnvironment.getField());
-        
+
         final TypedQuery<Object> query = getQuery(queryEnvironment, queryEnvironment.getField(), isDistinct, keys.toArray());
-        
+
+        // Let's execute query and get wrap result into stream
+        return getResultStream(query, fetchSize, isDistinct);
+    }
+
+    protected <T> Stream<T> getResultStream(TypedQuery<T> query,
+                                            int fetchSize,
+                                            boolean isDistinct) {
+
         // Let' try reduce overhead and disable all caching
         query.setHint(ORG_HIBERNATE_READ_ONLY, true);
-        query.setHint(ORG_HIBERNATE_FETCH_SIZE, fetchSize); 
+        query.setHint(ORG_HIBERNATE_FETCH_SIZE, fetchSize);
         query.setHint(ORG_HIBERNATE_CACHEABLE, false);
-        
+
         // Let's not pass distinct if enabled to have better performance
         if(isDistinct) {
             query.setHint(HIBERNATE_QUERY_PASS_DISTINCT_THROUGH, false);
@@ -218,49 +227,49 @@ protected Stream<Object> queryResultStream(DataFetchingEnvironment environment,
 
         // Let's execute query and get wrap result into stream
         return query.getResultStream()
-                    .peek(entityManager::detach);                
+                    .peek(entityManager::detach);
     }
-    
+
     protected Object querySingleResult(final DataFetchingEnvironment environment) {
         final MergedField queryField = flattenEmbeddedIdArguments(environment.getField());
-        
+
         final DataFetchingEnvironment queryEnvironment = getQueryEnvironment(environment,
                                                                              queryField);
-        
+
         TypedQuery<Object> query = getQuery(queryEnvironment, queryEnvironment.getField(), true);
-        
+
         if (logger.isDebugEnabled()) {
             logger.info("\nGraphQL JPQL Single Result Query String:\n    {}", getJPQLQueryString(query));
         }
-        
+
         return query.getSingleResult();
-    }    
-    
+    }
+
     public Long queryTotalCount(DataFetchingEnvironment environment) {
         final MergedField queryField = flattenEmbeddedIdArguments(environment.getField());
 
         final DataFetchingEnvironment queryEnvironment = getQueryEnvironment(environment,
                                                                              queryField);
-                
+
         TypedQuery<Long> countQuery = getCountQuery(queryEnvironment, queryEnvironment.getField());
-        
+
         if (logger.isDebugEnabled()) {
             logger.info("\nGraphQL JPQL Count Query String:\n    {}", getJPQLQueryString(countQuery));
         }
-        
+
         return countQuery.getSingleResult();
-    }    
+    }
 
     protected <T> TypedQuery<T> getQuery(DataFetchingEnvironment environment, Field field, boolean isDistinct, Object... keys) {
         DataFetchingEnvironment queryEnvironment = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment)
                                                                                  .localContext(Boolean.TRUE) // Fetch mode
                                                                                  .build();
-        
+
         CriteriaQuery<T> criteriaQuery = getCriteriaQuery(queryEnvironment, field, isDistinct, keys);
 
         return entityManager.createQuery(criteriaQuery);
     }
-    
+
     protected TypedQuery<Long> getCountQuery(DataFetchingEnvironment environment, Field field) {
         CriteriaBuilder cb = entityManager.getCriteriaBuilder();
         CriteriaQuery<Long> query = cb.createQuery(Long.class);
@@ -271,24 +280,24 @@ protected TypedQuery<Long> getCountQuery(DataFetchingEnvironment environment, Fi
                                                                                  .localContext(Boolean.FALSE) // Join mode
                                                                                  .build();
         root.alias("root");
-        
+
         query.select(cb.count(root));
-        
+
         List<Predicate> predicates = field.getArguments().stream()
-            .map(it -> getPredicate(cb, root, null, queryEnvironment, it))
+            .map(it -> getPredicate(field, cb, root, null, queryEnvironment, it))
             .filter(it -> it != null)
             .collect(Collectors.toList());
-        
+
         query.where(predicates.toArray(new Predicate[0]));
 
         return entityManager.createQuery(query);
     }
-    
+
     protected TypedQuery<Object> getKeysQuery(DataFetchingEnvironment environment, Field field) {
         CriteriaBuilder cb = entityManager.getCriteriaBuilder();
         CriteriaQuery<Object> query = cb.createQuery(Object.class);
         Root<?> from = query.from(entityType);
-        
+
         from.alias("root");
 
         DataFetchingEnvironment queryEnvironment = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment)
@@ -303,12 +312,12 @@ protected TypedQuery<Object> getKeysQuery(DataFetchingEnvironment environment, F
                                                  .collect(Collectors.toList());
             query.multiselect(selection);
         }
-        
+
         List<Predicate> predicates = field.getArguments().stream()
-            .map(it -> getPredicate(cb, from, null, queryEnvironment, it))
+            .map(it -> getPredicate(field, cb, from, null, queryEnvironment, it))
             .filter(it -> it != null)
             .collect(Collectors.toList());
-        
+
         query.where(predicates.toArray(new Predicate[0]));
 
         GraphQLSupport.fields(field.getSelectionSet())
@@ -319,46 +328,144 @@ protected TypedQuery<Object> getKeysQuery(DataFetchingEnvironment environment, F
                           // Process the orderBy clause
                           mayBeAddOrderBy(selection, query, cb, selectionPath, queryEnvironment);
                       });
-        
+
         mayBeAddDefaultOrderBy(query, from, cb);
-        
+
         return entityManager.createQuery(query);
     }
-    
+
+    protected Map<Object, List<Object>> loadOneToMany(DataFetchingEnvironment environment,
+                                                 Set<Object> keys) {
+        Field field = environment.getField();
+
+        TypedQuery<Object[]> query = getBatchQuery(environment, field, isDefaultDistinct(), keys);
+
+        if (logger.isDebugEnabled()) {
+            logger.info("\nGraphQL JPQL Batch Query String:\n    {}", getJPQLQueryString(query));
+        }
+
+        List<Object[]> resultList = query.getResultList();
+
+        Map<Object, List<Object>> batch = resultList.stream()
+                                                    .collect(groupingBy(t -> t[0],
+                                                                        Collectors.mapping(t -> t[1],
+                                                                                           GraphQLSupport.toResultList())));
+        Map<Object, List<Object>> resultMap = new LinkedHashMap<>();
+
+        keys.forEach(it -> {
+            List<Object> list = batch.getOrDefault(it, Collections.emptyList());
+
+            if (!list.isEmpty()) {
+                list = list.stream()
+                           .filter(GraphQLSupport.distinctByKey(GraphQLSupport::identityToString))
+                           .collect(Collectors.toList());
+            }
+
+            resultMap.put(it, list);
+        });
+
+        return resultMap;
+    }
+
+    protected Map<Object, Object> loadManyToOne(DataFetchingEnvironment environment,
+                                                Set<Object> keys) {
+             Field field = environment.getField();
+
+             TypedQuery<Object[]> query = getBatchQuery(environment, field, isDefaultDistinct(), keys);
+
+             if (logger.isDebugEnabled()) {
+                 logger.info("\nGraphQL JPQL Batch Query String:\n    {}", getJPQLQueryString(query));
+             }
+
+             List<Object[]> resultList = query.getResultList();
+
+             Map<Object, Object> batch = new LinkedHashMap<>();
+
+             resultList.forEach(item -> batch.put(item[0], item[1]));
+
+             Map<Object, Object> resultMap = new LinkedHashMap<>();
+
+             keys.forEach(it -> {
+                 Object list = batch.getOrDefault(it, null);
+
+                 resultMap.put(it, list);
+             });
+
+             return resultMap;
+         }
+
+    @SuppressWarnings( { "rawtypes", "unchecked" } )
+    protected TypedQuery<Object[]> getBatchQuery(DataFetchingEnvironment environment, Field field, boolean isDistinct, Set<Object> keys) {
+
+        SingularAttribute parentIdAttribute = entityType.getId(Object.class);
+
+        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
+        //CriteriaQuery<Object> query = cb.createQuery((Class<Object>) entityType.getJavaType());
+        CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
+        Root<?> from = query.from(entityType);
+
+        DataFetchingEnvironment queryEnvironment = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment)
+                                                                                 .root(query)
+                                                                                 .localContext(Boolean.TRUE)
+                                                                                 .build();
+
+        from.alias("owner");
+
+        // Must use inner join in parent context
+        Join join = from.join(field.getName())
+                        .on(from.get(parentIdAttribute.getName()).in(keys));
+
+        query.multiselect(from.get(parentIdAttribute.getName()),
+                          join.alias(field.getName()));
+
+        List<Predicate> predicates = getFieldPredicates(field, query, cb, from, join, queryEnvironment);
+
+        query.where(
+            predicates.toArray(new Predicate[0])
+        );
+
+        // optionally add default ordering
+        mayBeAddDefaultOrderBy(query, join, cb);
+
+        return entityManager.createQuery(query.distinct(isDistinct));
+    }
+
     @SuppressWarnings( { "rawtypes", "unchecked" } )
-    protected TypedQuery<Object> getCollectionQuery(DataFetchingEnvironment environment, Field field, boolean isDistinct) {
-        
-        Object source = environment.getSource();
-        
+    protected TypedQuery<Object> getBatchCollectionQuery(DataFetchingEnvironment environment, Field field, boolean isDistinct, Set<Object> keys) {
+
         SingularAttribute parentIdAttribute = entityType.getId(Object.class);
-        
-        Object parentIdValue = getAttributeValue(source, parentIdAttribute);
-        
+
         CriteriaBuilder cb = entityManager.getCriteriaBuilder();
-        CriteriaQuery<Object> query = cb.createQuery((Class<Object>) entityType.getJavaType());
+        //CriteriaQuery<Object> query = cb.createQuery((Class<Object>) entityType.getJavaType());
+        CriteriaQuery<Object> query = cb.createQuery();
         Root<?> from = query.from(entityType);
-        
+
+        DataFetchingEnvironment queryEnvironment = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment)
+                                                                                 .root(query)
+                                                                                 .localContext(Boolean.TRUE)
+                                                                                 .build();
+
         from.alias("owner");
-        
+
         // Must use inner join in parent context
         Join join = from.join(field.getName())
-                        .on(cb.in(from.get(parentIdAttribute.getName()))
-                              .value(parentIdValue));
-        
+                        .on(from.get(parentIdAttribute.getName()).in(keys));
+
         query.select(join.alias(field.getName()));
-        
-        List<Predicate> predicates = getFieldPredicates(field, query, cb, from, join, environment);
+
+        List<Predicate> predicates = getFieldPredicates(field, query, cb, from, join, queryEnvironment);
 
         query.where(
             predicates.toArray(new Predicate[0])
         );
-        
-        // optionally add default ordering 
+
+        // optionally add default ordering
         mayBeAddDefaultOrderBy(query, join, cb);
-        
+
         return entityManager.createQuery(query.distinct(isDistinct));
     }
-    
+
+
     @SuppressWarnings("unchecked")
     protected <T> CriteriaQuery<T> getCriteriaQuery(DataFetchingEnvironment environment, Field field, boolean isDistinct, Object... keys) {
         CriteriaBuilder cb = entityManager.getCriteriaBuilder();
@@ -376,7 +483,7 @@ protected <T> CriteriaQuery<T> getCriteriaQuery(DataFetchingEnvironment environm
         if (keys.length > 0) {
             if(hasIdAttribute()) {
                 predicates.add(from.get(idAttributeName()).in(keys));
-            } // array of idClass attributes 
+            } // array of idClass attributes
             else if (hasIdClassAttribue()) {
                 String[] names = idClassAttributeNames();
                 Map<String, List<Object>> idKeys = new HashMap<>();
@@ -388,20 +495,20 @@ else if (hasIdClassAttribue()) {
                                       .forEach(i -> {
                                           idKeys.computeIfAbsent(names[i], key -> new ArrayList<>())
                                                 .add(values[i]);
-                                      });        
+                                      });
                          });
-                
+
                 List<Predicate> idPredicates = Stream.of(names)
                                                      .map(name -> {
                                                          return from.get(name)
                                                                     .in(idKeys.get(name).toArray(new Object[0]));
                                                      })
                                                      .collect(Collectors.toList());
-                
-                predicates.add(cb.and(idPredicates.toArray(new Predicate[0]))); 
+
+                predicates.add(cb.and(idPredicates.toArray(new Predicate[0])));
             }
         }
-        
+
         // Use AND clause to filter results
         if(!predicates.isEmpty())
             query.where(predicates.toArray(new Predicate[0]));
@@ -411,14 +518,7 @@ else if (hasIdClassAttribue()) {
 
         return query.distinct(isDistinct);
     }
-    
-    <A,B,C>  Stream<C> zipped(List<A> lista, List<B> listb, BiFunction<A,B,C> zipper){
-        int shortestLength = Math.min(lista.size(),listb.size());
-        return IntStream.range(0,shortestLength).mapToObj( i -> {
-             return zipper.apply(lista.get(i), listb.get(i));
-        });        
-   }  
-    
+
     protected void mayBeAddOrderBy(Field selectedField, CriteriaQuery<?> query, CriteriaBuilder cb, Path<?> fieldPath, DataFetchingEnvironment environment) {
         // Singular attributes only
         if (fieldPath.getModel() instanceof SingularAttribute) {
@@ -429,18 +529,18 @@ protected void mayBeAddOrderBy(Field selectedField, CriteriaQuery<?> query, Crit
                          .map(argument -> getOrderByValue(argument, environment))
                          .ifPresent(orderBy -> {
                              List<Order> orders = new ArrayList<>(query.getOrderList());
-                             
-                             if ("DESC".equals(orderBy.getName())) {
+
+                             if (DESC.equals(orderBy.getName())) {
                                  orders.add(cb.desc(fieldPath));
                              } else {
                                  orders.add(cb.asc(fieldPath));
                              }
-                             
+
                              query.orderBy(orders);
                          });
         }
     }
-    
+
     protected final List<Predicate> getFieldPredicates(Field field, CriteriaQuery<?> query, CriteriaBuilder cb, Root<?> root, From<?,?> from, DataFetchingEnvironment environment) {
 
         List<Argument> arguments = new ArrayList<>();
@@ -450,12 +550,10 @@ protected final List<Predicate> getFieldPredicates(Field field, CriteriaQuery<?>
         GraphQLSupport.fields(field.getSelectionSet())
                       .filter(selection -> isPersistent(environment, selection.getName()))
                       .forEach(selection -> {
-                          
+
             Path<?> fieldPath = from.get(selection.getName());
             From<?,?> fetch = null;
-            Optional<Argument> optionalArgument = getArgument(selection, OPTIONAL);
             Optional<Argument> whereArgument = getArgument(selection, WHERE);
-            Boolean isOptional = null;
 
             // Build predicate arguments for singular attributes only
             if(fieldPath.getModel() instanceof SingularAttribute) {
@@ -467,12 +565,15 @@ protected final List<Predicate> getFieldPredicates(Field field, CriteriaQuery<?>
                 if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_ONE
                     || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_ONE
                 ) {
-                   // Let's do fugly conversion 
-                   isOptional = optionalArgument.map(it -> getArgumentValue(environment, it, Boolean.class))
-                                                        .orElse(attribute.isOptional());
+                   // Let's do fugly conversion
+                   Boolean isOptional = getOptionalArgumentValue(environment,
+                                                                 selection,
+                                                                 attribute);
 
                    // Let's apply left outer join to retrieve optional associations
-                   fetch = reuseFetch(from, selection.getName(), isOptional);
+                   if(!isOptional || !whereArgument.isPresent()) {
+                       fetch = reuseFetch(from, selection.getName(), isOptional);
+                   }
                 } else if(attribute.getPersistentAttributeType() == PersistentAttributeType.EMBEDDED) {
                     // Process where arguments clauses.
                     arguments.addAll(selection.getArguments()
@@ -481,63 +582,77 @@ protected final List<Predicate> getFieldPredicates(Field field, CriteriaQuery<?>
                                                   .map(it -> new Argument(selection.getName() + "." + it.getName(),
                                                                           it.getValue()))
                                                   .collect(Collectors.toList()));
-                        
+
                 }
             } else {
-                // We must add plural attributes with explicit join fetch 
-                // Let's do fugly conversion 
-                // the many end is a collection, and it is always optional by default (empty collection)
-                isOptional = optionalArgument.map(it -> getArgumentValue(environment, it, Boolean.class))
-                                             .orElse(toManyDefaultOptional);
-
                 GraphQLObjectType objectType = getObjectType(environment);
                 EntityType<?> entityType = getEntityType(objectType);
 
                 PluralAttribute<?, ?, ?> attribute = (PluralAttribute<?, ?, ?>) entityType.getAttribute(selection.getName());
-                
+
+                // We must add plural attributes with explicit join fetch
+                // the many end is a collection, and it is always optional by default (empty collection)
+                Boolean isOptional = getOptionalArgumentValue(environment,
+                                                              selection,
+                                                              attribute);
+
                 // Let's join fetch element collections to avoid filtering their values used where search criteria
                 if(PersistentAttributeType.ELEMENT_COLLECTION == attribute.getPersistentAttributeType()) {
                     from.fetch(selection.getName(), JoinType.LEFT);
-                } else {
+                } else if(!whereArgument.isPresent()) {
                     // Let's apply fetch join to retrieve associated plural attributes
-                    fetch = reuseFetch(from, selection.getName(), isOptional);
+                    if (!hasAnySelectionOrderBy(selection)) {
+                        fetch = reuseFetch(from, selection.getName(), isOptional);
+                    }
                 }
             }
-            // Let's build join fetch graph to avoid Hibernate error: 
+            // Let's build join fetch graph to avoid Hibernate error:
             // "query specified join fetching, but the owner of the fetched association was not present in the select list"
             if(selection.getSelectionSet() != null && fetch != null) {
                 Map<String, Object> variables = environment.getVariables();
-                
+
                 GraphQLFieldDefinition fieldDefinition = getFieldDefinition(environment.getGraphQLSchema(),
                                                                      this.getObjectType(environment),
-                                                                     selection);  
-                
+                                                                     selection);
+
                 List<Argument> values = whereArgument.map(Collections::singletonList)
                                                      .orElse(Collections.emptyList());
-                
+
                 Map<String, Object> fieldArguments = new ValuesResolver().getArgumentValues(fieldDefinition.getArguments(),
                                                                                             values,
                                                                                             variables);
-                
-                DataFetchingEnvironment fieldEnvironment = wherePredicateEnvironment(environment, 
-                                                                                     fieldDefinition, 
+
+                DataFetchingEnvironment fieldEnvironment = wherePredicateEnvironment(environment,
+                                                                                     fieldDefinition,
                                                                                      fieldArguments);
-                
-                predicates.addAll(getFieldPredicates(selection, query, cb, root, fetch, fieldEnvironment));
+
+                predicates.addAll(getFieldPredicates(selection,
+                                                     query,
+                                                     cb,
+                                                     root,
+                                                     fetch,
+                                                     fieldEnvironment));
             }
         });
-        
+
         arguments.addAll(field.getArguments());
 
         arguments.stream()
                  .filter(this::isPredicateArgument)
-                 .map(it -> getPredicate(cb, root, from, environment, it))
+                 .map(it -> getPredicate(field, cb, root, from, environment, it))
                  .filter(it -> it != null)
                  .forEach(predicates::add);
 
         return predicates;
     }
 
+    protected Boolean getOptionalArgumentValue(DataFetchingEnvironment environment,
+                                               Field selection,
+                                               Attribute<?, ?> attribute) {
+        return getArgument(selection, OPTIONAL).map(it -> getArgumentValue(environment, it, Boolean.class))
+                                               .orElseGet(() -> isOptionalAttribute(attribute));
+    }
+
     /**
      * if query orders are empty, then apply default ascending ordering
      * by root id attribute to prevent paging inconsistencies
@@ -564,7 +679,7 @@ protected void mayBeAddDefaultOrderBy(CriteriaQuery<?> query, From<?,?> from, Cr
                 }
             } else {
                 AttributePropertyDescriptor attribute =  attributePropertyDescriptor.get();
-                
+
                 GraphQLDefaultOrderBy order = attribute.getDefaultOrderBy().get();
                 if (order.asc()) {
                     query.orderBy(cb.asc(from.get(attribute.getName())));
@@ -578,7 +693,7 @@ protected void mayBeAddDefaultOrderBy(CriteriaQuery<?> query, From<?,?> from, Cr
     protected boolean isPredicateArgument(Argument argument) {
         return !isOrderByArgument(argument) && !isOptionalArgument(argument);
     }
-    
+
     protected boolean isOrderByArgument(Argument argument) {
         return GraphQLJpaSchemaBuilder.ORDER_BY_PARAM_NAME.equals(argument.getName());
     }
@@ -586,37 +701,37 @@ protected boolean isOrderByArgument(Argument argument) {
     protected boolean isOptionalArgument(Argument argument) {
         return OPTIONAL.equals(argument.getName());
     }
-    
+
     protected Optional<Argument> getArgument(Field selectedField, String argumentName) {
         return selectedField.getArguments()
                              .stream()
                              .filter(it -> it.getName()
                                              .equals(argumentName))
-                             .findFirst();        
+                             .findFirst();
     }
-    
+
     protected <R extends Attribute<?,?>> R getAttribute(String attributeName) {
         return (R) entityType.getAttribute(attributeName);
     }
- 
+
     @SuppressWarnings( "unchecked" )
-    protected Predicate getPredicate(CriteriaBuilder cb, Root<?> from, From<?,?> path, DataFetchingEnvironment environment, Argument argument) {
-        if(isLogicalArgument(argument) || 
-            isDistinctArgument(argument) || isPageArgument(argument) || 
+    protected Predicate getPredicate(Field field, CriteriaBuilder cb, Root<?> from, From<?,?> path, DataFetchingEnvironment environment, Argument argument) {
+        if(isLogicalArgument(argument) ||
+            isDistinctArgument(argument) || isPageArgument(argument) ||
             isAfterArgument(argument) || isFirstArgument(argument) ) {
             return null;
-        } 
-        else if(isWhereArgument(argument)) { 
+        }
+        else if(isWhereArgument(argument)) {
             return getWherePredicate(cb, from, path, argumentEnvironment(environment, argument), argument);
-        } 
+        }
         else if(!argument.getName().contains(".")) {
-            Attribute<?,?> argumentEntityAttribute = getAttribute(environment, argument);
+            Attribute<?,?> argumentEntityAttribute = getAttribute(environment, argument.getName());
 
             // If the argument is a list, let's assume we need to join and do an 'in' clause
             if (argumentEntityAttribute instanceof PluralAttribute) {
                 // Apply left outer join to retrieve optional associations
                 Boolean isFetch = environment.getLocalContext();
-                
+
                 return (isFetch ? reuseFetch(from, argument.getName(), false) : reuseJoin(from, argument.getName(), false))
                     .in(convertValue(environment, argument, argument.getValue()));
             }
@@ -624,9 +739,9 @@ else if(!argument.getName().contains(".")) {
             return cb.equal(path.get(argument.getName()), convertValue(environment, argument, argument.getValue()));
         } else {
             if(!argument.getName().endsWith(".where")) {
-                Path<?> field = getCompoundJoinedPath(path, argument.getName(), false);
+                Path<?> argumentPath = getCompoundJoinedPath(path, argument.getName(), false);
 
-                return cb.equal(field, convertValue(environment, argument, argument.getValue()));
+                return cb.equal(argumentPath, convertValue(environment, argument, argument.getValue()));
             } else {
                 String fieldName = argument.getName().split("\\.")[0];
 
@@ -648,23 +763,23 @@ else if(!argument.getName().contains(".")) {
             }
         }
     }
-    
-    
+
+
 
     @SuppressWarnings( "unchecked" )
     private <R extends Value<?>> R getValue(Argument argument, DataFetchingEnvironment environment) {
         Value<?> value = argument.getValue();
-        
+
         if(VariableReference.class.isInstance(value)) {
             Object variableValue = getVariableReferenceValue((VariableReference) value, environment);
-            
+
             GraphQLArgument graphQLArgument = environment.getExecutionStepInfo()
                                                 .getFieldDefinition()
                                                 .getArgument(argument.getName());
-            
+
             return (R) AstValueHelper.astFromValue(variableValue, graphQLArgument.getType());
         }
-        
+
         return (R) value;
     }
 
@@ -688,12 +803,12 @@ protected Predicate getWherePredicate(CriteriaBuilder cb, Root<?> root,  From<?,
 
         if(whereValue.getChildren().isEmpty())
             return cb.conjunction();
-        
+
         Logical logical = extractLogical(argument);
-        
+
         Map<String, Object> predicateArguments = new LinkedHashMap<>();
         predicateArguments.put(logical.name(), environment.getArguments());
-        
+
         DataFetchingEnvironment predicateDataFetchingEnvironment = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment)
                                                                                                  .arguments(predicateArguments)
                                                                                                  .build();
@@ -708,23 +823,23 @@ protected Predicate getArgumentPredicate(CriteriaBuilder cb, From<?,?> from,
         ObjectValue whereValue = getValue(argument, environment);
 
         if (whereValue.getChildren().isEmpty())
-            return cb.disjunction(); 
-        
+            return cb.disjunction();
+
         Logical logical = extractLogical(argument);
 
         List<Predicate> predicates = new ArrayList<>();
 
         whereValue.getObjectFields().stream()
             .filter(it -> Logical.names().contains(it.getName()))
-            .map(it -> { 
+            .map(it -> {
                 Map<String, Object> arguments = getFieldArguments(environment, it, argument);
-                
+
                 if(it.getValue() instanceof ArrayValue) {
                     return getArgumentsPredicate(cb, from,
                                                 argumentEnvironment(environment, arguments),
                                                 new Argument(it.getName(), it.getValue()));
                 }
-                
+
                 return getArgumentPredicate(cb, from,
                                             argumentEnvironment(environment, arguments),
                                             new Argument(it.getName(), it.getValue()));
@@ -745,57 +860,57 @@ protected Predicate getArgumentPredicate(CriteriaBuilder cb, From<?,?> from,
 
         return getCompoundPredicate(cb, predicates, logical);
     }
-    
-    
-    protected Predicate getObjectFieldPredicate(DataFetchingEnvironment environment, 
-                                                CriteriaBuilder cb, 
-                                                From<?,?> from, 
+
+
+    protected Predicate getObjectFieldPredicate(DataFetchingEnvironment environment,
+                                                CriteriaBuilder cb,
+                                                From<?,?> from,
                                                 Logical logical,
-                                                ObjectField objectField, 
-                                                Argument argument, 
+                                                ObjectField objectField,
+                                                Argument argument,
                                                 Map<String, Object> arguments
                                                  ) {
         if(isEntityType(environment)) {
-            Attribute<?,?> attribute = getAttribute(environment, argument);
-            
+            Attribute<?,?> attribute = getAttribute(environment, argument.getName());
+
             if(attribute.isAssociation()) {
                 GraphQLFieldDefinition fieldDefinition = getFieldDefinition(environment.getGraphQLSchema(),
                                                                      this.getObjectType(environment),
                                                                      new Field(objectField.getName()));
-                
+
                 if(Arrays.asList(Logical.EXISTS, Logical.NOT_EXISTS).contains(logical) ) {
                     AbstractQuery<?> query = environment.getRoot();
                     Subquery<?> subquery = query.subquery(attribute.getJavaType());
                     From<?,?> correlation = Root.class.isInstance(from) ? subquery.correlate((Root<?>) from)
                                                                         : subquery.correlate((Join<?,?>) from);
-                    
+
                     Join<?,?> correlationJoin = correlation.join(objectField.getName());
-                    
+
                     DataFetchingEnvironment existsEnvironment = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment)
                                                                                               .root(subquery)
-                                                                                              .build();                                                                                                  
-                    
+                                                                                              .build();
+
                     Predicate restriction = getArgumentPredicate(cb,
                                                                  correlationJoin,
                                                                  wherePredicateEnvironment(existsEnvironment, fieldDefinition, arguments),
                                                                  argument);
-                    
-                    
+
+
                     Predicate exists = cb.exists(subquery.select((Join) correlationJoin)
                                                          .where(restriction));
-                    
+
                     return logical == Logical.EXISTS ? exists : cb.not(exists);
                 }
-                
+
                 AbstractQuery<?> query = environment.getRoot();
                 Boolean isFetch = environment.getLocalContext();
                 Boolean isOptional = isOptionalAttribute(attribute);
-                
+
                 From<?,?> context = (isSubquery(query) || isCountQuery(query) || !isFetch)
                                         ? reuseJoin(from, objectField.getName(), isOptional)
                                         : reuseFetch(from, objectField.getName(), isOptional);
-                            
-                return getArgumentPredicate(cb, 
+
+                return getArgumentPredicate(cb,
                                             context,
                                             wherePredicateEnvironment(environment, fieldDefinition, arguments),
                                             argument);
@@ -813,7 +928,7 @@ protected Predicate getObjectFieldPredicate(DataFetchingEnvironment environment,
     protected boolean isSubquery(AbstractQuery<?> query) {
         return Subquery.class.isInstance(query);
     }
-    
+
     protected boolean isCountQuery(AbstractQuery<?> query) {
         return Optional.ofNullable(query.getSelection())
                        .map(Selection::getJavaType)
@@ -833,13 +948,13 @@ protected Predicate getArgumentsPredicate(CriteriaBuilder cb,
         Logical logical = extractLogical(argument);
 
         List<Predicate> predicates = new ArrayList<>();
-        
+
         List<Map<String,Object>> arguments = environment.getArgument(logical.name());
         List<ObjectValue> values =  whereValue.getValues()
                 .stream()
                 .map(ObjectValue.class::cast).collect(Collectors.toList());
-        
-        List<SimpleEntry<ObjectValue, Map<String, Object>>> tuples = 
+
+        List<SimpleEntry<ObjectValue, Map<String, Object>>> tuples =
                 IntStream.range(0, values.size())
                          .mapToObj(i -> new SimpleEntry<ObjectValue, Map<String, Object>>(values.get(i),
                                                                                           arguments.get(i)))
@@ -853,19 +968,19 @@ protected Predicate getArgumentsPredicate(CriteriaBuilder cb,
                              .map(it -> {
                                  Map<String, Object> args = e.getValue();
                                  Argument arg = new Argument(it.getName(), it.getValue());
-                                 
+
                                  if(ArrayValue.class.isInstance(it.getValue())) {
                                      return getArgumentsPredicate(cb,
                                                                  from,
                                                                  argumentEnvironment(environment, args),
                                                                  arg);
                                  }
-                                 
+
                                  return getArgumentPredicate(cb,
                                                              from,
                                                              argumentEnvironment(environment, args),
                                                              arg);
-                                 
+
                              }))
               .forEach(predicates::add);
 
@@ -877,18 +992,18 @@ protected Predicate getArgumentsPredicate(CriteriaBuilder cb,
                              .map(it -> {
                                  Map<String, Object> args = e.getValue();
                                  Argument arg = new Argument(it.getName(), it.getValue());
-                                 
+
                                  return getObjectFieldPredicate(environment, cb, from, logical, it, arg, args);
                              }))
               .filter(predicate -> predicate != null)
               .forEach(predicates::add);
-        
+
         return getCompoundPredicate(cb, predicates, logical);
     }
-    
+
     private Map<String, Object> getFieldArguments(DataFetchingEnvironment environment, ObjectField field, Argument argument) {
         Map<String, Object> arguments;
-        
+
         if (environment.getArgument(argument.getName()) instanceof Collection) {
             Collection<Map<String,Object>> list = environment.getArgument(argument.getName());
 
@@ -899,10 +1014,10 @@ private Map<String, Object> getFieldArguments(DataFetchingEnvironment environmen
         } else {
             arguments = environment.getArgument(argument.getName());
         }
-        
+
         return arguments;
     }
-    
+
     private Logical extractLogical(Argument argument) {
         return Optional.of(argument.getName())
                 .filter(it -> Logical.names().contains(it))
@@ -929,7 +1044,7 @@ private Predicate getLogicalPredicates(String fieldName,
              .map(it -> {
                  Map<String, Object> args = getFieldArguments(environment, it, argument);
                  Argument arg = new Argument(it.getName(), it.getValue());
-                 
+
                  return getLogicalPredicate(it.getName(),
                                           cb,
                                           path,
@@ -940,14 +1055,14 @@ private Predicate getLogicalPredicates(String fieldName,
              .forEach(predicates::add);
 
         return getCompoundPredicate(cb, predicates, logical);
-    }    
-    
+    }
+
     private Predicate getLogicalPredicate(String fieldName, CriteriaBuilder cb, From<?,?> path, ObjectField objectField, DataFetchingEnvironment environment, Argument argument) {
         ObjectValue expressionValue;
-        
+
         if(objectField.getValue() instanceof ObjectValue)
             expressionValue = (ObjectValue) objectField.getValue();
-        else 
+        else
             expressionValue = new ObjectValue(Arrays.asList(objectField));
 
         if(expressionValue.getChildren().isEmpty())
@@ -960,22 +1075,22 @@ private Predicate getLogicalPredicate(String fieldName, CriteriaBuilder cb, From
         // Let's parse logical expressions, i.e. AND, OR
         expressionValue.getObjectFields().stream()
             .filter(it -> Logical.names().contains(it.getName()))
-            .map(it -> { 
+            .map(it -> {
                 Map<String, Object> args = getFieldArguments(environment, it, argument);
                 Argument arg = new Argument(it.getName(), it.getValue());
-                
+
                 if(it.getValue() instanceof ArrayValue) {
                     return getLogicalPredicates(fieldName, cb, path, it,
                                                   argumentEnvironment(environment, args),
                                                   arg);
                 }
-                
+
                 return getLogicalPredicate(fieldName, cb, path, it,
                                          argumentEnvironment(environment, args),
                                          arg);
             })
             .forEach(predicates::add);
-        
+
         // Let's parse relation criteria expressions if present, i.e. books, author, etc.
         if(expressionValue.getObjectFields()
                           .stream()
@@ -987,21 +1102,21 @@ private Predicate getLogicalPredicate(String fieldName, CriteriaBuilder cb, From
             Map<String, Object> args = new LinkedHashMap<>();
             Argument arg = new Argument(logical.name(), expressionValue);
             boolean isOptional = false;
-            
+
             if(Logical.names().contains(argument.getName())) {
                 args.put(logical.name(), environment.getArgument(argument.getName()));
             } else {
                 args.put(logical.name(), environment.getArgument(fieldName));
 
-                isOptional = isOptionalAttribute(getAttribute(environment, argument));
+                isOptional = isOptionalAttribute(getAttribute(environment, argument.getName()));
             }
-            
-            return getArgumentPredicate(cb, reuseJoin(path, fieldName, isOptional),  
+
+            return getArgumentPredicate(cb, reuseJoin(path, fieldName, isOptional),
                                         wherePredicateEnvironment(environment, fieldDefinition, args),
                                         arg);
         }
-        
-        // Let's parse simple Criteria expressions, i.e. EQ, LIKE, etc. 
+
+        // Let's parse simple Criteria expressions, i.e. EQ, LIKE, etc.
         JpaPredicateBuilder pb = new JpaPredicateBuilder(cb);
 
         expressionValue.getObjectFields()
@@ -1018,15 +1133,15 @@ private Predicate getLogicalPredicate(String fieldName, CriteriaBuilder cb, From
         return getCompoundPredicate(cb, predicates, logical);
 
     }
-    
+
     private Predicate getCompoundPredicate(CriteriaBuilder cb, List<Predicate> predicates, Logical logical) {
         if(predicates.isEmpty())
             return cb.disjunction();
-        
+
         if(predicates.size() == 1) {
             return predicates.get(0);
         }
-        
+
         return  (logical == Logical.OR)
                 ? cb.or(predicates.toArray(new Predicate[0]))
                 : cb.and(predicates.toArray(new Predicate[0]));
@@ -1035,16 +1150,16 @@ private Predicate getCompoundPredicate(CriteriaBuilder cb, List<Predicate> predi
 	private PredicateFilter getPredicateFilter(ObjectField objectField, DataFetchingEnvironment environment, Argument argument) {
         EnumSet<PredicateFilter.Criteria> options =
             EnumSet.of(PredicateFilter.Criteria.valueOf(argument.getName()));
-        
+
         Map<String, Object> valueArguments = new LinkedHashMap<String,Object>();
         valueArguments.put(objectField.getName(), environment.getArgument(argument.getName()));
-        
+
         DataFetchingEnvironment dataFetchingEnvironment = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment)
                                                                                         .arguments(valueArguments)
                                                                                         .build();
-        
+
         Argument dataFetchingArgument = new Argument(objectField.getName(), argument.getValue());
-        
+
         Object filterValue = convertValue( dataFetchingEnvironment, dataFetchingArgument, argument.getValue() );
 
         return new PredicateFilter(objectField.getName(), filterValue, options );
@@ -1055,10 +1170,10 @@ protected final DataFetchingEnvironment argumentEnvironment(DataFetchingEnvironm
                                              .arguments(arguments)
                                              .build();
     }
-	
+
     protected final DataFetchingEnvironment argumentEnvironment(DataFetchingEnvironment environment, Argument argument) {
         Map<String, Object> arguments = environment.getArgument(argument.getName());
-        
+
         return DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(environment)
                                              .arguments(arguments)
                                              .build();
@@ -1071,7 +1186,7 @@ protected final DataFetchingEnvironment wherePredicateEnvironment(DataFetchingEn
                                              .fieldType(fieldDefinition.getType())
                                              .build();
     }
-    
+
     /**
      * @param fieldName
      * @return Path of compound field to the primitive type
@@ -1134,7 +1249,7 @@ private From<?,?> reuseJoin(From<?, ?> from, String fieldName, boolean outer) {
         }
         return outer ? from.join(fieldName, JoinType.LEFT) : from.join(fieldName);
     }
-    
+
     // trying to find already existing fetch joins to reuse
     private From<?,?> reuseFetch(From<?, ?> from, String fieldName, boolean outer) {
 
@@ -1144,8 +1259,8 @@ private From<?,?> reuseFetch(From<?, ?> from, String fieldName, boolean outer) {
             }
         }
         return outer ? (From<?,?>) from.fetch(fieldName, JoinType.LEFT) : (From<?,?>) from.fetch(fieldName);
-    }    
-    
+    }
+
     @SuppressWarnings( { "unchecked", "rawtypes" } )
     protected Object convertValue(DataFetchingEnvironment environment, Argument argument, Value value) {
         if (value instanceof NullValue) {
@@ -1167,15 +1282,15 @@ else if (value instanceof VariableReference) {
             if(javaType.isEnum()) {
                 if(argumentValue instanceof Collection) {
                     List<Enum> values = new ArrayList<>();
-                    
+
                     Collection.class.cast(argumentValue)
-                                    .forEach(it -> values.add(Enum.valueOf(javaType, it.toString()))); 
+                                    .forEach(it -> values.add(Enum.valueOf(javaType, it.toString())));
                     return values;
                 } else {
                     return Enum.valueOf(javaType, argumentValue.toString());
                 }
-            } 
-            else {    
+            }
+            else {
                 // Get resolved variable in environment arguments
                 return argumentValue;
             }
@@ -1189,28 +1304,28 @@ else if (value instanceof VariableReference) {
                     arrayValue = Collection.class.cast(arrayValue.iterator()
                                                                  .next());
                 }
-                
+
                 // Let's convert enum types, i.e. array of strings or EnumValue into Java type
                 if(getJavaType(environment, argument).isEnum()) {
                     Function<Object, Value> objectValue = (obj) -> Value.class.isInstance(obj)
                                                                               ? Value.class.cast(obj)
                                                                               : new EnumValue(obj.toString());
-                    // Return real typed resolved array values converted into Java enums 
+                    // Return real typed resolved array values converted into Java enums
                     return arrayValue.stream()
-                                     .map((it) -> convertValue(environment, 
-                                                               argument, 
+                                     .map((it) -> convertValue(environment,
+                                                               argument,
                                                                objectValue.apply(it)))
                                      .collect(Collectors.toList());
-                } 
-                // Let's try handle Ast Value types 
+                }
+                // Let's try handle Ast Value types
                 else if(arrayValue.stream()
                                   .anyMatch(it->it instanceof Value)) {
                         return arrayValue.stream()
-                                         .map(it -> convertValue(environment, 
-                                                                 argument, 
+                                         .map(it -> convertValue(environment,
+                                                                 argument,
                                                                  Value.class.cast(it)))
                                          .collect(Collectors.toList());
-                } 
+                }
                 // Return real typed resolved array value, i.e. Date, UUID, Long
                 else {
                     return arrayValue;
@@ -1235,7 +1350,7 @@ else if (value instanceof EnumValue) {
         } else if (value instanceof ObjectValue) {
             Class javaType = getJavaType(environment, argument);
             Map<String, Object> values = environment.getArgument(argument.getName());
-            
+
             try {
                 return getJavaBeanValue(javaType, values);
             } catch (Exception cause) {
@@ -1245,13 +1360,13 @@ else if (value instanceof EnumValue) {
 
         return value;
     }
-    
+
     private Object getJavaBeanValue(Class<?> javaType, Map<String, Object> values) throws Exception {
         Constructor<?> constructor = javaType.getConstructor();
         constructor.setAccessible(true);
 
         Object javaBean = constructor.newInstance();
-        
+
         values.entrySet()
               .stream()
               .forEach(entry -> {
@@ -1259,11 +1374,11 @@ private Object getJavaBeanValue(Class<?> javaType, Map<String, Object> values) t
                                    entry.getKey(),
                                    entry.getValue());
               });
-        
+
         return javaBean;
     }
-    
-    private void setPropertyValue(Object javaBean, String propertyName, Object propertyValue) { 
+
+    private void setPropertyValue(Object javaBean, String propertyName, Object propertyValue) {
         try {
             BeanInfo bi = Introspector.getBeanInfo(javaBean.getClass());
             PropertyDescriptor pds[] = bi.getPropertyDescriptors();
@@ -1271,7 +1386,7 @@ private void setPropertyValue(Object javaBean, String propertyName, Object prope
                 if (pd.getName().equals(propertyName)) {
                     Method setter = pd.getWriteMethod();
                     setter.setAccessible(true);
-                    
+
                     if (setter != null) {
                         setter.invoke(javaBean, new Object[] {propertyValue} );
                     }
@@ -1290,7 +1405,7 @@ private void setPropertyValue(Object javaBean, String propertyName, Object prope
      * @return Java class type
      */
     protected Class<?> getJavaType(DataFetchingEnvironment environment, Argument argument) {
-        Attribute<?,?> argumentEntityAttribute = getAttribute(environment, argument);
+        Attribute<?,?> argumentEntityAttribute = getAttribute(environment, argument.getName());
 
         if (argumentEntityAttribute instanceof PluralAttribute)
             return ((PluralAttribute<?,?,?>) argumentEntityAttribute).getElementType().getJavaType();
@@ -1305,13 +1420,15 @@ protected Class<?> getJavaType(DataFetchingEnvironment environment, Argument arg
      * @param argument
      * @return JPA model attribute
      */
-    private Attribute<?,?> getAttribute(DataFetchingEnvironment environment, Argument argument) {
+    private Attribute<?,?> getAttribute(DataFetchingEnvironment environment, String argument) {
         GraphQLObjectType objectType = getObjectType(environment);
         EntityType<?> entityType = getEntityType(objectType);
 
-        return entityType.getAttribute(argument.getName());
+        return entityType.getAttribute(argument);
     }
 
+
+
     private boolean isOptionalAttribute(Attribute<?,?> attribute) {
         if(SingularAttribute.class.isInstance(attribute)) {
             return SingularAttribute.class.cast(attribute).isOptional();
@@ -1319,10 +1436,10 @@ private boolean isOptionalAttribute(Attribute<?,?> attribute) {
         else if(PluralAttribute.class.isInstance(attribute)) {
             return true;
         }
-        
+
         return false;
     }
-    
+
     /**
      * Resolve JPA model entity type from GraphQL objectType
      *
@@ -1338,13 +1455,13 @@ private EntityType<?> getEntityType(GraphQLObjectType objectType) {
                                           .findFirst()
                                           .get();
     }
-    
+
     private boolean isEntityType(DataFetchingEnvironment environment) {
         GraphQLObjectType objectType = getObjectType(environment);
         return entityManager.getMetamodel()
                             .getEntities().stream()
                                           .anyMatch(it -> it.getName().equals(objectType.getName()));
-    }    
+    }
 
     /**
      * Resolve GraphQL object type from Argument output type.
@@ -1390,16 +1507,16 @@ protected GraphQLFieldDefinition getFieldDefinition(GraphQLSchema schema, GraphQ
         }
 
         GraphQLFieldDefinition fieldDefinition = parentType.getFieldDefinition(field.getName());
-        
+
         if (fieldDefinition != null) {
             return fieldDefinition;
         }
-        
+
         throw new GraphQLException("unknown field " + field.getName());
     }
-    
+
     protected final boolean isManagedType(Attribute<?,?> attribute) {
-    	return attribute.getPersistentAttributeType() != PersistentAttributeType.EMBEDDED 
+    	return attribute.getPersistentAttributeType() != PersistentAttributeType.EMBEDDED
     			&& attribute.getPersistentAttributeType() != PersistentAttributeType.BASIC
     			&& attribute.getPersistentAttributeType() != PersistentAttributeType.ELEMENT_COLLECTION;
     }
@@ -1449,7 +1566,7 @@ protected final <R extends Value<?>> R getObjectFieldValue(ObjectValue objectVal
     @SuppressWarnings( "unchecked" )
     protected final <T> T getArgumentValue(DataFetchingEnvironment environment, Argument argument, Class<T> type) {
         Value<?> value = argument.getValue();
-        
+
         if(VariableReference.class.isInstance(value)) {
             return (T)
                 environment.getVariables()
@@ -1470,7 +1587,7 @@ else if (FloatValue.class.isInstance(value)) {
         else if (NullValue.class.isInstance(value)) {
             return (T) null;
         }
-        
+
         throw new IllegalArgumentException("Not supported");
     }
 
@@ -1478,7 +1595,7 @@ protected boolean isPersistent(DataFetchingEnvironment environment,
                                    String attributeName) {
         GraphQLObjectType objectType = getObjectType(environment);
         EntityType<?> entityType = getEntityType(objectType);
-        
+
         return isPersistent(entityType, attributeName);
     }
 
@@ -1486,11 +1603,11 @@ protected boolean isPersistent(EntityType<?> entityType,
                                    String attributeName) {
         try {
             return entityType.getAttribute(attributeName) != null;
-        } catch (Exception ignored) { } 
-        
+        } catch (Exception ignored) { }
+
         return false;
     }
-    
+
     protected boolean isTransient(DataFetchingEnvironment environment,
                                   String attributeName) {
         return !isPersistent(environment, attributeName);
@@ -1500,31 +1617,33 @@ protected boolean isTransient(EntityType<?> entityType,
                                   String attributeName) {
         return !isPersistent(entityType, attributeName);
     }
-    
-    
+
+
     protected String getJPQLQueryString(TypedQuery<?> query) {
         try {
             Object queryImpl = query.unwrap(TypedQuery.class);
-            
+
             java.lang.reflect.Field queryStringField = ReflectionUtil.getField(queryImpl.getClass(),
                                                                                "queryString");
-                                                    
+
             ReflectionUtil.forceAccess(queryStringField);
-            
-            return queryStringField.get(queryImpl)
-                                   .toString();
-            
+
+            if(queryStringField != null) {
+                return queryStringField.get(queryImpl)
+                                       .toString();
+            }
+
         } catch (Exception ignored) {
             logger.error("Error getting JPQL string", ignored);
         }
-        
+
         return null;
     }
 
     protected boolean hasIdAttribute() {
         return entityType.getIdType() != null;
     }
-    
+
     protected String idAttributeName() {
         return entityType.getId(entityType.getIdType()
                                           .getJavaType()).getName();
@@ -1533,7 +1652,7 @@ protected String idAttributeName() {
     protected boolean hasIdClassAttribue() {
         return entityType.getIdClassAttributes() != null;
     }
-    
+
     protected String[] idClassAttributeNames() {
         return entityType.getIdClassAttributes()
                          .stream()
@@ -1542,7 +1661,14 @@ protected String[] idClassAttributeNames() {
                          .collect(Collectors.toList())
                          .toArray(new String[0]);
     }
-    
+
+
+    protected <T> T getParentIdAttributeValue(T entity) {
+        SingularAttribute<?, Object> parentIdAttribute = entityType.getId(Object.class);
+
+        return (T) getAttributeValue(entity, parentIdAttribute);
+    }
+
     /**
      * Fetches the value of the given SingularAttribute on the given
      * entity.
@@ -1550,21 +1676,21 @@ protected String[] idClassAttributeNames() {
      * http://stackoverflow.com/questions/7077464/how-to-get-singularattribute-mapped-value-of-a-persistent-object
      */
     @SuppressWarnings("unchecked")
-    protected <EntityType, FieldType> FieldType getAttributeValue(EntityType entity, SingularAttribute<EntityType, FieldType> field) {
+    protected <E, T> T getAttributeValue(T entity, SingularAttribute<E, T> field) {
         try {
             Member member = field.getJavaMember();
             if (member instanceof Method) {
                 // this should be a getter method:
-                return (FieldType) ((Method)member).invoke(entity);
+                return (T) ((Method)member).invoke(entity);
             } else if (member instanceof java.lang.reflect.Field) {
-                return (FieldType) ((java.lang.reflect.Field)member).get(entity);
+                return (T) ((java.lang.reflect.Field)member).get(entity);
             } else {
                 throw new IllegalArgumentException("Unexpected java member type. Expecting method or field, found: " + member);
             }
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
-    }    
+    }
 
     /**
      * Fetches the value of the given SingularAttribute on the given
@@ -1588,48 +1714,48 @@ protected <EntityType, FieldType> FieldType getAttributeValue(EntityType entity,
             throw new RuntimeException(e);
         }
     }
-    
+
     protected boolean resolveDistinctArgument(Field field) {
-        Argument distinctArg = extractArgument(field, 
-                                               SELECT_DISTINCT_PARAM_NAME, 
+        Argument distinctArg = extractArgument(field,
+                                               SELECT_DISTINCT_PARAM_NAME,
                                                new BooleanValue(defaultDistinct));
-        
+
         return BooleanValue.class.cast(distinctArg.getValue())
                                  .isValue();
     }
-    
+
     public boolean isDefaultDistinct() {
         return defaultDistinct;
     }
-    
+
     public String getSelectNodeName() {
         return selectNodeName;
     }
 
     public MergedField resolveQueryField(Field rootNode) {
         Optional<Field> recordsSelection = GraphQLSupport.searchByFieldName(rootNode, getSelectNodeName());
-        
+
         Field queryField = recordsSelection.map(selectNode -> Field.newField(selectNode.getName())
                                                                    .selectionSet(selectNode.getSelectionSet())
                                                                    .arguments(rootNode.getArguments())
                                                                    .directives(selectNode.getDirectives())
                                                                    .build())
-                                           .orElse(rootNode);    
+                                           .orElse(rootNode);
 
         return MergedField.newMergedField(queryField)
                           .build();
     }
 
-    
+
     public GraphQLObjectType getEntityObjectType() {
         return entityObjectType;
     }
 
-    
+
     public int getDefaultFetchSize() {
         return defaultFetchSize;
     }
-    
+
     private MergedField flattenEmbeddedIdArguments(Field field) {
         // manage object arguments (EmbeddedId)
         final List<Argument> argumentsWhereObjectsAreFlattened = field.getArguments()
@@ -1645,11 +1771,38 @@ private MergedField flattenEmbeddedIdArguments(Field field) {
                     }
                 })
                 .collect(Collectors.toList());
-        
+
         return MergedField.newMergedField(field.transform(builder -> builder.arguments(argumentsWhereObjectsAreFlattened)))
                           .build();
     }
-    
+
+    protected boolean hasAnySelectionOrderBy(Field field) {
+
+        if (!hasSelectionSet(field))
+            return false;
+
+        // Loop through all of the fields being requested
+        return field.getSelectionSet()
+                    .getSelections()
+                    .stream()
+                    .filter(Field.class::isInstance)
+                    .map(Field.class::cast)
+                    .anyMatch(selectedField -> {
+
+                        // Optional orderBy argument
+                        Optional<Argument> orderBy = selectedField.getArguments()
+                                                                  .stream()
+                                                                  .filter(this::isOrderByArgument)
+                                                                  .findFirst();
+
+                        if (orderBy.isPresent()) {
+                            return true;
+                        }
+
+                        return false;
+                    });
+
+    }
 
     /**
      * Creates builder to build {@link GraphQLJpaQueryFactory}.
@@ -1812,5 +1965,5 @@ public IBuildStage withDefaultFetchSize(int defaultFetchSize) {
         public GraphQLJpaQueryFactory build() {
             return new GraphQLJpaQueryFactory(this);
         }
-    }        
+    }
 }
diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java
index 1154f1213..a1a1a102c 100644
--- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java
@@ -31,6 +31,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -44,6 +45,7 @@
 import javax.persistence.metamodel.SingularAttribute;
 import javax.persistence.metamodel.Type;
 
+import org.dataloader.MappedBatchLoaderWithContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,7 +58,6 @@
 import com.introproventures.graphql.jpa.query.schema.impl.EntityIntrospector.EntityIntrospectionResult.AttributePropertyDescriptor;
 import com.introproventures.graphql.jpa.query.schema.impl.PredicateFilter.Criteria;
 import com.introproventures.graphql.jpa.query.schema.relay.GraphQLJpaRelayDataFetcher;
-
 import graphql.Assert;
 import graphql.Directives;
 import graphql.Scalars;
@@ -80,7 +81,7 @@
 
 /**
  * JPA specific schema builder implementation of {code #GraphQLSchemaBuilder} interface
- * 
+ *
  * @author Igor Dianov
  *
  */
@@ -93,30 +94,30 @@ public class GraphQLJpaSchemaBuilder implements GraphQLSchemaBuilder {
 
     public static final String PAGE_START_PARAM_NAME = "start";
     public static final String PAGE_LIMIT_PARAM_NAME = "limit";
-    
+
     public static final String QUERY_SELECT_PARAM_NAME = "select";
     public static final String QUERY_WHERE_PARAM_NAME = "where";
     public static final String QUERY_LOGICAL_PARAM_NAME = "logical";
 
     public static final String SELECT_DISTINCT_PARAM_NAME = "distinct";
-    
+
     protected NamingStrategy namingStrategy = new NamingStrategy() {};
-    
+
     public static final String ORDER_BY_PARAM_NAME = "orderBy";
-    
+
     private Map<Class<?>, GraphQLOutputType> classCache = new HashMap<>();
     private Map<EntityType<?>, GraphQLObjectType> entityCache = new HashMap<>();
     private Map<ManagedType<?>, GraphQLInputObjectType> inputObjectCache = new HashMap<>();
     private Map<ManagedType<?>, GraphQLInputObjectType> subqueryInputObjectCache = new HashMap<>();
     private Map<Class<?>, GraphQLObjectType> embeddableOutputCache = new HashMap<>();
     private Map<Class<?>, GraphQLInputObjectType> embeddableInputCache = new HashMap<>();
-    
+
     private static final Logger log = LoggerFactory.getLogger(GraphQLJpaSchemaBuilder.class);
 
     private EntityManager entityManager;
-     
+
     private String name = "GraphQLJPA";
-    
+
     private String description = "GraphQL Schema for all entities in this JPA application";
 
     private boolean isUseDistinctParameter = false;
@@ -128,11 +129,15 @@ public class GraphQLJpaSchemaBuilder implements GraphQLSchemaBuilder {
     private int defaultMaxResults = 100;
     private int defaultFetchSize = 100;
     private int defaultPageLimitSize = 100;
-    
+
     private final Relay relay = new Relay();
-    
+
     private final List<String> entityPaths = new ArrayList<>();
-    
+
+    private Supplier<BatchLoaderRegistry> batchLoadersRegistry = () -> {
+        return BatchLoaderRegistry.getInstance();
+    };
+
     public GraphQLJpaSchemaBuilder(EntityManager entityManager) {
         this.entityManager = entityManager;
     }
@@ -144,7 +149,7 @@ public GraphQLJpaSchemaBuilder(EntityManager entityManager) {
     public GraphQLSchema build() {
         GraphQLSchema.Builder schema = GraphQLSchema.newSchema()
                                                     .query(getQueryType());
-        
+
         if(enableSubscription) {
             schema.subscription(getSubscriptionType());
         }
@@ -152,16 +157,16 @@ public GraphQLSchema build() {
         if(enableDeferDirective) {
             schema.additionalDirective(Directives.DeferDirective);
         }
-        
+
         if(enableRelay) {
             schema.additionalType(Relay.pageInfoType);
         }
-        
+
         return schema.build();
     }
 
     private GraphQLObjectType getQueryType() {
-        GraphQLObjectType.Builder queryType = 
+        GraphQLObjectType.Builder queryType =
             GraphQLObjectType.newObject()
                 .name(this.name  + "Query")
                 .description(this.description);
@@ -173,7 +178,7 @@ private GraphQLObjectType getQueryType() {
                 .map(this::getQueryFieldByIdDefinition)
                 .collect(Collectors.toList())
         );
-        
+
         queryType.fields(
             entityManager.getMetamodel()
                 .getEntities().stream()
@@ -186,7 +191,7 @@ private GraphQLObjectType getQueryType() {
     }
 
     private GraphQLObjectType getSubscriptionType() {
-        GraphQLObjectType.Builder queryType = 
+        GraphQLObjectType.Builder queryType =
             GraphQLObjectType.newObject()
                 .name(this.name + "Subscription")
                 .description(this.description);
@@ -201,10 +206,10 @@ private GraphQLObjectType getSubscriptionType() {
 
         return queryType.build();
     }
-    
+
     private GraphQLFieldDefinition getQueryFieldByIdDefinition(EntityType<?> entityType) {
         GraphQLObjectType entityObjectType = getObjectType(entityType);
-        
+
         GraphQLJpaQueryFactory queryFactory = GraphQLJpaQueryFactory.builder()
                                                                     .withEntityManager(entityManager)
                                                                     .withEntityType(entityType)
@@ -212,12 +217,12 @@ private GraphQLFieldDefinition getQueryFieldByIdDefinition(EntityType<?> entityT
                                                                     .withSelectNodeName(entityObjectType.getName())
                                                                     .withToManyDefaultOptional(toManyDefaultOptional)
                                                                     .build();
-        
+
         DataFetcher<Object> dataFetcher = GraphQLJpaSimpleDataFetcher.builder()
                                                                      .withQueryFactory(queryFactory)
                                                                      .build();
         String fieldName = entityType.getName();
-        
+
         return GraphQLFieldDefinition.newFieldDefinition()
                 .name(enableRelay ? Introspector.decapitalize(fieldName) : fieldName)
                 .description(getSchemaDescription(entityType))
@@ -232,10 +237,10 @@ private GraphQLFieldDefinition getQueryFieldByIdDefinition(EntityType<?> entityT
                 )
                 .build();
     }
-    
+
     private GraphQLObjectType getConnectionType(GraphQLObjectType nodeType) {
         GraphQLObjectType edgeType = relay.edgeType(nodeType.getName(), nodeType, null, Collections.emptyList());
-        
+
         return relay.connectionType(nodeType.getName(), edgeType, Collections.emptyList());
     }
 
@@ -244,7 +249,7 @@ private GraphQLFieldDefinition getQueryFieldSelectDefinition(EntityType<?> entit
         final GraphQLObjectType outputType = enableRelay ? getConnectionType(entityObjectType) : getSelectType(entityType);
 
         final DataFetcher<? extends Object> dataFetcher;
-        
+
         GraphQLJpaQueryFactory queryFactory = GraphQLJpaQueryFactory.builder()
                                                                     .withEntityManager(entityManager)
                                                                     .withEntityType(entityType)
@@ -254,7 +259,7 @@ private GraphQLFieldDefinition getQueryFieldSelectDefinition(EntityType<?> entit
                                                                     .withDefaultDistinct(isDefaultDistinct)
                                                                     .withDefaultFetchSize(defaultFetchSize)
                                                                     .build();
-        
+
         if(enableRelay) {
             dataFetcher = GraphQLJpaRelayDataFetcher.builder()
                                                     .withQueryFactory(queryFactory)
@@ -270,7 +275,7 @@ private GraphQLFieldDefinition getQueryFieldSelectDefinition(EntityType<?> entit
         }
 
         String fieldName = namingStrategy.pluralize(entityType.getName());
-        
+
         GraphQLFieldDefinition.Builder fieldDefinition = GraphQLFieldDefinition.newFieldDefinition()
                 .name(enableRelay ? Introspector.decapitalize(fieldName) : fieldName)
                 .description("Query request wrapper for " + entityType.getName() + " to request paginated data. "
@@ -281,17 +286,17 @@ private GraphQLFieldDefinition getQueryFieldSelectDefinition(EntityType<?> entit
                 .dataFetcher(dataFetcher)
                 .argument(getWhereArgument(entityType))
                 .arguments(enableRelay ? relay.getForwardPaginationConnectionFieldArguments() : Collections.singletonList(paginationArgument));
-        
+
         if (isUseDistinctParameter) {
                 fieldDefinition.argument(distinctArgument(entityType));
         }
 
         return fieldDefinition.build();
     }
-    
+
     private GraphQLObjectType getSelectType(EntityType<?> entityType) {
         GraphQLObjectType selectObjectType = getObjectType(entityType);
-        
+
         GraphQLObjectType selectPagedResultType = GraphQLObjectType.newObject()
                 .name(namingStrategy.pluralize(entityType.getName()))
                 .description("Query response wrapper object for " + entityType.getName() + ".  When page is requested, this object will be returned with query metadata.")
@@ -313,14 +318,14 @@ private GraphQLObjectType getSelectType(EntityType<?> entityType) {
                     .type(new GraphQLList(selectObjectType))
                     .build()
                 )
-                .build();        
-        
+                .build();
+
         return selectPagedResultType;
     }
-    
+
     private GraphQLFieldDefinition getQueryFieldStreamDefinition(EntityType<?> entityType) {
         GraphQLObjectType entityObjectType = getObjectType(entityType);
-        
+
         GraphQLJpaQueryFactory queryFactory = GraphQLJpaQueryFactory.builder()
                                                                     .withEntityManager(entityManager)
                                                                     .withEntityType(entityType)
@@ -328,8 +333,8 @@ private GraphQLFieldDefinition getQueryFieldStreamDefinition(EntityType<?> entit
                                                                     .withSelectNodeName(SELECT_DISTINCT_PARAM_NAME)
                                                                     .withToManyDefaultOptional(toManyDefaultOptional)
                                                                     .withDefaultDistinct(isDefaultDistinct)
-                                                                    .build();                    
-        
+                                                                    .build();
+
         DataFetcher<Object> dataFetcher = GraphQLJpaStreamDataFetcher.builder()
                                                                      .withQueryFactory(queryFactory)
                                                                      .build();
@@ -343,13 +348,13 @@ private GraphQLFieldDefinition getQueryFieldStreamDefinition(EntityType<?> entit
                 .dataFetcher(dataFetcher)
                 .argument(paginationArgument)
                 .argument(getWhereArgument(entityType));
-        
+
         if (isUseDistinctParameter) {
                 fieldDefinition.argument(distinctArgument(entityType));
         }
 
         return fieldDefinition.build();
-    }    
+    }
 
     private Map<Class<?>, GraphQLArgument> whereArgumentsMap = new HashMap<>();
 
@@ -365,7 +370,7 @@ private GraphQLArgument distinctArgument(EntityType<?> entityType) {
     private GraphQLArgument getWhereArgument(ManagedType<?> managedType) {
         return whereArgumentsMap.computeIfAbsent(managedType.getJavaType(), (javaType) -> computeWhereArgument(managedType));
     }
-    
+
     private GraphQLArgument computeWhereArgument(ManagedType<?> managedType) {
     	String type=resolveWhereArgumentTypeName(managedType);
 
@@ -388,18 +393,18 @@ private GraphQLArgument computeWhereArgument(ManagedType<?> managedType) {
                                           .name(Logical.EXISTS.name())
                                           .description("Logical EXISTS subquery expression")
                                           .type(new GraphQLList(getSubqueryInputType(managedType)))
-                                          .build()            
+                                          .build()
             )
             .field(GraphQLInputObjectField.newInputObjectField()
                                           .name(Logical.NOT_EXISTS.name())
                                           .description("Logical NOT EXISTS subquery expression")
                                           .type(new GraphQLList(getSubqueryInputType(managedType)))
-                                          .build()            
+                                          .build()
             )
             .fields(managedType.getAttributes().stream()
                                                .filter(this::isValidInput)
                                                .filter(this::isNotIgnored)
-                                               .filter(this::isNotIgnoredFilter) 
+                                               .filter(this::isNotIgnoredFilter)
                                                .map(this::getWhereInputField)
                                                .collect(Collectors.toList())
             )
@@ -411,34 +416,34 @@ private GraphQLArgument computeWhereArgument(ManagedType<?> managedType) {
                                                .collect(Collectors.toList())
             )
             .build();
-        
+
         return GraphQLArgument.newArgument()
                               .name(QUERY_WHERE_PARAM_NAME)
                               .description("Where logical specification")
                               .type(whereInputObject)
                               .build();
-        
+
     }
 
     private String resolveWhereArgumentTypeName(ManagedType<?> managedType) {
         String typeName=resolveTypeName(managedType);
-        
+
         return namingStrategy.pluralize(typeName)+"CriteriaExpression";
     }
-    
+
     private String resolveSubqueryArgumentTypeName(ManagedType<?> managedType) {
         String typeName=resolveTypeName(managedType);
-        
+
         return namingStrategy.pluralize(typeName)+"SubqueryCriteriaExpression";
     }
 
     private GraphQLInputObjectType getSubqueryInputType(ManagedType<?> managedType) {
         return subqueryInputObjectCache.computeIfAbsent(managedType, this::computeSubqueryInputType);
     }
-    
+
     private GraphQLInputObjectType computeSubqueryInputType(ManagedType<?> managedType) {
         String type=resolveSubqueryArgumentTypeName(managedType);
-        
+
          Builder whereInputObject = GraphQLInputObjectType.newInputObject()
             .name(type)
             .description("Where logical AND specification of the provided list of criteria expressions")
@@ -458,13 +463,13 @@ private GraphQLInputObjectType computeSubqueryInputType(ManagedType<?> managedTy
                                           .name(Logical.EXISTS.name())
                                           .description("Logical EXISTS subquery expression")
                                           .type(new GraphQLList(new GraphQLTypeReference(type)))
-                                          .build()            
+                                          .build()
             )
             .field(GraphQLInputObjectField.newInputObjectField()
                                           .name(Logical.NOT_EXISTS.name())
                                           .description("Logical NOT EXISTS subquery expression")
                                           .type(new GraphQLList(new GraphQLTypeReference(type)))
-                                          .build()            
+                                          .build()
             )
             .fields(managedType.getAttributes().stream()
                                                .filter(Attribute::isAssociation)
@@ -473,37 +478,37 @@ private GraphQLInputObjectType computeSubqueryInputType(ManagedType<?> managedTy
                                                .map(this::getWhereInputRelationField)
                                                .collect(Collectors.toList())
             );
-        
+
         return whereInputObject.build();
-        
-    }     
-        
+
+    }
+
     private String resolveTypeName(ManagedType<?> managedType) {
         String typeName="";
-        
+
         if (managedType instanceof EmbeddableType){
             typeName = managedType.getJavaType().getSimpleName()+"EmbeddableType";
         } else if (managedType instanceof EntityType) {
             typeName = ((EntityType<?>)managedType).getName();
         }
-        
+
         return typeName;
     }
 
     private GraphQLInputObjectType getWhereInputType(ManagedType<?> managedType) {
         return inputObjectCache.computeIfAbsent(managedType, this::computeWhereInputType);
     }
-    
+
     private String resolveWhereInputTypeName(ManagedType<?> managedType) {
         String typeName=resolveTypeName(managedType);
 
         return namingStrategy.pluralize(typeName)+"RelationCriteriaExpression";
-        
+
     }
-    
+
     private GraphQLInputObjectType computeWhereInputType(ManagedType<?> managedType) {
         String type=resolveWhereInputTypeName(managedType);
-        
+
          Builder whereInputObject = GraphQLInputObjectType.newInputObject()
             .name(type)
             .description("Where logical AND specification of the provided list of criteria expressions")
@@ -523,13 +528,13 @@ private GraphQLInputObjectType computeWhereInputType(ManagedType<?> managedType)
                                           .name(Logical.EXISTS.name())
                                           .description("Logical EXISTS subquery expression")
                                           .type(new GraphQLList(getSubqueryInputType(managedType)))
-                                          .build()            
+                                          .build()
             )
             .field(GraphQLInputObjectField.newInputObjectField()
                                           .name(Logical.NOT_EXISTS.name())
                                           .description("Logical NOT EXISTS subquery expression")
                                           .type(new GraphQLList(getSubqueryInputType(managedType)))
-                                          .build()            
+                                          .build()
             )
             .fields(managedType.getAttributes().stream()
                                                .filter(this::isValidInput)
@@ -545,15 +550,15 @@ private GraphQLInputObjectType computeWhereInputType(ManagedType<?> managedType)
                                                .map(this::getWhereInputRelationField)
                                                .collect(Collectors.toList())
             );
-         
-        
+
+
         return whereInputObject.build();
-        
-    }    
-    
+
+    }
+
     private GraphQLInputObjectField getWhereInputRelationField(Attribute<?,?> attribute) {
         ManagedType<?> foreignType = getForeignType(attribute);
-        
+
         String type = resolveWhereInputTypeName(foreignType);
         String description = getSchemaDescription(attribute);
 
@@ -561,9 +566,9 @@ private GraphQLInputObjectField getWhereInputRelationField(Attribute<?,?> attrib
                                       .name(attribute.getName())
                                       .description(description)
                                       .type(new GraphQLTypeReference(type))
-                                      .build(); 
+                                      .build();
     }
-    
+
     private GraphQLInputObjectField getWhereInputField(Attribute<?,?> attribute) {
         GraphQLInputType type = getWhereAttributeType(attribute);
         String description = getSchemaDescription(attribute);
@@ -573,20 +578,20 @@ private GraphQLInputObjectField getWhereInputField(Attribute<?,?> attribute) {
                                           .name(attribute.getName())
                                           .description(description)
                                           .type(type)
-                                          .build(); 
+                                          .build();
         }
 
         throw new IllegalArgumentException("Attribute " + attribute.getName() + " cannot be mapped as an Input Argument");
     }
 
     private Map<String, GraphQLInputType> whereAttributesMap = new HashMap<>();
-    
+
     private GraphQLInputType getWhereAttributeType(Attribute<?,?> attribute) {
         String type =  namingStrategy.singularize(attribute.getName())+attribute.getDeclaringType().getJavaType().getSimpleName()+"Criteria";
 
         if(whereAttributesMap.containsKey(type))
            return whereAttributesMap.get(type);
-       
+
         GraphQLInputObjectType.Builder builder = GraphQLInputObjectType.newInputObject()
             .name(type)
             .description("Criteria expression specification of "+namingStrategy.singularize(attribute.getName())+" attribute in entity " + attribute.getDeclaringType().getJavaType())
@@ -614,7 +619,7 @@ private GraphQLInputType getWhereAttributeType(Attribute<?,?> attribute) {
                 .type(getAttributeInputType(attribute))
                 .build()
             );
-       
+
             if(!attribute.getJavaType().isEnum()) {
                 if(!attribute.getJavaType().equals(String.class)) {
                     builder.field(GraphQLInputObjectField.newInputObjectField()
@@ -642,7 +647,7 @@ private GraphQLInputType getWhereAttributeType(Attribute<?,?> attribute) {
                         .build()
                     );
                 }
-           
+
                 if(attribute.getJavaType().equals(String.class)) {
                     builder.field(GraphQLInputObjectField.newInputObjectField()
                         .name(Criteria.LIKE.name())
@@ -704,10 +709,10 @@ private GraphQLInputType getWhereAttributeType(Attribute<?,?> attribute) {
                            .type(getAttributeInputType(attribute))
                            .build()
                     );
-                } 
-                else if (attribute.getJavaMember().getClass().isAssignableFrom(Field.class) 
+                }
+                else if (attribute.getJavaMember().getClass().isAssignableFrom(Field.class)
                         && Field.class.cast(attribute.getJavaMember())
-                                      .isAnnotationPresent(Convert.class)) 
+                                      .isAnnotationPresent(Convert.class))
                 {
                     builder.field(GraphQLInputObjectField.newInputObjectField()
                                                          .name(Criteria.LOCATE.name())
@@ -716,7 +721,7 @@ else if (attribute.getJavaMember().getClass().isAssignableFrom(Field.class)
                                                          .build());
                 }
             }
-            
+
             builder.field(GraphQLInputObjectField.newInputObjectField()
                 .name(Criteria.IS_NULL.name())
                 .description("Is Null criteria")
@@ -755,13 +760,13 @@ else if (attribute.getJavaMember().getClass().isAssignableFrom(Field.class)
            );
 
        GraphQLInputType answer = builder.build();
-       
+
        whereAttributesMap.putIfAbsent(type, answer);
-       
+
        return answer;
-       
+
     }
-    
+
     private GraphQLArgument getArgument(Attribute<?,?> attribute) {
         GraphQLInputType type = getAttributeInputType(attribute);
         String description = getSchemaDescription(attribute);
@@ -772,7 +777,7 @@ private GraphQLArgument getArgument(Attribute<?,?> attribute) {
                 .description(description)
                 .build();
     }
-    
+
     private GraphQLType getEmbeddableType(EmbeddableType<?> embeddableType, boolean input) {
         if (input && embeddableInputCache.containsKey(embeddableType.getJavaType()))
             return embeddableInputCache.get(embeddableType.getJavaType());
@@ -807,16 +812,16 @@ private GraphQLType getEmbeddableType(EmbeddableType<?> embeddableType, boolean
         } else{
             embeddableOutputCache.putIfAbsent(embeddableType.getJavaType(), (GraphQLObjectType) graphQLType);
         }
-        
+
         return graphQLType;
     }
-    
+
 
     private GraphQLObjectType getObjectType(EntityType<?> entityType) {
         return entityCache.computeIfAbsent(entityType, this::computeObjectType);
     }
-    
-    
+
+
     private GraphQLObjectType computeObjectType(EntityType<?> entityType) {
     	return GraphQLObjectType.newObject()
 				                .name(entityType.getName())
@@ -839,16 +844,16 @@ private List<GraphQLFieldDefinition> getTransientFields(ManagedType<?> managedTy
         return EntityIntrospector.introspect(managedType)
                                  .getTransientPropertyDescriptors()
                                  .stream()
-                                  .filter(AttributePropertyDescriptor::isNotIgnored)
-                                  .map(this::getJavaFieldDefinition)
+                                 .filter(AttributePropertyDescriptor::isNotIgnored)
+                                 .map(this::getJavaFieldDefinition)
                                  .collect(Collectors.toList());
     }
-    
+
     @SuppressWarnings( { "rawtypes" } )
     private GraphQLFieldDefinition getJavaFieldDefinition(AttributePropertyDescriptor propertyDescriptor) {
     	GraphQLOutputType type = getGraphQLTypeFromJavaType(propertyDescriptor.getPropertyType());
         DataFetcher dataFetcher = PropertyDataFetcher.fetching(propertyDescriptor.getName());
-        
+
         String description = propertyDescriptor.getSchemaDescription().orElse(null);
 
         return GraphQLFieldDefinition.newFieldDefinition()
@@ -883,31 +888,77 @@ && isNotIgnoredOrder(attribute) ) {
             );
         }
 
+
         // Get the fields that can be queried on (i.e. Simple Types, no Sub-Objects)
-        if (attribute instanceof SingularAttribute 
+        if (attribute instanceof SingularAttribute
             && attribute.getPersistentAttributeType() != Attribute.PersistentAttributeType.BASIC) {
             ManagedType foreignType = getForeignType(attribute);
             SingularAttribute<?,?> singularAttribute = SingularAttribute.class.cast(attribute);
 
             // TODO fix page count query
             arguments.add(getWhereArgument(foreignType));
-            
-            // to-one end could be optional  
+
+            // to-one end could be optional
             arguments.add(optionalArgument(singularAttribute.isOptional()));
 
+            GraphQLObjectType entityObjectType = GraphQLObjectType.newObject()
+                    .name(baseEntity.getName())
+                    .build();
+
+            GraphQLJpaQueryFactory graphQLJpaQueryFactory = GraphQLJpaQueryFactory.builder()
+                                                                                  .withEntityManager(entityManager)
+                                                                                  .withEntityType(baseEntity)
+                                                                                  .withEntityObjectType(entityObjectType)
+                                                                                  .withSelectNodeName(entityObjectType.getName())
+                                                                                  .withDefaultDistinct(isDefaultDistinct)
+                                                                                  .build();
+
+            String dataLoaderKey = baseEntity.getName() + "." + attribute.getName();
+
+            MappedBatchLoaderWithContext<Object, Object> mappedBatchLoader = new GraphQLJpaToOneMappedBatchLoader(graphQLJpaQueryFactory);
+
+            batchLoadersRegistry.get()
+                                .registerToOne(dataLoaderKey, mappedBatchLoader);
+
+            dataFetcher = new GraphQLJpaToOneDataFetcher(graphQLJpaQueryFactory,
+                                                         (SingularAttribute) attribute);
+
+
         } //  Get Sub-Objects fields queries via DataFetcher
         else if (attribute instanceof PluralAttribute
-            && (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_MANY
+                && (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_MANY
                 || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_MANY)) {
             Assert.assertNotNull(baseEntity, "For attribute "+attribute.getName() + " cannot find declaring type!");
             EntityType elementType =  (EntityType) ((PluralAttribute) attribute).getElementType();
 
             arguments.add(getWhereArgument(elementType));
-            
+
             // make it configurable via builder api
             arguments.add(optionalArgument(toManyDefaultOptional));
+
+            GraphQLObjectType entityObjectType = GraphQLObjectType.newObject()
+                                                                  .name(baseEntity.getName())
+                                                                  .build();
+
+            GraphQLJpaQueryFactory graphQLJpaQueryFactory = GraphQLJpaQueryFactory.builder()
+                                                                                  .withEntityManager(entityManager)
+                                                                                  .withEntityType(baseEntity)
+                                                                                  .withEntityObjectType(entityObjectType)
+                                                                                  .withSelectNodeName(entityObjectType.getName())
+                                                                                  .withDefaultDistinct(isDefaultDistinct)
+                                                                                  .build();
+
+            String dataLoaderKey = baseEntity.getName() + "." + attribute.getName();
+
+            MappedBatchLoaderWithContext<Object, List<Object>> mappedBatchLoader = new GraphQLJpaToManyMappedBatchLoader(graphQLJpaQueryFactory);
+
+            batchLoadersRegistry.get()
+                                .registerToMany(dataLoaderKey, mappedBatchLoader);
+
+            dataFetcher = new GraphQLJpaToManyDataFetcher(graphQLJpaQueryFactory,
+                                                             (PluralAttribute) attribute);
         }
-        
+
         return GraphQLFieldDefinition.newFieldDefinition()
                 .name(attribute.getName())
                 .description(getSchemaDescription(attribute))
@@ -916,7 +967,7 @@ else if (attribute instanceof PluralAttribute
                 .arguments(arguments)
                 .build();
     }
-    
+
     private GraphQLArgument optionalArgument(Boolean defaultValue) {
         return GraphQLArgument.newArgument()
                 .name("optional")
@@ -925,14 +976,14 @@ private GraphQLArgument optionalArgument(Boolean defaultValue) {
                 .defaultValue(defaultValue)
                 .build();
     }
-    
+
     protected ManagedType<?> getForeignType(Attribute<?,?> attribute) {
         if(SingularAttribute.class.isInstance(attribute))
             return (ManagedType<?>) ((SingularAttribute<?,?>) attribute).getType();
         else
             return (EntityType<?>) ((PluralAttribute<?, ?, ?>) attribute).getElementType();
     }
-    
+
     @SuppressWarnings( { "rawtypes" } )
     private GraphQLInputObjectField getInputObjectField(Attribute attribute) {
         GraphQLInputType type = getAttributeInputType(attribute);
@@ -949,7 +1000,7 @@ private Stream<Attribute<?,?>> findBasicAttributes(Collection<Attribute<?,?>> at
     }
 
     private GraphQLInputType getAttributeInputType(Attribute<?,?> attribute) {
-        
+
         try {
             return (GraphQLInputType) getAttributeType(attribute, true);
         } catch (ClassCastException e){
@@ -970,27 +1021,27 @@ private GraphQLType getAttributeType(Attribute<?,?> attribute, boolean input) {
 
         if (isBasic(attribute)) {
         	return getGraphQLTypeFromJavaType(attribute.getJavaType());
-        } 
+        }
         else if (isEmbeddable(attribute)) {
         	EmbeddableType embeddableType = (EmbeddableType) ((SingularAttribute) attribute).getType();
         	return getEmbeddableType(embeddableType, input);
-        } 
+        }
         else if (isToMany(attribute)) {
             EntityType foreignType = (EntityType) ((PluralAttribute) attribute).getElementType();
-            
+
             return input ? getWhereInputType(foreignType) : new GraphQLList(new GraphQLTypeReference(foreignType.getName()));
-        } 
+        }
         else if (isToOne(attribute)) {
             EntityType foreignType = (EntityType) ((SingularAttribute) attribute).getType();
-            
+
             return input ? getWhereInputType(foreignType) : new GraphQLTypeReference(foreignType.getName());
-        } 
+        }
         else if (isElementCollection(attribute)) {
             Type foreignType = ((PluralAttribute) attribute).getElementType();
-            
+
             if(foreignType.getPersistenceType() == Type.PersistenceType.BASIC) {
                 GraphQLType graphQLType = getGraphQLTypeFromJavaType(foreignType.getJavaType());
-                
+
             	return input ? graphQLType : new GraphQLList(graphQLType);
             }
         }
@@ -1005,15 +1056,15 @@ else if (isElementCollection(attribute)) {
     protected final boolean isEmbeddable(Attribute<?,?> attribute) {
     	return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED;
     }
-    
+
     protected final boolean isBasic(Attribute<?,?> attribute) {
     	return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC;
     }
-    
+
     protected final boolean isElementCollection(Attribute<?,?> attribute) {
     	return  attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION;
     }
-    
+
     protected final boolean isToMany(Attribute<?,?> attribute) {
     	return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_MANY
         		|| attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_MANY;
@@ -1022,12 +1073,12 @@ protected final boolean isToMany(Attribute<?,?> attribute) {
     protected final boolean isOneToMany(Attribute<?,?> attribute) {
         return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_MANY;
     }
-    
+
     protected final boolean isToOne(Attribute<?,?> attribute) {
     	return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_ONE
         		|| attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_ONE;
     }
-    
+
 
     protected final boolean isValidInput(Attribute<?,?> attribute) {
         return attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.BASIC ||
@@ -1040,7 +1091,7 @@ private String getSchemaDescription(Attribute<?,?> attribute) {
                                  .getSchemaDescription(attribute.getName())
                                  .orElse(null);
     }
-    
+
     private String getSchemaDescription(EntityType<?> entityType) {
         return EntityIntrospector.introspect(entityType)
                                  .getSchemaDescription()
@@ -1052,11 +1103,11 @@ private String getSchemaDescription(EmbeddableType<?> embeddableType) {
                                  .getSchemaDescription()
                                  .orElse(null);
     }
-    
+
     private boolean isNotIgnored(EmbeddableType<?> attribute) {
         return isNotIgnored(attribute.getJavaType());
     }
-    
+
     private boolean isNotIgnored(Attribute<?,?> attribute) {
         return isNotIgnored(attribute.getJavaMember()) && isNotIgnored(attribute.getJavaType());
     }
@@ -1064,11 +1115,11 @@ private boolean isNotIgnored(Attribute<?,?> attribute) {
     private boolean isIdentity(Attribute<?,?> attribute) {
         return attribute instanceof SingularAttribute && ((SingularAttribute<?,?>)attribute).isId();
     }
-    
+
     private boolean isNotIgnored(EntityType<?> entityType) {
         return isNotIgnored(entityType.getJavaType()) && isNotIgnored(entityType.getJavaType().getName());
     }
-    
+
     private boolean isNotIgnored(String name) {
         return entityPaths.isEmpty() || entityPaths.stream()
                                                    .anyMatch(prefix -> name.startsWith(prefix));
@@ -1112,14 +1163,14 @@ protected boolean isNotIgnoredOrder(Attribute<?,?> attribute) {
         return false;
     }
 
-    
+
     @SuppressWarnings( "unchecked" )
     private GraphQLOutputType getGraphQLTypeFromJavaType(Class<?> clazz) {
         if (clazz.isEnum()) {
-            
+
             if (classCache.containsKey(clazz))
                 return classCache.get(clazz);
-            
+
             GraphQLEnumType.Builder enumBuilder = GraphQLEnumType.newEnum().name(clazz.getSimpleName());
             int ordinal = 0;
             for (Enum<?> enumValue : ((Class<Enum<?>>)clazz).getEnumConstants())
@@ -1129,7 +1180,7 @@ private GraphQLOutputType getGraphQLTypeFromJavaType(Class<?> clazz) {
             setNoOpCoercing(enumType);
 
             classCache.putIfAbsent(clazz, enumType);
-            
+
             return enumType;
         } else if (clazz.isArray()) {
             return GraphQLList.list(JavaScalars.of(clazz.getComponentType()));
@@ -1139,21 +1190,21 @@ private GraphQLOutputType getGraphQLTypeFromJavaType(Class<?> clazz) {
     }
 
     protected GraphQLInputType getFieldsEnumType(EntityType<?> entityType) {
-            
+
         GraphQLEnumType.Builder enumBuilder = GraphQLEnumType.newEnum().name(entityType.getName()+"FieldsEnum");
         final AtomicInteger ordinal = new AtomicInteger();
-        
+
         entityType.getAttributes().stream()
             .filter(this::isValidInput)
             .filter(this::isNotIgnored)
             .forEach(it -> enumBuilder.value(it.getName(), ordinal.incrementAndGet()));
-        
+
         GraphQLInputType answer = enumBuilder.build();
         setNoOpCoercing(answer);
 
         return answer;
     }
-    
+
     /**
      * JPA will deserialize Enum's for us...we don't want GraphQL doing it.
      *
@@ -1168,8 +1219,8 @@ private void setNoOpCoercing(GraphQLType type) {
             log.error("Unable to set coercing for " + type, e);
         }
     }
-    
-    private static final GraphQLArgument paginationArgument = 
+
+    private static final GraphQLArgument paginationArgument =
             newArgument().name(PAGE_PARAM_NAME)
                          .description("Page object for pageble requests, specifying the requested start page and limit size.")
                          .type(newInputObject().name("Page")
@@ -1194,7 +1245,7 @@ private void setNoOpCoercing(GraphQLType type) {
                     .value("DESC", "DESC", "Descending")
                     .build();
 
-    
+
     /**
      * @return the name
      */
@@ -1208,7 +1259,7 @@ public String getName() {
     @Override
     public GraphQLJpaSchemaBuilder name(String name) {
         this.name = name;
-        
+
         return this;
     }
 
@@ -1255,7 +1306,7 @@ public GraphQLJpaSchemaBuilder defaultDistinct(boolean isDefaultDistinct) {
 
         return this;
     }
-    
+
     /**
      * @param namingStrategy the namingStrategy to set
      */
@@ -1263,7 +1314,7 @@ public void setNamingStrategy(NamingStrategy namingStrategy) {
         this.namingStrategy = namingStrategy;
     }
 
-    
+
     static class NoOpCoercing implements Coercing<Object, Object> {
 
         @Override
@@ -1285,21 +1336,21 @@ public Object parseLiteral(Object input) {
     @Override
     public GraphQLJpaSchemaBuilder entityPath(String path) {
         Assert.assertNotNull(path, "path is null");
-        
+
         entityPaths.add(path);
-        
+
         return this;
     }
 
     @Override
     public GraphQLJpaSchemaBuilder namingStrategy(NamingStrategy instance) {
         Assert.assertNotNull(instance, "instance is null");
-        
+
         this.namingStrategy = instance;
-        
+
         return this;
     }
-    
+
     public boolean isToManyDefaultOptional() {
         return toManyDefaultOptional;
     }
@@ -1311,10 +1362,10 @@ public void setToManyDefaultOptional(boolean toManyDefaultOptional) {
 
     public GraphQLJpaSchemaBuilder toManyDefaultOptional(boolean toManyDefaultOptional) {
         this.toManyDefaultOptional = toManyDefaultOptional;
-        
+
         return this;
     }
-    
+
     public boolean isEnableSubscription() {
         return enableSubscription;
     }
@@ -1328,23 +1379,23 @@ public GraphQLJpaSchemaBuilder enableSubscription(boolean enableSubscription) {
     public boolean isEnableDeferDirective() {
         return enableDeferDirective;
     }
-    
+
     public GraphQLJpaSchemaBuilder enableDeferDirective(boolean enableDeferDirective) {
         this.enableDeferDirective = enableDeferDirective;
 
         return this;
     }
-    
+
     public boolean isEnableRelay() {
         return enableRelay;
     }
-    
+
     public GraphQLJpaSchemaBuilder enableRelay(boolean enableRelay) {
         this.enableRelay = enableRelay;
 
         return this;
     }
-    
+
     public int getDefaultMaxResults() {
         return defaultMaxResults;
     }
@@ -1358,7 +1409,7 @@ public GraphQLJpaSchemaBuilder defaultMaxResults(int defaultMaxResults) {
     public int getDefaultPageLimitSize() {
         return defaultPageLimitSize;
     }
-    
+
     public GraphQLJpaSchemaBuilder defaultPageLimitSize(int defaultPageLimitSize) {
         this.defaultPageLimitSize = defaultPageLimitSize;
 
@@ -1368,11 +1419,11 @@ public GraphQLJpaSchemaBuilder defaultPageLimitSize(int defaultPageLimitSize) {
     public int getDefaultFetchSize() {
         return defaultFetchSize;
     }
-    
+
     public GraphQLJpaSchemaBuilder defaultFetchSize(int defaultFetchSize) {
         this.defaultFetchSize = defaultFetchSize;
 
         return this;
     }
-    
+
 }
\ No newline at end of file
diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToManyDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToManyDataFetcher.java
new file mode 100644
index 000000000..1ce48fa45
--- /dev/null
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToManyDataFetcher.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017 IntroPro Ventures Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.introproventures.graphql.jpa.query.schema.impl;
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.persistence.metamodel.PluralAttribute;
+
+import org.dataloader.DataLoader;
+import org.dataloader.DataLoaderOptions;
+import org.dataloader.DataLoaderRegistry;
+import org.dataloader.MappedBatchLoaderWithContext;
+
+import com.introproventures.graphql.jpa.query.support.GraphQLSupport;
+import graphql.GraphQLContext;
+import graphql.language.Argument;
+import graphql.language.Field;
+import graphql.schema.DataFetcher;
+import graphql.schema.DataFetchingEnvironment;
+import graphql.schema.GraphQLType;
+
+/**
+ * One-To-Many DataFetcher that uses where argument to filter collection attributes
+ *
+ * @author Igor Dianov
+ *
+ */
+class GraphQLJpaToManyDataFetcher implements DataFetcher<Object> {
+
+    private final PluralAttribute<Object,Object,Object> attribute;
+    private final GraphQLJpaQueryFactory queryFactory;
+
+    public GraphQLJpaToManyDataFetcher(GraphQLJpaQueryFactory queryFactory,
+                                          PluralAttribute<Object,Object,Object> attribute) {
+        this.queryFactory = queryFactory;
+        this.attribute = attribute;
+    }
+
+    @Override
+    public Object get(DataFetchingEnvironment environment) {
+        Field field = environment.getField();
+        GraphQLType parentType = environment.getParentType();
+
+        Object source = environment.getSource();
+        Optional<Argument> whereArg = GraphQLSupport.getWhereArgument(field);
+
+        // Resolve collection query if where argument is present or any field in selection has orderBy argument
+        if (whereArg.isPresent() || queryFactory.hasAnySelectionOrderBy(field)) {
+            Object parentIdValue = queryFactory.getParentIdAttributeValue(source);
+            String dataLoaderKey = parentType.getName() + "." + Optional.ofNullable(field.getAlias())
+                                                                        .orElseGet(attribute::getName);
+
+            DataLoader<Object, List<Object>> dataLoader = getDataLoader(environment,
+                                                                        dataLoaderKey);
+
+            return dataLoader.load(parentIdValue, environment);
+        }
+
+        // Let hibernate resolve collection query
+        return queryFactory.getAttributeValue(source,
+                                              attribute);
+    }
+
+    protected DataLoader<Object, List<Object>> getDataLoader(DataFetchingEnvironment environment,
+                                                             String dataLoaderKey) {
+        GraphQLContext context = environment.getContext();
+        DataLoaderRegistry dataLoaderRegistry = context.get("dataLoaderRegistry");
+
+        if (!dataLoaderRegistry.getKeys()
+                              .contains(dataLoaderKey)) {
+            synchronized (dataLoaderRegistry) {
+                MappedBatchLoaderWithContext<Object, List<Object>> mappedBatchLoader = new GraphQLJpaToManyMappedBatchLoader(queryFactory);
+
+                DataLoaderOptions options = DataLoaderOptions.newOptions()
+                                                             .setCachingEnabled(false);
+
+                DataLoader<Object, List<Object>> dataLoader = DataLoader.newMappedDataLoader(mappedBatchLoader,
+                                                                                             options);
+                dataLoaderRegistry.register(dataLoaderKey, dataLoader);
+            }
+        }
+
+        return  dataLoaderRegistry.getDataLoader(dataLoaderKey);
+    }
+
+}
diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToManyMappedBatchLoader.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToManyMappedBatchLoader.java
new file mode 100644
index 000000000..279fdde66
--- /dev/null
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToManyMappedBatchLoader.java
@@ -0,0 +1,33 @@
+package com.introproventures.graphql.jpa.query.schema.impl;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+import org.dataloader.BatchLoaderEnvironment;
+import org.dataloader.MappedBatchLoaderWithContext;
+
+import graphql.schema.DataFetchingEnvironment;
+
+// a batch loader function that will be called with N or more keys for batch loading
+class GraphQLJpaToManyMappedBatchLoader implements MappedBatchLoaderWithContext<Object, List<Object>> {
+
+    private final GraphQLJpaQueryFactory queryFactory;
+
+    public GraphQLJpaToManyMappedBatchLoader(GraphQLJpaQueryFactory queryFactory) {
+        this.queryFactory = queryFactory;
+    }
+
+    @Override
+    public CompletionStage<Map<Object, List<Object>>> load(Set<Object> keys, BatchLoaderEnvironment environment) {
+        Object key = keys.iterator().next();
+        DataFetchingEnvironment context = (DataFetchingEnvironment) environment.getKeyContexts()
+                                                                               .get(key);
+
+        return CompletableFuture.supplyAsync(() -> queryFactory.loadOneToMany(context, keys));
+    }
+
+
+};
diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToOneDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToOneDataFetcher.java
new file mode 100644
index 000000000..ded77cbc6
--- /dev/null
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToOneDataFetcher.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017 IntroPro Ventures Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.introproventures.graphql.jpa.query.schema.impl;
+
+import java.util.Optional;
+
+import javax.persistence.metamodel.Attribute.PersistentAttributeType;
+import javax.persistence.metamodel.SingularAttribute;
+
+import org.dataloader.DataLoader;
+import org.dataloader.DataLoaderOptions;
+import org.dataloader.DataLoaderRegistry;
+import org.dataloader.MappedBatchLoaderWithContext;
+
+import graphql.GraphQLContext;
+import graphql.language.Field;
+import graphql.schema.DataFetcher;
+import graphql.schema.DataFetchingEnvironment;
+import graphql.schema.GraphQLType;
+
+/**
+ * One-To-Many DataFetcher that uses where argument to filter collection attributes
+ *
+ * @author Igor Dianov
+ *
+ */
+class GraphQLJpaToOneDataFetcher implements DataFetcher<Object> {
+
+    private final SingularAttribute<Object,Object> attribute;
+    private final GraphQLJpaQueryFactory queryFactory;
+
+    public GraphQLJpaToOneDataFetcher(GraphQLJpaQueryFactory queryFactory,
+                                      SingularAttribute<Object,Object> attribute) {
+        this.queryFactory = queryFactory;
+        this.attribute = attribute;
+    }
+
+    @Override
+    public Object get(DataFetchingEnvironment environment) {
+        Field field = environment.getField();
+        GraphQLType parentType = environment.getParentType();
+
+        Object source = environment.getSource();
+        Boolean isOptional = queryFactory.getOptionalArgumentValue(environment,
+                                                                   field,
+                                                                   attribute);
+        // Resolve collection query if where argument is present
+        if (isOptional && !PersistentAttributeType.EMBEDDED.equals(attribute.getPersistentAttributeType())) {
+            Object parentIdValue = queryFactory.getParentIdAttributeValue(source);
+            String dataLoaderKey = parentType.getName() + "." + Optional.ofNullable(field.getAlias())
+                                                                        .orElseGet(attribute::getName);
+
+            DataLoader<Object, Object> dataLoader = getDataLoader(environment,
+                                                                  dataLoaderKey);
+
+            return dataLoader.load(parentIdValue, environment);
+        }
+
+        // Let hibernate resolve collection query
+        return queryFactory.getAttributeValue(source,
+                                              attribute);
+    }
+
+    protected DataLoader<Object, Object> getDataLoader(DataFetchingEnvironment environment,
+                                                       String dataLoaderKey) {
+        GraphQLContext context = environment.getContext();
+        DataLoaderRegistry dataLoaderRegistry = context.get("dataLoaderRegistry");
+
+        if (!dataLoaderRegistry.getKeys()
+                               .contains(dataLoaderKey)) {
+            synchronized (dataLoaderRegistry) {
+                MappedBatchLoaderWithContext<Object, Object> mappedBatchLoader = new GraphQLJpaToOneMappedBatchLoader(queryFactory);
+
+                DataLoaderOptions options = DataLoaderOptions.newOptions()
+                                                             .setCachingEnabled(false);
+
+                DataLoader<Object, Object> dataLoader = DataLoader.newMappedDataLoader(mappedBatchLoader,
+                                                                                       options);
+                dataLoaderRegistry.register(dataLoaderKey, dataLoader);
+            }
+        }
+
+        return environment.getDataLoader(dataLoaderKey);
+    }
+
+}
diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToOneMappedBatchLoader.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToOneMappedBatchLoader.java
new file mode 100644
index 000000000..93fb4668b
--- /dev/null
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaToOneMappedBatchLoader.java
@@ -0,0 +1,30 @@
+package com.introproventures.graphql.jpa.query.schema.impl;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+import org.dataloader.BatchLoaderEnvironment;
+import org.dataloader.MappedBatchLoaderWithContext;
+
+import graphql.schema.DataFetchingEnvironment;
+
+// a batch loader function that will be called with N or more keys for batch loading
+class GraphQLJpaToOneMappedBatchLoader implements MappedBatchLoaderWithContext<Object, Object> {
+
+    private final GraphQLJpaQueryFactory queryFactory;
+
+    public GraphQLJpaToOneMappedBatchLoader(GraphQLJpaQueryFactory queryFactory) {
+        this.queryFactory = queryFactory;
+    }
+
+    @Override
+    public CompletionStage<Map<Object, Object>> load(Set<Object> keys, BatchLoaderEnvironment environment) {
+        Object key = keys.iterator().next();
+        DataFetchingEnvironment context = (DataFetchingEnvironment) environment.getKeyContexts()
+                                                                               .get(key);
+
+        return CompletableFuture.supplyAsync(() -> queryFactory.loadManyToOne(context, keys));
+    }
+};
diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/support/GraphQLSupport.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/support/GraphQLSupport.java
index 7d5256a98..8779f1d2d 100644
--- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/support/GraphQLSupport.java
+++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/support/GraphQLSupport.java
@@ -4,20 +4,22 @@
 import static com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder.QUERY_WHERE_PARAM_NAME;
 
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Collector;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder;
 import com.introproventures.graphql.jpa.query.schema.impl.PageArgument;
-
 import graphql.language.Argument;
 import graphql.language.Field;
 import graphql.language.ObjectField;
@@ -32,16 +34,16 @@ public static Stream<Field> fields(SelectionSet selectionSet) {
                            .stream()
                            .filter(Field.class::isInstance)
                            .map(Field.class::cast);
-        
+
     }
 
     public static Optional<Field> searchByFieldName(Field root, String fieldName) {
         Predicate<Field> matcher = (field) -> fieldName.equals(field.getName());
 
         return search(root, matcher);
-    }    
+    }
+
 
-    
     public static Optional<Field> search(Field root, Predicate<Field> predicate) {
         Queue<Field> queue = new ArrayDeque<>();
         queue.add(root);
@@ -58,7 +60,7 @@ public static Optional<Field> search(Field root, Predicate<Field> predicate) {
         }
 
         return Optional.empty();
-    }    
+    }
 
     public static final Collection<Field> selections(Field field) {
         SelectionSet selectionSet = Optional.ofNullable(field.getSelectionSet())
@@ -67,7 +69,7 @@ public static final Collection<Field> selections(Field field) {
 
         return fields(selectionSet).collect(Collectors.toList());
     }
-    
+
     public static  Optional<Argument> getPageArgument(Field field) {
         return field.getArguments()
             .stream()
@@ -81,13 +83,13 @@ public static  Optional<Argument> getWhereArgument(Field field) {
             .filter(it -> QUERY_WHERE_PARAM_NAME.equals(it.getName()))
             .findFirst();
     }
-    
+
     public static  PageArgument extractPageArgument(DataFetchingEnvironment environment, Optional<Argument> paginationRequest, int defaultPageLimitSize) {
 
         if (paginationRequest.isPresent()) {
 
             Map<String, Integer> pagex = environment.getArgument(GraphQLJpaSchemaBuilder.PAGE_PARAM_NAME);
-            
+
             Integer start = pagex.getOrDefault(GraphQLJpaSchemaBuilder.PAGE_START_PARAM_NAME, 1);
             Integer limit = pagex.getOrDefault(GraphQLJpaSchemaBuilder.PAGE_LIMIT_PARAM_NAME, defaultPageLimitSize);
 
@@ -96,7 +98,7 @@ public static  PageArgument extractPageArgument(DataFetchingEnvironment environm
 
         return new PageArgument(1, defaultPageLimitSize);
     }
-    
+
     public static  Field removeArgument(Field field, Optional<Argument> argument) {
 
         if (!argument.isPresent()) {
@@ -109,7 +111,7 @@ public static  Field removeArgument(Field field, Optional<Argument> argument) {
         return field.transform(builder -> builder.arguments(newArguments));
 
       }
-    
+
     public static Boolean isWhereArgument(Argument argument) {
         return GraphQLJpaSchemaBuilder.QUERY_WHERE_PARAM_NAME.equals(argument.getName());
     }
@@ -125,7 +127,7 @@ public static Boolean isFirstArgument(Argument argument) {
     public static Boolean isAfterArgument(Argument argument) {
         return "after".equals(argument.getName());
     }
-    
+
     public static Boolean isLogicalArgument(Argument argument) {
         return GraphQLJpaSchemaBuilder.QUERY_LOGICAL_PARAM_NAME.equals(argument.getName());
     }
@@ -133,7 +135,7 @@ public static Boolean isLogicalArgument(Argument argument) {
     public static Boolean isDistinctArgument(Argument argument) {
         return GraphQLJpaSchemaBuilder.SELECT_DISTINCT_PARAM_NAME.equals(argument.getName());
     }
-    
+
     public static final Optional<ObjectField> getObjectField(ObjectValue objectValue, String fieldName) {
         return objectValue.getObjectFields().stream()
                                             .filter(it -> fieldName.equals(it.getName()))
@@ -144,6 +146,40 @@ public static final Optional<Field> getSelectionField(Field field, String fieldN
         return GraphQLSupport.fields(field.getSelectionSet())
                              .filter(it -> fieldName.equals(it.getName()))
                              .findFirst();
-    }    
-    
+    }
+
+    public static Collector<Object, List<Object>, List<Object>> toResultList() {
+        return Collector.of(ArrayList::new,
+                            (list, item) -> {
+                                if (item != null) {
+                                    list.add(item);
+                                }
+                            },
+                            (left, right) -> {
+                                left.addAll(right);
+                                return left;
+                            },
+                            Collector.Characteristics.CONCURRENT);
+    }
+
+    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
+        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
+
+        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
+    }
+
+    public static String identityToString(final Object object) {
+        if (object == null) {
+            return null;
+        }
+        final String name = object.getClass().getName();
+        final String hexString = Integer.toHexString(System.identityHashCode(object));
+        final StringBuilder builder = new StringBuilder(name.length() + 1 + hexString.length());
+        // @formatter:off
+        builder.append(name)
+              .append("@")
+              .append(hexString);
+        // @formatter:off
+        return builder.toString();
+    }
 }
diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLEnumVariableBindingsTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLEnumVariableBindingsTests.java
index aa202b42f..0e2245283 100644
--- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLEnumVariableBindingsTests.java
+++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLEnumVariableBindingsTests.java
@@ -191,9 +191,11 @@ public void queryEnumArrayVariableBindingInEmbeddedRelation() {
                 +   "{id=1, name=Leo Tolstoy, books=["
                 +       "{id=2, title=War and Peace, genre=NOVEL}, "
                 +       "{id=3, title=Anna Karenina, genre=NOVEL}"
-                +   "]}"
+                +   "]}, "
+                +   "{id=4, name=Anton Chekhov, books=[]}, "
+                +   "{id=8, name=Igor Dianov, books=[]}"
                 + "]}}";
-
+        
         //when
         Object result = executor.execute(query, variables).getData();
 
diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java
index c774276a7..b2f621ec7 100644
--- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java
+++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java
@@ -42,7 +42,6 @@
 import com.introproventures.graphql.jpa.query.AbstractSpringBootTestSupport;
 import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor;
 import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder;
-
 import graphql.ErrorType;
 import graphql.ExecutionResult;
 import graphql.GraphQLError;
@@ -52,7 +51,7 @@
 
 @SpringBootTest
 public class GraphQLExecutorTests extends AbstractSpringBootTestSupport {
-    
+
     @SpringBootApplication
     static class Application {
         @Bean
@@ -62,14 +61,14 @@ public GraphQLExecutor graphQLExecutor(final GraphQLSchemaBuilder graphQLSchemaB
 
         @Bean
         public GraphQLSchemaBuilder graphQLSchemaBuilder(final EntityManager entityManager) {
-            
+
             return new GraphQLJpaSchemaBuilder(entityManager)
                 .name("GraphQLBooks")
                 .description("Books JPA test schema");
         }
-        
+
     }
-    
+
     @Autowired
     private GraphQLExecutor executor;
 
@@ -77,12 +76,12 @@ public GraphQLSchemaBuilder graphQLSchemaBuilder(final EntityManager entityManag
     public static void init() {
         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
     }
-    
+
     @Test
     public void contextLoads() {
         Assert.isAssignable(GraphQLExecutor.class, executor.getClass());
     }
-    
+
     @Test
     public void GetsAllThings() {
         //given
@@ -94,14 +93,14 @@ public void GetsAllThings() {
 
         //then
         assertThat(result.toString()).isEqualTo(expected);
-        
+
     }
 
     @Test
     public void queryForThingById() {
         //given
         String query = "query ThingByIdQuery { Thing(id: \"2d1ebc5b-7d27-4197-9cf0-e84451c5bbb1\") { id type } }";
-        
+
         String expected = "{Thing={id=2d1ebc5b-7d27-4197-9cf0-e84451c5bbb1, type=Thing1}}";
 
         //when
@@ -139,7 +138,7 @@ public void queryEmptyParameter() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @SuppressWarnings( { "rawtypes", "unchecked", "serial" } )
     @Test
     public void queryWithParameterNoResult() {
@@ -221,14 +220,14 @@ public void queryWithAlias() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
-    
+
+
     // https://github.com/introproventures/graphql-jpa-query/issues/33
     @Test
     public void queryForElementCollection() {
         //given
         String query = "{ Author(id: 1) { id name, phoneNumbers } }";
-        
+
         String expected = "{Author={id=1, name=Leo Tolstoy, phoneNumbers=[1-123-1234, 1-123-5678]}}";
 
         //when
@@ -242,7 +241,7 @@ public void queryForElementCollection() {
     public void queryForEnumIn() {
         //given
         String query = "{ Books(where: {genre: {IN: PLAY}}) { select { id title, genre } }}";
-        
+
         String expected = "{Books={select=["
         		+ "{id=5, title=The Cherry Orchard, genre=PLAY}, "
         		+ "{id=6, title=The Seagull, genre=PLAY}, "
@@ -255,12 +254,12 @@ public void queryForEnumIn() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryForEnumInArray() {
         //given
         String query = "{ Books(where: {genre: {IN: [NOVEL, PLAY]}}) { select { id title, genre } }}";
-        
+
         String expected = "{Books={select=["
         		+ "{id=2, title=War and Peace, genre=NOVEL}, "
         		+ "{id=3, title=Anna Karenina, genre=NOVEL}, "
@@ -280,7 +279,7 @@ public void queryForEnumInArray() {
     public void queryForEnumNinArray() {
         //given
         String query = "{ Books(where: {genre: {NIN: [NOVEL]}}) { select { id title, genre } }}";
-        
+
         String expected = "{Books={select=["
         		+ "{id=5, title=The Cherry Orchard, genre=PLAY}, "
         		+ "{id=6, title=The Seagull, genre=PLAY}, "
@@ -293,12 +292,12 @@ public void queryForEnumNinArray() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryForEnumEq() {
         //given
         String query = "{ Books(where: {genre: {EQ: NOVEL}}) { select { id title, genre } }}";
-        
+
         String expected = "{Books={select=["
         		+ "{id=2, title=War and Peace, genre=NOVEL}, "
         		+ "{id=3, title=Anna Karenina, genre=NOVEL}"
@@ -315,7 +314,7 @@ public void queryForEnumEq() {
     public void queryForEnumNe() {
         //given
         String query = "{ Books(where: {genre: {NE: PLAY}}) { select { id title, genre } }}";
-        
+
         String expected = "{Books={select=["
         		+ "{id=2, title=War and Peace, genre=NOVEL}, "
         		+ "{id=3, title=Anna Karenina, genre=NOVEL}"
@@ -327,12 +326,12 @@ public void queryForEnumNe() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryForEnumNin() {
         //given
         String query = "{ Books(where: {genre: {NIN: PLAY}}) { select { id title, genre } }}";
-        
+
         String expected = "{Books={select=["
         		+ "{id=2, title=War and Peace, genre=NOVEL}, "
         		+ "{id=3, title=Anna Karenina, genre=NOVEL}"
@@ -344,12 +343,12 @@ public void queryForEnumNin() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-        
+
     @Test
     public void queryForParentWithEnum() {
         //given
         String query = "{ Books { select { id title, author( where: { genre: { EQ: NOVEL } }) { name } } } }";
-        
+
         String expected = "{Books={select=["
         		+ "{id=2, title=War and Peace, author={name=Leo Tolstoy}}, "
         		+ "{id=3, title=Anna Karenina, author={name=Leo Tolstoy}}"
@@ -361,32 +360,33 @@ public void queryForParentWithEnum() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryAuthorBooksWithExplictOptional() {
         //given
         String query = "query { "
-                + "Authors(" + 
-                "    where: {" + 
-                "      books: {" + 
-                "        title: {LIKE: \"War\"}" + 
-                "      }" + 
-                "    }" + 
-                "  ) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books(optional: true) {" + 
-                "        id" + 
-                "        title(orderBy: ASC)" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                + "Authors(" +
+                "    where: {" +
+                "      books: {" +
+                "        title: {LIKE: \"War\"}" +
+                "      }" +
+                "    }" +
+                "  ) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books(optional: true) {" +
+                "        id" +
+                "        title(orderBy: ASC)" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }"
                 + "}";
-        
+
         String expected = "{Authors={select=["
                 + "{id=1, name=Leo Tolstoy, books=["
+                + "{id=3, title=Anna Karenina, genre=NOVEL}, "
                 + "{id=2, title=War and Peace, genre=NOVEL}]}"
                 + "]}}";
 
@@ -396,32 +396,32 @@ public void queryAuthorBooksWithExplictOptional() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-       
+
     @Test
     public void queryAuthorBooksWithExplictOptionalEXISTS() {
         //given
         String query = "query { "
-                + "Authors(" + 
+                + "Authors(" +
                 "    where: {" +
                 "      EXISTS: {" +
-                "        books: {" + 
-                "          title: {LIKE: \"War\"}" + 
-                "        }" + 
-                "      }" + 
-                "    }" + 
-                "  ) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books(optional: true) {" + 
-                "        id" + 
-                "        title(orderBy: ASC)" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                "        books: {" +
+                "          title: {LIKE: \"War\"}" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  ) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books(optional: true) {" +
+                "        id" +
+                "        title(orderBy: ASC)" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }"
                 + "}";
-        
+
         String expected = "{Authors={select=["
                 + "{id=1, name=Leo Tolstoy, books=[{id=3, title=Anna Karenina, genre=NOVEL}, "
                 + "{id=2, title=War and Peace, genre=NOVEL}]}"
@@ -432,30 +432,154 @@ public void queryAuthorBooksWithExplictOptionalEXISTS() {
 
         // then
         assertThat(result.toString()).isEqualTo(expected);
-    }    
+    }
+
+    @Test
+    public void queryAuthorBooksWithCollectionOrderBy() {
+        //given
+        String query = "query { "
+                + "Authors {" +
+                "    select {" +
+                "      id" +
+                "      name(orderBy: ASC)" +
+                "      books {" +
+                "        id" +
+                "        title(orderBy: DESC)" +
+                "      }" +
+                "    }" +
+                "  }"
+                + "}";
+
+        String expected = "{Authors={select=["
+                +   "{id=4, name=Anton Chekhov, books=["
+                +       "{id=7, title=Three Sisters}, "
+                +       "{id=6, title=The Seagull}, "
+                +       "{id=5, title=The Cherry Orchard}"
+                +   "]}, "
+                +   "{id=8, name=Igor Dianov, books=[]}, "
+                +   "{id=1, name=Leo Tolstoy, books=["
+                +       "{id=2, title=War and Peace}, "
+                +       "{id=3, title=Anna Karenina}"
+                +   "]}"
+                + "]}}";
+
+        //when
+        Object result = executor.execute(query).getData();
+
+        // then
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+    @Test
+    public void queryBooksAuthorWithImplicitOptionalFalse() {
+        //given
+        String query = "query { "
+                + "Books {" +
+                "    select {" +
+                "      id" +
+                "      title" +
+                "      author(where: {name: {LIKE: \"Leo\"}}) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }"
+                + "}";
+
+        String expected = "{Books={select=["
+                +   "{id=2, title=War and Peace, author={name=Leo Tolstoy}}, "
+                +   "{id=3, title=Anna Karenina, author={name=Leo Tolstoy}}"
+                + "]}}";
+
+        //when
+        Object result = executor.execute(query).getData();
+
+        // then
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+    @Test
+    public void queryAuthorBooksByAlliasesWithInlineCollections() {
+        //given
+        String query = "query { "
+                + " Authors {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      War: books(where: {title: {LIKE: \"War\"}}) {" +
+                "        title" +
+                "      }" +
+                "      Anna: books(where: {title: {LIKE: \"Anna\"}}) {" +
+                "        title" +
+                "      }" +
+                "    }" +
+                "  }"
+                + "}";
+
+        String expected = "{Authors={select=["
+                +   "{id=1, name=Leo Tolstoy, War=[{title=War and Peace}], Anna=[{title=Anna Karenina}]}, "
+                +   "{id=4, name=Anton Chekhov, War=[], Anna=[]}, "
+                +   "{id=8, name=Igor Dianov, War=[], Anna=[]}"
+                + "]}}";
+
+        //when
+        Object result = executor.execute(query).getData();
+
+        // then
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+    @Test
+    public void queryAuthorBooksByAlliasesWithInlineWhereSearch() {
+        //given
+        String query = "query { "
+                + " Authors(where: {name: {LIKE: \"Leo\"}}) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      War: books(where: {title: {LIKE: \"War\"}}) {" +
+                "        title" +
+                "      }" +
+                "      Anna: books(where: {title: {LIKE: \"Anna\"}}) {" +
+                "        title" +
+                "      }" +
+                "    }" +
+                "  }"
+                + "}";
+
+        String expected = "{Authors={select=["
+                +   "{id=1, name=Leo Tolstoy, War=[{title=War and Peace}], Anna=[{title=Anna Karenina}]}"
+                + "]}}";
+
+        //when
+        Object result = executor.execute(query).getData();
+
+        // then
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
     @Test
     public void queryAuthorBooksWithIsNullId() {
         //given
         String query = "query { "
-                + "Authors(" + 
-                "    where: {" + 
-                "      books: {" + 
-                "        id: {IS_NULL: true}" + 
-                "      }" + 
-                "    }" + 
-                "  ) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                + "Authors(" +
+                "    where: {" +
+                "      books: {" +
+                "        id: {IS_NULL: true}" +
+                "      }" +
+                "    }" +
+                "  ) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }"
                 + "}";
-        
+
         String expected = "{Authors={select=[{id=8, name=Igor Dianov, books=[]}]}}";
 
         //when
@@ -464,14 +588,43 @@ public void queryAuthorBooksWithIsNullId() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
-    
+
+    @Test
+    public void queryBooksAuthorWithExplictOptionalTrue() {
+        //given
+        String query = "query { "
+                + "Books {" +
+                "    select {" +
+                "      id" +
+                "      title" +
+                "      author(optional: true, where: {name: {LIKE: \"Leo\"}}) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }"
+                + "}";
+
+        String expected = "{Books={select=["
+                +   "{id=2, title=War and Peace, author={name=Leo Tolstoy}}, "
+                +   "{id=3, title=Anna Karenina, author={name=Leo Tolstoy}}, "
+                +   "{id=5, title=The Cherry Orchard, author=null}, "
+                +   "{id=6, title=The Seagull, author=null}, "
+                +   "{id=7, title=Three Sisters, author=null}"
+                + "]}}";
+
+        //when
+        Object result = executor.execute(query).getData();
+
+        // then
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
     // https://github.com/introproventures/graphql-jpa-query/issues/30
     @Test
     public void queryForEntityWithMappedSuperclass() {
         //given
         String query = "{ Car(id: \"1\") { id brand } }";
-        
+
         String expected = "{Car={id=1, brand=Ford}}";
 
         //when
@@ -486,7 +639,7 @@ public void queryForEntityWithMappedSuperclass() {
     public void queryForEntityWithEmbeddedIdAndEmbeddedField() {
         //given
         String query = "{ Boat(boatId: {id: \"1\" country: \"EN\"}) { boatId {id country} engine { identification } } }";
-        
+
         String expected = "{Boat={boatId={id=1, country=EN}, engine={identification=12345}}}";
 
         //when
@@ -509,7 +662,7 @@ public void queryForEntityWithEmbeddedFieldWithWhere() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
- 
+
     @Test
     public void queryWithNumericBetweenPredicate() {
         //given:
@@ -578,8 +731,8 @@ public void queryWithDateNotBetweenPredicate() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
 
     @Test
     public void queryForEntitiesWithWithEmbeddedIdWithWhere() {
@@ -594,21 +747,21 @@ public void queryForEntitiesWithWithEmbeddedIdWithWhere() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryForBooksWithWhereAuthorById() {
         //given
         String query = "query { "
-                + "Books(where: {author: {id: {EQ: 1}}}) {" + 
-                "    select {" + 
-                "      id" + 
-                "      title" + 
-                "      genre" + 
-                "      author {" + 
-                "        id" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
+                + "Books(where: {author: {id: {EQ: 1}}}) {" +
+                "    select {" +
+                "      id" +
+                "      title" +
+                "      genre" +
+                "      author {" +
+                "        id" +
+                "        name" +
+                "      }" +
+                "    }" +
                 "  }"+
                 "}";
 
@@ -622,20 +775,20 @@ public void queryForBooksWithWhereAuthorById() {
 
         // then
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryForBooksWithWhereAuthorEqIdWithVariables() {
         //given
         String query = "query($authorId: Long ) { "
-                + "  Books(where: {" + 
-                "    author: {id: {EQ: $authorId}}" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      title" + 
-                "      genre" + 
-                "    }" + 
+                + "  Books(where: {" +
+                "    author: {id: {EQ: $authorId}}" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      title" +
+                "      genre" +
+                "    }" +
                 "  }"+
                 "}";
         Map<String, Object> variables = new HashMap<String, Object>() {{
@@ -653,27 +806,27 @@ public void queryForBooksWithWhereAuthorEqIdWithVariables() {
 
         // then
         assertThat(result.toString()).isEqualTo(expected);
-    }    
+    }
 
     @Test
     public void queryForAuthorsWithWhereEXISTSBooksLIKETitle() {
         //given
         String query = "query { "
-                + "Authors(where: {" + 
-                "    EXISTS: {" + 
-                "      books: {" + 
-                "        title: {LIKE: \"War\"}" + 
-                "      }" + 
-                "    }" + 
+                + "Authors(where: {" +
+                "    EXISTS: {" +
+                "      books: {" +
+                "        title: {LIKE: \"War\"}" +
+                "      }" +
+                "    }" +
                 "  }) {" +
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "      }" + 
-                "    }" + 
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "      }" +
+                "    }" +
                 "  }"+
                 "}";
 
@@ -689,28 +842,28 @@ public void queryForAuthorsWithWhereEXISTSBooksLIKETitle() {
 
         // then
         assertThat(result.toString()).isEqualTo(expected);
-    }        
-    
+    }
+
     @Test
     public void queryForAuthorsWithWhereEXISTSBooksLIKETitleANDAuthorLIKEName() {
         //given
         String query = "query { "
-                + "Authors(where: {" + 
-                "    EXISTS: {" + 
-                "      books: {" + 
-                "        author: {name: {LIKE: \"Leo\"}}" + 
-                "        title: {LIKE: \"War\"}" + 
-                "      }" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "      }" + 
-                "    }" + 
+                + "Authors(where: {" +
+                "    EXISTS: {" +
+                "      books: {" +
+                "        author: {name: {LIKE: \"Leo\"}}" +
+                "        title: {LIKE: \"War\"}" +
+                "      }" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "      }" +
+                "    }" +
                 "  }"+
                 "}";
 
@@ -726,31 +879,31 @@ public void queryForAuthorsWithWhereEXISTSBooksLIKETitleANDAuthorLIKEName() {
 
         // then
         assertThat(result.toString()).isEqualTo(expected);
-    }        
+    }
+
 
-    
     @Test
     public void queryForAuthorsWithWhereEXISTSBooksLIKETitleANDEXISTSAuthorLIKEName() {
         //given
         String query = "query { "
-                + "  Authors(where: {" + 
-                "    EXISTS: {" + 
-                "      books: {" + 
-                "        EXISTS: {" + 
-                "            author: {name: {LIKE: \"Leo\"}}  " + 
-                "        }" + 
-                "        title: {LIKE: \"War\"}" + 
-                "      }" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "      }" + 
-                "    }" + 
+                + "  Authors(where: {" +
+                "    EXISTS: {" +
+                "      books: {" +
+                "        EXISTS: {" +
+                "            author: {name: {LIKE: \"Leo\"}}  " +
+                "        }" +
+                "        title: {LIKE: \"War\"}" +
+                "      }" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "      }" +
+                "    }" +
                 "  }"+
                 "}";
 
@@ -766,28 +919,28 @@ public void queryForAuthorsWithWhereEXISTSBooksLIKETitleANDEXISTSAuthorLIKEName(
 
         // then
         assertThat(result.toString()).isEqualTo(expected);
-    }  
-    
+    }
+
     @Test
     public void queryForAuthorsWithWhereEXISTSBooksLIKETitleEmpty() {
         //given
         String query = "query { "
-                + "Authors(where: {" + 
-                "    EXISTS: {" + 
-                "      books: {" + 
-                "        author: {name: {LIKE: \"Anton\"}}" + 
-                "        title: {LIKE: \"War\"}" + 
-                "      }" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "      }" + 
-                "    }" + 
+                + "Authors(where: {" +
+                "    EXISTS: {" +
+                "      books: {" +
+                "        author: {name: {LIKE: \"Anton\"}}" +
+                "        title: {LIKE: \"War\"}" +
+                "      }" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "      }" +
+                "    }" +
                 "  }"+
                 "}";
 
@@ -799,25 +952,25 @@ public void queryForAuthorsWithWhereEXISTSBooksLIKETitleEmpty() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryForAuthorsWithWhereNOTEXISTSBooksLIKETitleWar() {
         //given
         String query = "query { "
-                + "Authors(where: {" + 
-                "    NOT_EXISTS: {" + 
-                "      books: {" + 
-                "        title: {LIKE: \"War\"}" + 
-                "      }" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "      }" + 
+                + "Authors(where: {" +
+                "    NOT_EXISTS: {" +
+                "      books: {" +
+                "        title: {LIKE: \"War\"}" +
+                "      }" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "      }" +
                 "    }"+
                 "  }"+
                 "}";
@@ -835,29 +988,29 @@ public void queryForAuthorsWithWhereNOTEXISTSBooksLIKETitleWar() {
 
         // then
         assertThat(result.toString()).isEqualTo(expected);
-    }        
+    }
 
     @Test
     public void queryForAuthorsWithWhereBooksNOTEXISTSAuthorLIKENameLeo() {
         //given
         String query = "query { "
-                + "  Authors(where: {" + 
-                "    books: {" + 
-                "      NOT_EXISTS: {" + 
-                "        author: {" + 
-                "          name: {LIKE: \"Leo\"}" + 
-                "        }" + 
-                "      }" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "      }" + 
-                "    }" + 
+                + "  Authors(where: {" +
+                "    books: {" +
+                "      NOT_EXISTS: {" +
+                "        author: {" +
+                "          name: {LIKE: \"Leo\"}" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "      }" +
+                "    }" +
                 "  }"+
                 "}";
 
@@ -955,16 +1108,16 @@ public void queryTotalForAuthorsWithWhereBooksNOTEXISTSAuthorLIKENameLeo() {
     public void queryForAuthorssWithWhereBooksGenreEquals() {
         //given
         String query = "query { "
-                + "Authors(where: {books: {genre: {EQ: NOVEL}}}) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                + "Authors(where: {books: {genre: {EQ: NOVEL}}}) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }"+
                 "}";
 
@@ -980,31 +1133,31 @@ public void queryForAuthorssWithWhereBooksGenreEquals() {
 
         // then
         assertThat(result.toString()).isEqualTo(expected);
-    }        
-    
+    }
+
     @Test
     public void queryForAuthorssWithWhereBooksManyToOneRelationCriteria() {
         //given
         String query = "query { " +
-                "  Authors(where: {" + 
-                "    books: {" + 
-                "      author: {" + 
-                "        name: {LIKE: \"Leo\"}" + 
-                "      }" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "        author {" + 
-                "          name" + 
-                "        }" + 
-                "      }" + 
-                "    }" + 
+                "  Authors(where: {" +
+                "    books: {" +
+                "      author: {" +
+                "        name: {LIKE: \"Leo\"}" +
+                "      }" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "        author {" +
+                "          name" +
+                "        }" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1020,28 +1173,28 @@ public void queryForAuthorssWithWhereBooksManyToOneRelationCriteria() {
 
         // then
         assertThat(result.toString()).isEqualTo(expected);
-    }        
-        
+    }
+
 
     @Test
     public void queryWithWhereInsideOneToManyRelationsImplicitAND() {
         //given:
         String query = "query { "
-                + "Authors(where: {" + 
-                "    books: {" + 
-                "      genre: {IN: NOVEL}" + 
-                "      title: {LIKE: \"War\"}" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                + "Authors(where: {" +
+                "    books: {" +
+                "      genre: {IN: NOVEL}" +
+                "      title: {LIKE: \"War\"}" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1058,29 +1211,29 @@ public void queryWithWhereInsideOneToManyRelationsImplicitAND() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
+    }
 
     @Test
     public void queryWithWhereInsideOneToManyRelationsImplicitANDWithEXISTS() {
         //given:
         String query = "query { "
-                + "Authors(where: {" + 
+                + "Authors(where: {" +
                 "    EXISTS: {" +
                 "      books: {" +
-                "        genre: {IN: NOVEL}" + 
-                "        title: {LIKE: \"War\"}" + 
-                "      }" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                "        genre: {IN: NOVEL}" +
+                "        title: {LIKE: \"War\"}" +
+                "      }" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1098,29 +1251,29 @@ public void queryWithWhereInsideOneToManyRelationsImplicitANDWithEXISTS() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
+    }
 
     @Test
     public void queryWithWhereInsideOneToManyRelationsWithExplictAND() {
         //given:
         String query = "query { "
-                + "Authors(where: {" + 
-                "    books: {" + 
+                + "Authors(where: {" +
+                "    books: {" +
                 "      AND: { "+
-                "        genre: {IN: NOVEL}" + 
+                "        genre: {IN: NOVEL}" +
                 "        title: {LIKE: \"War\"}" +
                 "      }" +
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1135,31 +1288,31 @@ public void queryWithWhereInsideOneToManyRelationsWithExplictAND() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryWithWhereInsideOneToManyRelationsWithExplictANDEXISTS() {
         //given:
         String query = "query { "
                 + "Authors(where: {" +
                 "    EXISTS: {" +
-                "      books: {" + 
+                "      books: {" +
                 "        AND: { "+
-                "          genre: {IN: NOVEL}" + 
+                "          genre: {IN: NOVEL}" +
                 "          title: {LIKE: \"War\"}" +
                 "        }" +
-                "      }" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                "      }" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1174,29 +1327,29 @@ public void queryWithWhereInsideOneToManyRelationsWithExplictANDEXISTS() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryWithWhereInsideOneToManyRelationsWithExplictOR() {
         //given:
         String query = "query { "
-                + "Authors(where: {" + 
-                "    books: {" + 
+                + "Authors(where: {" +
+                "    books: {" +
                 "      OR: { "+
-                "        genre: {IN: NOVEL}" + 
+                "        genre: {IN: NOVEL}" +
                 "        title: {LIKE: \"War\"}" +
                 "      }" +
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1210,32 +1363,32 @@ public void queryWithWhereInsideOneToManyRelationsWithExplictOR() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryWithWhereInsideOneToManyNestedRelationsWithManyToOneAndOR() {
         //given:
         String query = "query { " +
-                "  Authors(where: {" + 
-                "    books: {" + 
+                "  Authors(where: {" +
+                "    books: {" +
                 "      author: {name: {LIKE:\"Leo\"}}" +
                 "      AND: {" +
-                "        OR: {" + 
-                "          id: {EQ: 2}" + 
-                "          title: {LIKE: \"Anna\"}" + 
-                "        }" + 
+                "        OR: {" +
+                "          id: {EQ: 2}" +
+                "          title: {LIKE: \"Anna\"}" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
                 "      }" +
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1251,29 +1404,29 @@ public void queryWithWhereInsideOneToManyNestedRelationsWithManyToOneAndOR() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryWithWhereInsideOneToManyNestedRelationsWithOneToManyDeepSelect() {
         //given:
         String query = "query { " +
-                "  Authors(where: {" + 
-                "    books: {" + 
-                "      author: {name: {LIKE:\"Leo\"}}" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "        author {" + 
-                "          name" + 
-                "        }" + 
-                "      }" + 
-                "    }" + 
+                "  Authors(where: {" +
+                "    books: {" +
+                "      author: {name: {LIKE:\"Leo\"}}" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "        author {" +
+                "          name" +
+                "        }" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1289,34 +1442,34 @@ public void queryWithWhereInsideOneToManyNestedRelationsWithOneToManyDeepSelect(
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
-    
+    }
+
+
     @Test
     public void queryWithWhereInsideManyToOneNestedRelationsWithOnToManyCollectionFilter() {
         //given:
         String query = "query { " +
-                "  Books(where: {" + 
-                "    title:{LIKE: \"War\"}" + 
-                "    author: {" + 
-                "      name:{LIKE: \"Leo\"}" + 
-                "      books: {title: {LIKE: \"Anna\"}}" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      title" + 
-                "      genre" + 
-                "      author {" + 
-                "        id" + 
-                "        name" + 
-                "        books {" + 
-                "          id" + 
-                "          title" + 
-                "          genre" + 
-                "        }" + 
-                "      }" + 
-                "    }" + 
+                "  Books(where: {" +
+                "    title:{LIKE: \"War\"}" +
+                "    author: {" +
+                "      name:{LIKE: \"Leo\"}" +
+                "      books: {title: {LIKE: \"Anna\"}}" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      title" +
+                "      genre" +
+                "      author {" +
+                "        id" +
+                "        name" +
+                "        books {" +
+                "          id" +
+                "          title" +
+                "          genre" +
+                "        }" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1336,35 +1489,35 @@ public void queryWithWhereInsideManyToOneNestedRelationsWithOnToManyCollectionFi
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryWithWhereInsideManyToOneNestedRelationsWithOnToManyCollectionFilterEXISTS() {
         //given:
         String query = "query { " +
-                "  Books(where: {" + 
-                "    title:{LIKE: \"War\"}" + 
+                "  Books(where: {" +
+                "    title:{LIKE: \"War\"}" +
                 "    EXISTS: {" +
-                "      author: {" + 
-                "        name:{LIKE: \"Leo\"}" + 
-                "        books: {title: {LIKE: \"Anna\"}}" + 
-                "      }" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      title" + 
-                "      genre" + 
-                "      author {" + 
-                "        id" + 
-                "        name" + 
-                "        books {" + 
-                "          id" + 
-                "          title" + 
-                "          genre" + 
-                "        }" + 
-                "      }" + 
-                "    }" + 
+                "      author: {" +
+                "        name:{LIKE: \"Leo\"}" +
+                "        books: {title: {LIKE: \"Anna\"}}" +
+                "      }" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      title" +
+                "      genre" +
+                "      author {" +
+                "        id" +
+                "        name" +
+                "        books {" +
+                "          id" +
+                "          title" +
+                "          genre" +
+                "        }" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1385,25 +1538,25 @@ public void queryWithWhereInsideManyToOneNestedRelationsWithOnToManyCollectionFi
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryWithOneToManyNestedRelationsWithManyToOneOptionalTrue() {
         //given:
         String query = "query { " +
-                "  Authors {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "        author(optional: true) {" + 
-                "          id" + 
-                "        }" + 
-                "      }" + 
-                "    }" + 
+                "  Authors {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "        author(optional: true) {" +
+                "          id" +
+                "        }" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1425,25 +1578,25 @@ public void queryWithOneToManyNestedRelationsWithManyToOneOptionalTrue() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
+    }
 
     @Test
     public void queryWithOneToManyNestedRelationsWithManyToOneOptionalFalse() {
         //given:
         String query = "query { " +
-                "  Authors {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "        author(optional: false) {" + 
-                "          id" + 
-                "        }" + 
-                "      }" + 
-                "    }" + 
+                "  Authors {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "        author(optional: false) {" +
+                "          id" +
+                "        }" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1464,8 +1617,8 @@ public void queryWithOneToManyNestedRelationsWithManyToOneOptionalFalse() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void ignoreFilter() {
         //given
@@ -1553,21 +1706,21 @@ public void titleOrder() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryForAuthorsWithDefaultOptionalBooks() {
         //given
         String query = "query { "
-                + "Authors {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                + "Authors {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }"+
                 "}";
 
@@ -1590,21 +1743,21 @@ public void queryForAuthorsWithDefaultOptionalBooks() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryForAuthorsWithExlicitOptionalBooksFalse() {
         //given
         String query = "query { "
-                + "Authors {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books(optional: false) {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                + "Authors {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books(optional: false) {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }"+
                 "}";
 
@@ -1626,21 +1779,21 @@ public void queryForAuthorsWithExlicitOptionalBooksFalse() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
- 
+
     @Test
     public void queryForAuthorsWithExlicitOptionalBooksTrue() {
         //given
         String query = "query { "
-                + "Authors {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books(optional: true) {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
+                + "Authors {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books(optional: true) {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
                 "  }"+
                 "}";
 
@@ -1685,7 +1838,7 @@ public void queryForTransientMethodAnnotatedWithGraphQLIgnoreShouldFail() {
                 .extracting("validationErrorType", "queryPath")
                 .containsOnly(tuple(ValidationErrorType.FieldUndefined, list("Books", "select", "authorName")));
     }
-    
+
     @Test
     public void queryWithEQNotMatchingCase() {
         //given:
@@ -1699,7 +1852,7 @@ public void queryWithEQNotMatchingCase() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithEQMatchingCase() {
         //given:
@@ -1715,7 +1868,7 @@ public void queryWithEQMatchingCase() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithLOWERNotMatchingCase() {
         //given:
@@ -1731,7 +1884,7 @@ public void queryWithLOWERNotMatchingCase() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithLOWERMatchingCase() {
         //given:
@@ -1747,7 +1900,7 @@ public void queryWithLOWERMatchingCase() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithEQCaseInsensitive() {
         //given:
@@ -1793,7 +1946,7 @@ public void queryWithEQCaseSensitiveNotMatching() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithNECaseInsensitive() {
         //given:
@@ -1842,7 +1995,7 @@ public void queryWithNECaseSensitiveNonMatching() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithLIKECaseInsensitive() {
         //given:
@@ -1888,7 +2041,7 @@ public void queryWithLIKECaseSensitiveNonMatching() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithSTARTSCaseInsensitive() {
         //given:
@@ -1934,7 +2087,7 @@ public void queryWithSTARTSCaseSensitiveNonMatching() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithENDSCaseInsensitive() {
         //given:
@@ -1965,7 +2118,7 @@ public void queryWithENDSCaseSensitive() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     public void queryWithENDSCaseSensitiveNonMatching() {
         //given:
         String query = "query { Books ( where: { title: {ENDS : \"peace\"}}) { select { id title} } }";
@@ -1977,32 +2130,32 @@ public void queryWithENDSCaseSensitiveNonMatching() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void shouldNotReturnStaleCacheResultsFromPreviousQueryForCollectionCriteriaExpression() {
         //given:
-        String query = "query ($genre: Genre) {" + 
-                "  Authors(where: { " + 
-                "    books: {" + 
-                "        genre: {EQ: $genre}" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "query ($genre: Genre) {" +
+                "  Authors(where: { " +
+                "    books: {" +
+                "        genre: {EQ: $genre}" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         //when: 1st query
         Object result1 = executor.execute(query, Collections.singletonMap("genre", "PLAY")).getData();
-        
+
         String expected1 = "{Authors={select=["
                 +   "{id=4, name=Anton Chekhov, books=["
                 +       "{id=5, title=The Cherry Orchard, genre=PLAY}, "
@@ -2013,10 +2166,10 @@ public void shouldNotReturnStaleCacheResultsFromPreviousQueryForCollectionCriter
 
         //then:
         assertThat(result1.toString()).isEqualTo(expected1);
-        
+
         //when: 2nd query
         Object result2 = executor.execute(query, Collections.singletonMap("genre", "NOVEL")).getData();
-        
+
         String expected2 = "{Authors={select=["
                 +   "{id=1, name=Leo Tolstoy, books=["
                 +       "{id=2, title=War and Peace, genre=NOVEL}, "
@@ -2027,51 +2180,55 @@ public void shouldNotReturnStaleCacheResultsFromPreviousQueryForCollectionCriter
         //then:
         assertThat(result2.toString()).isEqualTo(expected2);
     }
-    
+
     @Test
     public void shouldNotReturnStaleCacheResultsFromPreviousQueryForEmbeddedCriteriaExpression() {
         //given:
-        String query = "query ($genre: Genre) {" + 
-                "  Authors {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      books(where:{ genre: {EQ: $genre} }) {" + 
-                "        id" + 
-                "        title" + 
-                "        genre" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "query ($genre: Genre) {" +
+                "  Authors {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      books(where:{ genre: {EQ: $genre} }) {" +
+                "        id" +
+                "        title" +
+                "        genre" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         //when: 1st query
         Object result1 = executor.execute(query, Collections.singletonMap("genre", "PLAY")).getData();
-        
+
         String expected1 = "{Authors={select=["
+                +   "{id=1, name=Leo Tolstoy, books=[]}, "
                 +   "{id=4, name=Anton Chekhov, books=["
                 +       "{id=5, title=The Cherry Orchard, genre=PLAY}, "
                 +       "{id=6, title=The Seagull, genre=PLAY}, "
                 +       "{id=7, title=Three Sisters, genre=PLAY}"
-                +   "]}"
+                +   "]}, "
+                + "{id=8, name=Igor Dianov, books=[]}"
                 + "]}}";
 
         //then:
         assertThat(result1.toString()).isEqualTo(expected1);
-        
+
         //when: 2nd query
         Object result2 = executor.execute(query, Collections.singletonMap("genre", "NOVEL")).getData();
-        
+
         String expected2 = "{Authors={select=["
                 +   "{id=1, name=Leo Tolstoy, books=["
                 +       "{id=2, title=War and Peace, genre=NOVEL}, "
                 +       "{id=3, title=Anna Karenina, genre=NOVEL}"
-                +   "]}"
+                +   "]}, "
+                +   "{id=4, name=Anton Chekhov, books=[]}, "
+                +   "{id=8, name=Igor Dianov, books=[]}"
                 + "]}}";
 
         //then:
         assertThat(result2.toString()).isEqualTo(expected2);
-    }      
+    }
 
     @Test
     public void queryWithEnumParameterShouldExecuteWithNoError() {
@@ -2114,13 +2271,13 @@ public void queryWithEnumParameterShouldExecuteWithNoError() {
                               "War and Peace")
                 );
     }
-    
+
     // https://github.com/introproventures/graphql-jpa-query/issues/198
     @Test
     public void queryOptionalElementCollections() {
         //given
         String query = "{ Author(id: 8) { id name phoneNumbers books { id title tags } } }";
-        
+
         String expected = "{Author={id=8, name=Igor Dianov, phoneNumbers=[], books=[]}}";
 
         //when
@@ -2128,19 +2285,19 @@ public void queryOptionalElementCollections() {
 
         // then
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryElementCollectionsWithWhereCriteriaExpression() {
         //given:
-        String query = "query {" + 
-                "  Books(where: {tags: {EQ: \"war\"}}) {" + 
-                "    select {" + 
-                "      id" + 
-                "      title" + 
-                "      tags" + 
-                "    }" + 
-                "  }" + 
+        String query = "query {" +
+                "  Books(where: {tags: {EQ: \"war\"}}) {" +
+                "    select {" +
+                "      id" +
+                "      title" +
+                "      tags" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Books={select=[{id=2, title=War and Peace, tags=[piece, war]}]}}";
@@ -2151,12 +2308,12 @@ public void queryElementCollectionsWithWhereCriteriaExpression() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithNullVarables() {
         //given
         String query = "{ Author(id: 1) { id name } }";
-        
+
         String expected = "{Author={id=1, name=Leo Tolstoy}}";
 
         //when
@@ -2165,5 +2322,5 @@ public void queryWithNullVarables() {
         // then
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
 }
\ No newline at end of file
diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java
index b9dcd9eb3..e9062d7ba 100644
--- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java
+++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLWhereVariableBindingsTests.java
@@ -233,17 +233,14 @@ public void queryWithPropertyWhereVariableBinding() throws IOException {
 		// then
 		then(executionResult.getErrors()).isEmpty();
 		Map<String, Object> result = executionResult.getData();
-		then(result)
-				.extracting("Authors")
-				.flatExtracting("select")
-				.extracting("name")
-				.containsOnly("Leo Tolstoy");
-		then(result)
-				.extracting("Authors")
-				.flatExtracting("select")
-				.flatExtracting("books")
-				.extracting("genre")
-				.containsOnly(NOVEL);
+		
+		String expected = "{Authors={select=["
+		        + "{name=Leo Tolstoy, books=[{genre=NOVEL}, {genre=NOVEL}]}, "
+		        + "{name=Anton Chekhov, books=[]}, "
+		        + "{name=Igor Dianov, books=[]}"
+		        + "]}}";
+		
+		then(result.toString()).isEqualTo(expected);
 	}
 
 	@Test
diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/StarwarsQueryExecutorTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/StarwarsQueryExecutorTests.java
index c59eccde8..f67d6eeb0 100644
--- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/StarwarsQueryExecutorTests.java
+++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/StarwarsQueryExecutorTests.java
@@ -40,6 +40,7 @@
 import com.introproventures.graphql.jpa.query.schema.model.starwars.Droid;
 
 @SpringBootTest
+@Transactional
 public class StarwarsQueryExecutorTests extends AbstractSpringBootTestSupport {
 
     @SpringBootApplication
@@ -83,7 +84,7 @@ public void JPASampleTester() {
         assertThat(result).isNotEmpty();
         assertThat(result).hasSize(13);
     }
-    
+
     @Test
     @Transactional
     public void queryManyToManyTester() {
@@ -105,11 +106,11 @@ public void queryManyToManyTester() {
         assertThat(result.get(0).getName()).isEqualTo("C-3PO");
         assertThat(result.get(0).getFriends()).hasSize(3);
         assertThat(result.get(0).getFriends()).extracting(Character::getName)
-                                              .containsOnly("Han Solo", "Leia Organa", "R2-D2"); 
+                                              .containsOnly("Han Solo", "Leia Organa", "R2-D2");
         assertThat(result.get(0).getFriends()).flatExtracting(Character::getFriendsOf)
                                               .extracting(Character::getName)
                                               .containsOnly("Luke Skywalker");
-        
+
         assertThat(result.get(1).getName()).isEqualTo("R2-D2");
         assertThat(result.get(1).getFriends()).hasSize(2);
         assertThat(result.get(1).getFriends()).extracting(Character::getName)
@@ -171,7 +172,44 @@ public void queryManyToOneJoinByIdWithVariables() {
             put("id", "2001");
         }};
 
-        String expected = "{Humans={select=[{name=Darth Vader, homePlanet=Tatooine, favoriteDroid={name=R2-D2}}]}}";
+        String expected = "{Humans={select=["
+                +   "{name=Luke Skywalker, homePlanet=Tatooine, favoriteDroid=null}, "
+                +   "{name=Darth Vader, homePlanet=Tatooine, favoriteDroid={name=R2-D2}}, "
+                +   "{name=Han Solo, homePlanet=null, favoriteDroid=null}, "
+                +   "{name=Leia Organa, homePlanet=Alderaan, favoriteDroid=null}, "
+                +   "{name=Wilhuff Tarkin, homePlanet=null, favoriteDroid=null}"
+                + "]}}";
+
+        //when:
+        Object result = executor.execute(query,variables).getData();
+
+        //then:
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+    @SuppressWarnings("serial")
+    @Test
+    public void queryManyToOneJoinByIdWithVariablesOptionalFalse() {
+        //given:
+        String query = "query ($id: String!) {" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      homePlanet" +
+                "      favoriteDroid(where: {id: {EQ: $id}}, optional: false) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }" +
+                "}" +
+                "";
+        Map<String, Object> variables = new HashMap<String, Object>() {{
+            put("id", "2001");
+        }};
+
+        String expected = "{Humans={select=["
+                +   "{name=Darth Vader, homePlanet=Tatooine, favoriteDroid={name=R2-D2}}"
+                + "]}}";
 
         //when:
         Object result = executor.execute(query,variables).getData();
@@ -269,7 +307,7 @@ public void queryDeepNesting() {
                 + "{name=Leia Organa, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=C-3PO}, {name=Han Solo}, {name=Luke Skywalker}, {name=R2-D2}]}, "
                 + "{name=Luke Skywalker, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=C-3PO}, {name=Han Solo}, {name=Leia Organa}, {name=R2-D2}]}"
                 + "]}}";
-                
+
         //when:
         Object result = executor.execute(query).getData();
 
@@ -289,7 +327,7 @@ public void queryDeepNestingPlural() {
                 + "{name=Luke Skywalker, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=C-3PO}, {name=Han Solo}, {name=Leia Organa}, {name=R2-D2}]}"
                 + "]"
                 + "}]}}";
-        
+
         //when:
         Object result = executor.execute(query).getData();
 
@@ -340,7 +378,7 @@ public void queryWhereRootPagedWithVariables() {
             put("limit", 2);
         }};
 
-        
+
         String expected = "{Humans={pages=3, total=5, select=[{name=Luke Skywalker}, {name=Darth Vader}]}}";
 
         //when:
@@ -349,7 +387,7 @@ public void queryWhereRootPagedWithVariables() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryPaginationWithoutRecords() {
         //given:
@@ -450,14 +488,52 @@ public void queryByCollectionOfEnumsAtRootLevel() {
     @Test
     public void queryByRestrictingSubObject() {
         //given:
-        String query = "query { Humans { select { name gender(where:{ code: {EQ: \"Male\"}}) { description } } } }";
+        String query = "query {" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      gender(where: {code: {EQ: \"Male\"}}) {" +
+                "        description" +
+                "      }" +
+                "    }" +
+                "  }" +
+                "}";
 
         String expected = "{Humans={select=["
-            + "{name=Luke Skywalker, gender={description=Male}}, "
-            + "{name=Darth Vader, gender={description=Male}}, "
-            + "{name=Han Solo, gender={description=Male}}, "
-            + "{name=Wilhuff Tarkin, gender={description=Male}}"
-            + "]}}";
+                +   "{name=Luke Skywalker, gender={description=Male}}, "
+                +   "{name=Darth Vader, gender={description=Male}}, "
+                +   "{name=Han Solo, gender={description=Male}}, "
+                +   "{name=Leia Organa, gender=null}, "
+                +   "{name=Wilhuff Tarkin, gender={description=Male}}"
+                + "]}}";
+
+        //when:
+        Object result = executor.execute(query).getData();
+
+        //then:
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+    @Test
+    public void queryByRestrictingSubObjectOptionalFalse() {
+        //given:
+        String query = "query {" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      gender(where: {code: {EQ: \"Male\"}}, optional: false) {" +
+                "        description" +
+                "      }" +
+                "    }" +
+                "  }" +
+                "}";
+
+        String expected = "{Humans={select=["
+                +   "{name=Luke Skywalker, gender={description=Male}}, "
+                +   "{name=Darth Vader, gender={description=Male}}, "
+                +   "{name=Han Solo, gender={description=Male}}, "
+                +   "{name=Wilhuff Tarkin, gender={description=Male}}"
+                + "]}}";
 
         //when:
         Object result = executor.execute(query).getData();
@@ -567,14 +643,14 @@ public void queryWithTypenameDeepNesting() {
                 + "{name=Han Solo, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=Leia Organa, __typename=Character}, {name=Luke Skywalker, __typename=Character}, {name=R2-D2, __typename=Character}], __typename=Character}, "
                 + "{name=Leia Organa, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=C-3PO, __typename=Character}, {name=Han Solo, __typename=Character}, {name=Luke Skywalker, __typename=Character}, {name=R2-D2, __typename=Character}], __typename=Character}, "
                 + "{name=Luke Skywalker, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=C-3PO, __typename=Character}, {name=Han Solo, __typename=Character}, {name=Leia Organa, __typename=Character}, {name=R2-D2, __typename=Character}], __typename=Character}], __typename=Droid}}";
-        
+
         //when:
         Object result = executor.execute(query).getData();
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithTypenameSimple() {
         //given:
@@ -590,7 +666,7 @@ public void queryWithTypenameSimple() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryWithStringBetweenPredicate() {
         //given:
@@ -624,24 +700,24 @@ public void queryWithStringNotBetweenPredicate() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
+    }
 
     @Test
     public void queryWithWhereInsideManyToOneRelations() {
         //given:
-        String query = "query {" + 
-                "  Humans(where: {" + 
-                "    favoriteDroid: {appearsIn: {IN: [A_NEW_HOPE]}}" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      favoriteDroid {" + 
-                "        name" + 
-                "        appearsIn" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "query {" +
+                "  Humans(where: {" +
+                "    favoriteDroid: {appearsIn: {IN: [A_NEW_HOPE]}}" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid {" +
+                "        name" +
+                "        appearsIn" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
@@ -654,24 +730,24 @@ public void queryWithWhereInsideManyToOneRelations() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
+    }
 
     @Test
     public void queryWithWhereInsideManyToOneRelationsNotExisting() {
         //given:
-        String query = "query {" + 
-                "  Humans(where: {" + 
-                "    favoriteDroid: {appearsIn: {IN: [PHANTOM_MENACE]}}" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      favoriteDroid {" + 
-                "        name" + 
-                "        appearsIn" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "query {" +
+                "  Humans(where: {" +
+                "    favoriteDroid: {appearsIn: {IN: [PHANTOM_MENACE]}}" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid {" +
+                "        name" +
+                "        appearsIn" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=[]}}";
@@ -681,30 +757,30 @@ public void queryWithWhereInsideManyToOneRelationsNotExisting() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryWithWhereInsideOneToManyRelationsNotExisting() {
         //given:
-        String query = "query {" + 
-                " Humans(where: {" + 
-                "    friends: {appearsIn: {EQ: PHANTOM_MENACE}}" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      favoriteDroid {" + 
-                "        name" + 
-                "        primaryFunction { function }" + 
-                "        appearsIn" + 
-                "      }" + 
-                "      friends {" + 
-                "        id" + 
-                "        name" + 
-                "        appearsIn" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "query {" +
+                " Humans(where: {" +
+                "    friends: {appearsIn: {EQ: PHANTOM_MENACE}}" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid {" +
+                "        name" +
+                "        primaryFunction { function }" +
+                "        appearsIn" +
+                "      }" +
+                "      friends {" +
+                "        id" +
+                "        name" +
+                "        appearsIn" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=[]}}";
@@ -714,40 +790,35 @@ public void queryWithWhereInsideOneToManyRelationsNotExisting() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryWithWhereInsideCompositeRelationsAndCollectionFiltering() {
         //given:
-        String query = "query {" + 
-                "  Characters(where: {" + 
-                "    friends: {appearsIn: {IN: A_NEW_HOPE}}" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      appearsIn" + 
-                "      friends(where: {name: {LIKE: \"Leia\"}}) {" + 
-                "        id" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "query {" +
+                "  Characters(where: {" +
+                "    friends: {appearsIn: {IN: A_NEW_HOPE}}" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      appearsIn" +
+                "      friends(where: {name: {LIKE: \"Leia\"}}) {" +
+                "        id" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Characters={select=["
-                + "{id=1000, name=Luke Skywalker, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=["
-                +   "{id=1003, name=Leia Organa}"
-                + "]}, "
-                + "{id=1002, name=Han Solo, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=["
-                +   "{id=1003, name=Leia Organa}"
-                + "]}, "
-                + "{id=2000, name=C-3PO, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=["
-                +   "{id=1003, name=Leia Organa}"
-                + "]}, "
-                + "{id=2001, name=R2-D2, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=["
-                +   "{id=1003, name=Leia Organa}"
-                + "]}"
+                +   "{id=1000, name=Luke Skywalker, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{id=1003, name=Leia Organa}]}, "
+                +   "{id=1001, name=Darth Vader, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI], friends=[]}, "
+                +   "{id=1002, name=Han Solo, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{id=1003, name=Leia Organa}]}, "
+                +   "{id=1003, name=Leia Organa, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[]}, "
+                +   "{id=1004, name=Wilhuff Tarkin, appearsIn=[A_NEW_HOPE], friends=[]}, "
+                +   "{id=2000, name=C-3PO, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{id=1003, name=Leia Organa}]}, "
+                +   "{id=2001, name=R2-D2, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{id=1003, name=Leia Organa}]}"
                 + "]}}";
 
         //when:
@@ -755,32 +826,32 @@ public void queryWithWhereInsideCompositeRelationsAndCollectionFiltering() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }       
-    
+    }
+
     @Test
     public void queryWithWhereInsideCompositeRelationsAndCollectionFiltering2() {
         //given:
-        String query = "query {" + 
-                "  Humans(where: {" + 
-                "    favoriteDroid: { id: {EQ: \"2000\"}}" + 
-                "    friends: {" + 
-                "      appearsIn: {IN: A_NEW_HOPE}" + 
-                "    }" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      favoriteDroid {" + 
-                "        id" + 
-                "        name" + 
-                "        primaryFunction { function }" + 
-                "      }" + 
-                "      friends(where: {name: {LIKE: \"Leia\"}}) {" + 
-                "        id" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }  " + 
+        String query = "query {" +
+                "  Humans(where: {" +
+                "    favoriteDroid: { id: {EQ: \"2000\"}}" +
+                "    friends: {" +
+                "      appearsIn: {IN: A_NEW_HOPE}" +
+                "    }" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid {" +
+                "        id" +
+                "        name" +
+                "        primaryFunction { function }" +
+                "      }" +
+                "      friends(where: {name: {LIKE: \"Leia\"}}) {" +
+                "        id" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }  " +
                 "}";
 
         String expected = "{Humans={select=["
@@ -792,25 +863,25 @@ public void queryWithWhereInsideCompositeRelationsAndCollectionFiltering2() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }       
-    
-    
+    }
+
+
     @Test
     public void queryWithWhereInsideOneToManyRelations() {
         //given:
         String query = "query { "
-                + " Humans(where: {friends: {appearsIn: {IN: A_NEW_HOPE}} }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      favoriteDroid {" + 
-                "        name" + 
-                "      }" + 
-                "      friends {" + 
-                "        name" + 
-                "        appearsIn" + 
-                "      }" + 
-                "    }" + 
+                + " Humans(where: {friends: {appearsIn: {IN: A_NEW_HOPE}} }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid {" +
+                "        name" +
+                "      }" +
+                "      friends {" +
+                "        name" +
+                "        appearsIn" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -855,21 +926,21 @@ public void queryWithWhereInsideOneToManyRelations() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryHumansWithFavoriteDroidDefaultOptionalTrue() {
         //given:
         String query = "query { "
-                + "Humans {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      homePlanet" + 
-                "      favoriteDroid {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+                + "Humans {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      homePlanet" +
+                "      favoriteDroid {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
@@ -886,21 +957,21 @@ public void queryHumansWithFavoriteDroidDefaultOptionalTrue() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-        
+
     @Test
     public void queryHumansWittFavorideDroidExplicitOptionalFalse() {
         //given:
         String query = "query { "
-                + "Humans {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      homePlanet" + 
-                "      favoriteDroid(optional: false) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+                + "Humans {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      homePlanet" +
+                "      favoriteDroid(optional: false) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
@@ -914,23 +985,23 @@ public void queryHumansWittFavorideDroidExplicitOptionalFalse() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryHumansWittFavorideDroidExplicitOptionalFalseParameterBinding() {
         //given:
         String query = "query($optional: Boolean) { "
-                + "Humans {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      homePlanet" + 
-                "      favoriteDroid(optional: $optional) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+                + "Humans {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      homePlanet" +
+                "      favoriteDroid(optional: $optional) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
-        
+
         Map<String, Object> variables = Collections.singletonMap("optional", false);
 
         String expected = "{Humans={select=["
@@ -943,19 +1014,53 @@ public void queryHumansWittFavorideDroidExplicitOptionalFalseParameterBinding()
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void queryFilterManyToOneEmbdeddedCriteria() {
         //given:
-        String query = "query { Droids { select { name  primaryFunction(where: {function: {EQ:\"Astromech\"}}) { function }}}}";
+        String query = "query {" +
+                "  Droids {" +
+                "    select {" +
+                "      name" +
+                "      primaryFunction(where: {function: {EQ: \"Astromech\"}}) {" +
+                "        function" +
+                "      }" +
+                "    }" +
+                "  }" +
+                "}" +
+                "";
 
-        String expected = "{Droids={" +
-                            "select=[{" +
-                              "name=R2-D2, " +
-                              "primaryFunction={function=Astromech}" +
-                            "}]" + 
-                          "}}";
+        String expected = "{Droids={select=["
+                +   "{name=C-3PO, primaryFunction=null}, "
+                +   "{name=R2-D2, primaryFunction={function=Astromech}}"
+                + "]}}";
+
+        //when:
+        Object result = executor.execute(query).getData();
+
+        //then:
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+    @Test
+    public void queryFilterManyToOneEmbdeddedCriteriaOptionalFalse() {
+        //given:
+        String query = "query {" +
+                "  Droids {" +
+                "    select {" +
+                "      name" +
+                "      primaryFunction(where: {function: {EQ: \"Astromech\"}} optional: false) {" +
+                "        function" +
+                "      }" +
+                "    }" +
+                "  }" +
+                "}" +
+                "";
+
+        String expected = "{Droids={select=["
+                +   "{name=R2-D2, primaryFunction={function=Astromech}}"
+                + "]}}";
 
         //when:
         Object result = executor.execute(query).getData();
@@ -968,13 +1073,13 @@ public void queryFilterManyToOneEmbdeddedCriteria() {
     public void queryFilterManyToOnRelationCriteria() {
         //given:
         String query = "query { " +
-                "    Droids(where: {primaryFunction: { function: {EQ:\"Astromech\"}}}) { " + 
-                "      select {" + 
-                "        name " + 
-                "        primaryFunction {" + 
-                "          function" + 
-                "        }" + 
-                "      }" + 
+                "    Droids(where: {primaryFunction: { function: {EQ:\"Astromech\"}}}) { " +
+                "      select {" +
+                "        name " +
+                "        primaryFunction {" +
+                "          function" +
+                "        }" +
+                "      }" +
                 "    }"
                 + "}";
 
@@ -982,7 +1087,7 @@ public void queryFilterManyToOnRelationCriteria() {
                             "select=[{" +
                               "name=R2-D2, " +
                               "primaryFunction={function=Astromech}" +
-                            "}]" + 
+                            "}]" +
                           "}}";
 
         //when:
@@ -991,7 +1096,7 @@ public void queryFilterManyToOnRelationCriteria() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryFilterNestedManyToOneToDo() {
         //given:
@@ -1011,21 +1116,13 @@ public void queryFilterNestedManyToOneToDo() {
                 "    }" +
                 "}";
 
-        String expected = "{Humans={" +
-                            "select=[" +
-                                "{" +
-                                    "id=1001, " +
-                                    "name=Darth Vader, " +
-                                    "homePlanet=Tatooine, " +
-                                    "favoriteDroid={" +
-                                        "name=R2-D2, " +
-                                        "primaryFunction={" +
-                                            "function=Astromech" +
-                                        "}" +
-                                    "}" +
-                                "}" +
-                            "]" +
-                        "}}";
+        String expected = "{Humans={select=["
+                + "{id=1000, name=Luke Skywalker, homePlanet=Tatooine, favoriteDroid={name=C-3PO, primaryFunction=null}}, "
+                + "{id=1001, name=Darth Vader, homePlanet=Tatooine, favoriteDroid={name=R2-D2, primaryFunction={function=Astromech}}}, "
+                + "{id=1002, name=Han Solo, homePlanet=null, favoriteDroid=null}, "
+                + "{id=1003, name=Leia Organa, homePlanet=Alderaan, favoriteDroid=null}, "
+                + "{id=1004, name=Wilhuff Tarkin, homePlanet=null, favoriteDroid=null}"
+                + "]}}";
 
         //when:
         Object result = executor.execute(query).getData();
@@ -1033,7 +1130,38 @@ public void queryFilterNestedManyToOneToDo() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
+    @Test
+    public void queryFilterNestedManyToOneToDoOptionalFalse() {
+        //given:
+        String query = "query {" +
+                "    Humans {" +
+                "        select {" +
+                "            id" +
+                "            name" +
+                "            homePlanet" +
+                "            favoriteDroid {" +
+                "                name" +
+                "                primaryFunction(where:{function:{EQ:\"Astromech\"}}, optional: false) {" +
+                "                      function" +
+                "                }" +
+                "            }" +
+                "        }" +
+                "    }" +
+                "}";
+
+        String expected = "{Humans={select=["
+                + "{id=1001, name=Darth Vader, homePlanet=Tatooine, favoriteDroid={name=R2-D2, primaryFunction={function=Astromech}}}"
+                + "]}}";
+
+        //when:
+        Object result = executor.execute(query).getData();
+
+        //then:
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+
     @Test
     public void queryFilterNestedManyToOneRelationCriteria() {
         //given:
@@ -1075,29 +1203,29 @@ public void queryFilterNestedManyToOneRelationCriteria() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void queryFilterNestedManyToManyRelationCriteria() {
         //given:
         String query = "query {" +
-                "    Humans(where: {" + 
-                "      friends: { name: { LIKE: \"Leia\" } } " + 
-                "      favoriteDroid: { primaryFunction: { function: { EQ: \"Protocol\" } } }" + 
-                "    }) {" + 
-                "      select {" + 
-                "        id" + 
-                "        name" + 
-                "        homePlanet" + 
-                "        favoriteDroid {" + 
-                "          name" + 
-                "          primaryFunction {" + 
-                "            function" + 
-                "          }" + 
-                "        }" + 
-                "        friends {" + 
-                "          name" + 
-                "        }" + 
-                "      }" + 
+                "    Humans(where: {" +
+                "      friends: { name: { LIKE: \"Leia\" } } " +
+                "      favoriteDroid: { primaryFunction: { function: { EQ: \"Protocol\" } } }" +
+                "    }) {" +
+                "      select {" +
+                "        id" +
+                "        name" +
+                "        homePlanet" +
+                "        favoriteDroid {" +
+                "          name" +
+                "          primaryFunction {" +
+                "            function" +
+                "          }" +
+                "        }" +
+                "        friends {" +
+                "          name" +
+                "        }" +
+                "      }" +
                 "    }  " +
                 "}";
 
@@ -1129,24 +1257,24 @@ public void queryFilterNestedManyToManyRelationCriteriaWithEXISTS() {
         String query = "query {" +
                 "    Humans(where: {" +
                 "      EXISTS: {" +
-                "        friends: { name: { LIKE: \"Leia\" } } " + 
+                "        friends: { name: { LIKE: \"Leia\" } } " +
                 "        favoriteDroid: { primaryFunction: { function: { EQ: \"Protocol\" } } }" +
                 "      }"+
-                "    }) {" + 
-                "      select {" + 
-                "        id" + 
-                "        name" + 
-                "        homePlanet" + 
-                "        favoriteDroid {" + 
-                "          name" + 
-                "          primaryFunction {" + 
-                "            function" + 
-                "          }" + 
-                "        }" + 
-                "        friends {" + 
-                "          name" + 
-                "        }" + 
-                "      }" + 
+                "    }) {" +
+                "      select {" +
+                "        id" +
+                "        name" +
+                "        homePlanet" +
+                "        favoriteDroid {" +
+                "          name" +
+                "          primaryFunction {" +
+                "            function" +
+                "          }" +
+                "        }" +
+                "        friends {" +
+                "          name" +
+                "        }" +
+                "      }" +
                 "    }  " +
                 "}";
 
@@ -1175,26 +1303,26 @@ public void queryFilterNestedManyToManyRelationCriteriaWithEXISTS() {
         assertThat(result.toString()).isEqualTo(expected);
     }
 
-    
+
     @Test
     public void queryWithWhereInsideOneToManyRelationsShouldApplyFilterCriterias() {
         //given:
         String query = "query { "
                 + " Humans(where: {"
                 +      "friends: {appearsIn: {IN: A_NEW_HOPE}} "
-                +      "favoriteDroid: {name: {EQ: \"C-3PO\"}} "  
-                +   "}) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      favoriteDroid {" + 
-                "        name" + 
-                "      }" + 
-                "      friends {" + 
-                "        name" + 
-                "        appearsIn" + 
-                "      }" + 
-                "    }" + 
+                +      "favoriteDroid: {name: {EQ: \"C-3PO\"}} "
+                +   "}) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid {" +
+                "        name" +
+                "      }" +
+                "      friends {" +
+                "        name" +
+                "        appearsIn" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1215,20 +1343,20 @@ public void queryWithWhereInsideOneToManyRelationsShouldApplyFilterCriterias() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-        
+
     @Test
     public void queryWithOneToManyRelationsShouldUseLeftOuterJoin() {
         //given:
         String query = "query { " +
-                "  Humans {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      homePlanet" + 
-                "      favoriteDroid {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
+                "  Humans {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      homePlanet" +
+                "      favoriteDroid {" +
+                "        name" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1246,21 +1374,21 @@ public void queryWithOneToManyRelationsShouldUseLeftOuterJoin() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
-    
+
+
     @Test
     public void queryWithWhereOneToManyRelationsShouldUseLeftOuterJoinAndApplyCriteria() {
         //given:
         String query = "query { " +
-                "  Humans(where: {favoriteDroid: {name: {EQ: \"C-3PO\"}}}) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      homePlanet" + 
-                "      favoriteDroid {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
+                "  Humans(where: {favoriteDroid: {name: {EQ: \"C-3PO\"}}}) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      homePlanet" +
+                "      favoriteDroid {" +
+                "        name" +
+                "      }" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1279,14 +1407,14 @@ public void queryWithWhereOneToManyRelationsShouldUseLeftOuterJoinAndApplyCriter
     public void queryWithNestedWhereSearchCriteriaShouldFetchElementCollectionsAttributes() {
         //given:
         String query = "query { " +
-                "  Characters (where: {" + 
-                "    appearsIn :{IN: THE_FORCE_AWAKENS}" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id, " + 
-                "      name," + 
-                "      appearsIn" + 
-                "    }" + 
+                "  Characters (where: {" +
+                "    appearsIn :{IN: THE_FORCE_AWAKENS}" +
+                "  }) {" +
+                "    select {" +
+                "      id, " +
+                "      name," +
+                "      appearsIn" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1304,25 +1432,25 @@ public void queryWithNestedWhereSearchCriteriaShouldFetchElementCollectionsAttri
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-        
-    
+
+
     @Test
     public void queryWithNestedWhereCompoundSearchCriteriaShouldFetchElementCollectionsAttributes() {
         //given:
         String query = "query { " +
-                "  Humans(where:{" + 
-                "    favoriteDroid: {name: {EQ: \"C-3PO\"}} " + 
-                "    appearsIn: {IN: [THE_FORCE_AWAKENS]}" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "      homePlanet" + 
-                "      favoriteDroid {" + 
-                "        name" + 
-                "      }" + 
-                "      appearsIn" + 
-                "    }" + 
+                "  Humans(where:{" +
+                "    favoriteDroid: {name: {EQ: \"C-3PO\"}} " +
+                "    appearsIn: {IN: [THE_FORCE_AWAKENS]}" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      homePlanet" +
+                "      favoriteDroid {" +
+                "        name" +
+                "      }" +
+                "      appearsIn" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1340,19 +1468,19 @@ public void queryWithNestedWhereCompoundSearchCriteriaShouldFetchElementCollecti
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-     
+
 
     @Test
     public void queryShouldReturnDistictResultsByDefault() {
         //given:
         String query = "query { " +
-                "  Humans (where: { " + 
-                "    appearsIn: {IN: [A_NEW_HOPE, EMPIRE_STRIKES_BACK]}" + 
-                "  }) {" + 
-                "    select {" + 
-                "      id" + 
-                "      name" + 
-                "    }" + 
+                "  Humans (where: { " +
+                "    appearsIn: {IN: [A_NEW_HOPE, EMPIRE_STRIKES_BACK]}" +
+                "  }) {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "    }" +
                 "  }" +
                 "}";
 
@@ -1369,25 +1497,27 @@ public void queryShouldReturnDistictResultsByDefault() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void querySetOfEnumsWithinEmbeddedSelectClauseEQ() {
         //given:
-        String query = "{" + 
-                "  Humans {" + 
-                "    select {" + 
-                "      name" + 
-                "      friends(where: {appearsIn: {EQ: THE_FORCE_AWAKENS}}) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      friends(where: {appearsIn: {EQ: THE_FORCE_AWAKENS}}) {" +
+                "        name(orderBy: ASC)" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
                 + "{name=Luke Skywalker, friends=[{name=C-3PO}, {name=Han Solo}, {name=Leia Organa}, {name=R2-D2}]}, "
+                + "{name=Darth Vader, friends=[]}, "
                 + "{name=Han Solo, friends=[{name=Leia Organa}, {name=Luke Skywalker}, {name=R2-D2}]}, "
-                + "{name=Leia Organa, friends=[{name=C-3PO}, {name=Han Solo}, {name=Luke Skywalker}, {name=R2-D2}]}"
+                + "{name=Leia Organa, friends=[{name=C-3PO}, {name=Han Solo}, {name=Luke Skywalker}, {name=R2-D2}]}, "
+                + "{name=Wilhuff Tarkin, friends=[]}"
                 + "]}}";
 
         //when:
@@ -1395,26 +1525,28 @@ public void querySetOfEnumsWithinEmbeddedSelectClauseEQ() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
+    }
 
     @Test
     public void querySetOfEnumsWithinEmbeddedSelectClauseEQArray() {
         //given:
-        String query = "{" + 
-                "  Humans {" + 
-                "    select {" + 
-                "      name" + 
-                "      friends(where: {appearsIn: {EQ: THE_FORCE_AWAKENS}}) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      friends(where: {appearsIn: {EQ: THE_FORCE_AWAKENS}}) {" +
+                "        name(orderBy: ASC)" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
                 + "{name=Luke Skywalker, friends=[{name=C-3PO}, {name=Han Solo}, {name=Leia Organa}, {name=R2-D2}]}, "
+                + "{name=Darth Vader, friends=[]}, "
                 + "{name=Han Solo, friends=[{name=Leia Organa}, {name=Luke Skywalker}, {name=R2-D2}]}, "
-                + "{name=Leia Organa, friends=[{name=C-3PO}, {name=Han Solo}, {name=Luke Skywalker}, {name=R2-D2}]}"
+                + "{name=Leia Organa, friends=[{name=C-3PO}, {name=Han Solo}, {name=Luke Skywalker}, {name=R2-D2}]}, "
+                + "{name=Wilhuff Tarkin, friends=[]}"
                 + "]}}";
 
         //when:
@@ -1422,26 +1554,28 @@ public void querySetOfEnumsWithinEmbeddedSelectClauseEQArray() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void querySetOfEnumsWithinEmbeddedSelectClauseIN() {
         //given:
-        String query = "{" + 
-                "  Humans {" + 
-                "    select {" + 
-                "      name" + 
-                "      friends(where: {appearsIn: {IN: THE_FORCE_AWAKENS}}) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      friends(where: {appearsIn: {IN: THE_FORCE_AWAKENS}}) {" +
+                "        name(orderBy: ASC)" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
                 + "{name=Luke Skywalker, friends=[{name=C-3PO}, {name=Han Solo}, {name=Leia Organa}, {name=R2-D2}]}, "
+                + "{name=Darth Vader, friends=[]}, "
                 + "{name=Han Solo, friends=[{name=Leia Organa}, {name=Luke Skywalker}, {name=R2-D2}]}, "
-                + "{name=Leia Organa, friends=[{name=C-3PO}, {name=Han Solo}, {name=Luke Skywalker}, {name=R2-D2}]}"
+                + "{name=Leia Organa, friends=[{name=C-3PO}, {name=Han Solo}, {name=Luke Skywalker}, {name=R2-D2}]}, "
+                + "{name=Wilhuff Tarkin, friends=[]}"
                 + "]}}";
 
         //when:
@@ -1450,25 +1584,27 @@ public void querySetOfEnumsWithinEmbeddedSelectClauseIN() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void querySetOfEnumsWithinEmbeddedSelectClauseINArray() {
         //given:
-        String query = "{" + 
-                "  Humans {" + 
-                "    select {" + 
-                "      name" + 
-                "      friends(where: {appearsIn: {IN: [THE_FORCE_AWAKENS]}}) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      friends(where: {appearsIn: {IN: [THE_FORCE_AWAKENS]}}) {" +
+                "        name(orderBy: ASC)" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
                 + "{name=Luke Skywalker, friends=[{name=C-3PO}, {name=Han Solo}, {name=Leia Organa}, {name=R2-D2}]}, "
+                + "{name=Darth Vader, friends=[]}, "
                 + "{name=Han Solo, friends=[{name=Leia Organa}, {name=Luke Skywalker}, {name=R2-D2}]}, "
-                + "{name=Leia Organa, friends=[{name=C-3PO}, {name=Han Solo}, {name=Luke Skywalker}, {name=R2-D2}]}"
+                + "{name=Leia Organa, friends=[{name=C-3PO}, {name=Han Solo}, {name=Luke Skywalker}, {name=R2-D2}]}, "
+                + "{name=Wilhuff Tarkin, friends=[]}"
                 + "]}}";
 
         //when:
@@ -1476,24 +1612,27 @@ public void querySetOfEnumsWithinEmbeddedSelectClauseINArray() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }   
-    
+    }
+
     @Test
     public void querySetOfEnumsWithinEmbeddedSelectClauseNE() {
         //given:
-        String query = "{" + 
-                "  Humans {" + 
-                "    select {" + 
-                "      name" + 
-                "      friends(where: {appearsIn: {NE: THE_FORCE_AWAKENS}}) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      friends(where: {appearsIn: {NE: THE_FORCE_AWAKENS}}) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
+                + "{name=Luke Skywalker, friends=[]}, "
                 + "{name=Darth Vader, friends=[{name=Wilhuff Tarkin}]}, "
+                + "{name=Han Solo, friends=[]}, "
+                + "{name=Leia Organa, friends=[]}, "
                 + "{name=Wilhuff Tarkin, friends=[{name=Darth Vader}]}"
                 + "]}}";
 
@@ -1502,24 +1641,27 @@ public void querySetOfEnumsWithinEmbeddedSelectClauseNE() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }    
-    
+    }
+
     @Test
     public void querySetOfEnumsWithinEmbeddedSelectClauseNEArray() {
         //given:
-        String query = "{" + 
-                "  Humans {" + 
-                "    select {" + 
-                "      name" + 
-                "      friends(where: {appearsIn: {NE: THE_FORCE_AWAKENS}}) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      friends(where: {appearsIn: {NE: THE_FORCE_AWAKENS}}) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
+                + "{name=Luke Skywalker, friends=[]}, "
                 + "{name=Darth Vader, friends=[{name=Wilhuff Tarkin}]}, "
+                + "{name=Han Solo, friends=[]}, "
+                + "{name=Leia Organa, friends=[]}, "
                 + "{name=Wilhuff Tarkin, friends=[{name=Darth Vader}]}"
                 + "]}}";
 
@@ -1529,24 +1671,27 @@ public void querySetOfEnumsWithinEmbeddedSelectClauseNEArray() {
         //then:
         assertThat(result.toString()).isEqualTo(expected);
     }
-    
+
     @Test
     public void querySetOfEnumsWithinEmbeddedSelectClauseNIN() {
-        
-        //given:
-        String query = "{" + 
-                "  Humans {" + 
-                "    select {" + 
-                "      name" + 
-                "      friends(where: {appearsIn: {NIN: THE_FORCE_AWAKENS}}) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+
+        //given:
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      friends(where: {appearsIn: {NIN: THE_FORCE_AWAKENS}}) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
+                + "{name=Luke Skywalker, friends=[]}, "
                 + "{name=Darth Vader, friends=[{name=Wilhuff Tarkin}]}, "
+                + "{name=Han Solo, friends=[]}, "
+                + "{name=Leia Organa, friends=[]}, "
                 + "{name=Wilhuff Tarkin, friends=[{name=Darth Vader}]}"
                 + "]}}";
 
@@ -1555,25 +1700,28 @@ public void querySetOfEnumsWithinEmbeddedSelectClauseNIN() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }       
+    }
 
     @Test
     public void querySetOfEnumsWithinEmbeddedSelectClauseNINArray() {
-        
-        //given:
-        String query = "{" + 
-                "  Humans {" + 
-                "    select {" + 
-                "      name" + 
-                "      friends(where: {appearsIn: {NIN: [THE_FORCE_AWAKENS]}}) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+
+        //given:
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      friends(where: {appearsIn: {NIN: [THE_FORCE_AWAKENS]}}) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
+                + "{name=Luke Skywalker, friends=[]}, "
                 + "{name=Darth Vader, friends=[{name=Wilhuff Tarkin}]}, "
+                + "{name=Han Solo, friends=[]}, "
+                + "{name=Leia Organa, friends=[]}, "
                 + "{name=Wilhuff Tarkin, friends=[{name=Darth Vader}]}"
                 + "]}}";
 
@@ -1582,61 +1730,67 @@ public void querySetOfEnumsWithinEmbeddedSelectClauseNINArray() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }       
+    }
 
     @Test
     public void queryEmbeddedWhereWithPluralAssociations() {
-        
+
         //given:
         String query = "{ "
-                + "Droids {" + 
-                "    select {" + 
-                "      name" + 
-                "      friends(where:{" + 
-                "        NOT_EXISTS:{ friends:{name:{EQ:\"R2-D2\"}}}" + 
-                "      }) {" + 
-                "        name" + 
-                "        friends {" + 
-                "          name" + 
-                "        }" + 
-                "      }" + 
-                "    } " + 
-                "  } " + 
+                + "Droids {" +
+                "    select {" +
+                "      name" +
+                "      friends(where:{" +
+                "        NOT_EXISTS:{ friendsOf: {name:{EQ:\"R2-D2\"}}}" +
+                "      }) {" +
+                "        name" +
+                "        friendsOf {" +
+                "          name" +
+                "        }" +
+                "      }" +
+                "    } " +
+                "  } " +
                 "}";
 
         String expected = "{Droids={select=["
                 + "{name=C-3PO, friends=["
-                +   "{name=R2-D2, friends=["
+                +   "{name=R2-D2, friendsOf=["
+                +       "{name=C-3PO}, "
                 +       "{name=Han Solo}, "
                 +       "{name=Leia Organa}, "
                 +       "{name=Luke Skywalker}"
                 +   "]}"
-                + "]}]}}";
+                + "]}, "
+                + "{name=R2-D2, friends=[]}"
+                + "]}}";
 
         //when:
         Object result = executor.execute(query).getData();
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }       
-    
+    }
+
     @Test
     public void queryEmbeddedWhereWithPluralAssociationsNOT_EXISTS() {
-        
-        //given:
-        String query = "{" + 
-                "  Humans {" + 
-                "    select {" + 
-                "      name" + 
-                "      friends(where: {appearsIn: {NIN: [THE_FORCE_AWAKENS]}}) {" + 
-                "        name" + 
-                "      }" + 
-                "    }" + 
-                "  }" + 
+
+        //given:
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      name" +
+                "      friends(where: {appearsIn: {NIN: [THE_FORCE_AWAKENS]}}) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  }" +
                 "}";
 
         String expected = "{Humans={select=["
+                + "{name=Luke Skywalker, friends=[]}, "
                 + "{name=Darth Vader, friends=[{name=Wilhuff Tarkin}]}, "
+                + "{name=Han Solo, friends=[]}, "
+                + "{name=Leia Organa, friends=[]}, "
                 + "{name=Wilhuff Tarkin, friends=[{name=Darth Vader}]}"
                 + "]}}";
 
@@ -1645,26 +1799,26 @@ public void queryEmbeddedWhereWithPluralAssociationsNOT_EXISTS() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }       
-        
+    }
+
     @Test
     public void queryEmbeddedWhereWithNestedPluralAssociationsNOT_EXISTS() {
-        
-        //given:
-        String query = "{" + 
-                "  Droids {" + 
-                "     select {" + 
-                "      name" + 
-                "      friends(where:{" + 
-                "         NOT_EXISTS: {friends:{name:{EQ:\"Leia Organa\"}}}" + 
-                "      }) {" + 
-                "        name" + 
-                "        friends {" + 
-                "          name" + 
-                "        }" + 
-                "      }" + 
-                "    } " + 
-                "  }" + 
+
+        //given:
+        String query = "{" +
+                "  Droids {" +
+                "     select {" +
+                "      name" +
+                "      friends(where:{" +
+                "         NOT_EXISTS: {friends:{name:{EQ:\"Leia Organa\"}}}" +
+                "      }) {" +
+                "        name" +
+                "        friends {" +
+                "          name" +
+                "        }" +
+                "      }" +
+                "    } " +
+                "  }" +
                 "}";
 
         String expected = "{Droids={select=["
@@ -1689,26 +1843,26 @@ public void queryEmbeddedWhereWithNestedPluralAssociationsNOT_EXISTS() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }      
-    
+    }
+
     @Test
     public void queryEmbeddedWhereWithRecursivePluralAssociationsNOT_EXISTS() {
-        
-        //given:
-        String query = "{" + 
-                "  Droids(where: {" + 
-                "    friends: {NOT_EXISTS: {friends:{name:{EQ:\"Leia Organa\"}}}}" + 
-                "  }) {" + 
-                "     select {" + 
-                "      name" + 
-                "      friends {" + 
-                "        name" + 
-                "        friends {" + 
-                "          name" + 
-                "        }" + 
-                "      }" + 
-                "    } " + 
-                "  }" + 
+
+        //given:
+        String query = "{" +
+                "  Droids(where: {" +
+                "    friends: {NOT_EXISTS: {friends:{name:{EQ:\"Leia Organa\"}}}}" +
+                "  }) {" +
+                "     select {" +
+                "      name" +
+                "      friends {" +
+                "        name" +
+                "        friends {" +
+                "          name" +
+                "        }" +
+                "      }" +
+                "    } " +
+                "  }" +
                 "}";
 
         String expected = "{Droids={select=["
@@ -1733,26 +1887,26 @@ public void queryEmbeddedWhereWithRecursivePluralAssociationsNOT_EXISTS() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }   
-    
+    }
+
     @Test
     public void queryEmbeddedWhereWithManyToManyAssociations() {
-        
-        //given:
-        String query = "{" + 
-                "  Droids(where: {" + 
-                "    friends: {friendsOf:{name:{EQ:\"Leia Organa\"}}}" + 
-                "  }) {" + 
-                "     select {" + 
-                "      name" + 
-                "      friends {" + 
-                "        name" + 
-                "        friendsOf {" + 
-                "          name" + 
-                "        }" + 
-                "      }" + 
-                "    } " + 
-                "  } " + 
+
+        //given:
+        String query = "{" +
+                "  Droids(where: {" +
+                "    friends: {friendsOf:{name:{EQ:\"Leia Organa\"}}}" +
+                "  }) {" +
+                "     select {" +
+                "      name" +
+                "      friends {" +
+                "        name" +
+                "        friendsOf {" +
+                "          name" +
+                "        }" +
+                "      }" +
+                "    } " +
+                "  } " +
                 "}";
 
         String expected = "{Droids={select=["
@@ -1772,34 +1926,35 @@ public void queryEmbeddedWhereWithManyToManyAssociations() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }  
-    
+    }
+
     @Test
     public void queryEmbeddedWhereWithManyToManyAssociationsUsingEXISTS() {
-        
-        //given:
-        String query = "{" + 
-                "  Droids {" + 
-                "     select {" + 
-                "      name" + 
-                "      friends(where: {EXISTS: {friendsOf:{name:{EQ:\"Leia Organa\"}}}}) {" + 
-                "        name" + 
-                "        friendsOf {" + 
-                "          name" + 
-                "        }" + 
-                "      }" + 
-                "    } " + 
-                "  } " + 
+
+        //given:
+        String query = "{" +
+                "  Droids {" +
+                "     select {" +
+                "      name" +
+                "      friends(where: {EXISTS: {friendsOf:{name:{EQ:\"Leia Organa\"}}}}) {" +
+                "        name" +
+                "        friendsOf {" +
+                "          name" +
+                "        }" +
+                "      }" +
+                "    } " +
+                "  } " +
                 "}";
 
         String expected = "{Droids={select=["
                 + "{name=C-3PO, friends=["
-                +   "{name=Han Solo, friendsOf=[{name=C-3PO}, {name=Leia Organa}, {name=Luke Skywalker}, {name=R2-D2}]}, "
                 +   "{name=Luke Skywalker, friendsOf=[{name=C-3PO}, {name=Han Solo}, {name=Leia Organa}, {name=R2-D2}]}, "
+                +   "{name=Han Solo, friendsOf=[{name=C-3PO}, {name=Leia Organa}, {name=Luke Skywalker}, {name=R2-D2}]}, "
                 +   "{name=R2-D2, friendsOf=[{name=C-3PO}, {name=Han Solo}, {name=Leia Organa}, {name=Luke Skywalker}]}"
                 + "]}, "
-                +   "{name=R2-D2, friends=[{name=Han Solo, friendsOf=[{name=C-3PO}, {name=Leia Organa}, {name=Luke Skywalker}, {name=R2-D2}]}, "
-                +   "{name=Luke Skywalker, friendsOf=[{name=C-3PO}, {name=Han Solo}, {name=Leia Organa}, {name=R2-D2}]}"
+                + "{name=R2-D2, friends=["
+                +   "{name=Luke Skywalker, friendsOf=[{name=C-3PO}, {name=Han Solo}, {name=Leia Organa}, {name=R2-D2}]}, "
+                +   "{name=Han Solo, friendsOf=[{name=C-3PO}, {name=Leia Organa}, {name=Luke Skywalker}, {name=R2-D2}]}"
                 + "]}"
                 + "]}}";
 
@@ -1808,5 +1963,159 @@ public void queryEmbeddedWhereWithManyToManyAssociationsUsingEXISTS() {
 
         //then:
         assertThat(result.toString()).isEqualTo(expected);
-    }      
+    }
+
+    @Test
+    public void queryWithInLineExplicitOptionalFalseForSingularAttribute() {
+
+        //given:
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid(optional: false, where:{name:{LIKE:\"R%\"}}) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  } " +
+                "}";
+
+        String expected = "{Humans={select=[{id=1001, name=Darth Vader, favoriteDroid={name=R2-D2}}]}}";
+
+        //when:
+        Object result = executor.execute(query).getData();
+
+        //then:
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+    @Test
+    public void queryWithInLineExplicitOptionalTrueForSingularAttribute() {
+
+        //given:
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid(optional:true, where:{name:{LIKE:\"R%\"}}) {" +
+                "        name" +
+                "      }" +
+                "    }" +
+                "  } " +
+                "}";
+
+        String expected = "{Humans={select=["
+                +   "{id=1000, name=Luke Skywalker, favoriteDroid=null}, "
+                +   "{id=1001, name=Darth Vader, favoriteDroid={name=R2-D2}}, "
+                +   "{id=1002, name=Han Solo, favoriteDroid=null}, "
+                +   "{id=1003, name=Leia Organa, favoriteDroid=null}, "
+                +   "{id=1004, name=Wilhuff Tarkin, favoriteDroid=null}"
+                + "]}}";
+
+        //when:
+        Object result = executor.execute(query).getData();
+
+        //then:
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+    @Test
+    public void queryWithInLineImplictOptionalTrueForSingularAttribute() {
+
+        //given:
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid {" +
+                "        name" +
+                "        primaryFunction {" +
+                "          function" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  } " +
+                "}";
+
+        String expected = "{Humans={select=["
+                + "{id=1000, name=Luke Skywalker, favoriteDroid={name=C-3PO, primaryFunction={function=Protocol}}}, "
+                + "{id=1001, name=Darth Vader, favoriteDroid={name=R2-D2, primaryFunction={function=Astromech}}}, "
+                + "{id=1002, name=Han Solo, favoriteDroid=null}, "
+                + "{id=1003, name=Leia Organa, favoriteDroid=null}, "
+                + "{id=1004, name=Wilhuff Tarkin, favoriteDroid=null}]}}";
+
+        //when:
+        Object result = executor.execute(query).getData();
+
+        //then:
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+    @Test
+    public void queryWithInLineImplictOptionalTrueForSingularAttributeAndWhereSearchCriteria() {
+
+        //given:
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid(where: {primaryFunction: {function: {EQ: \"Protocol\"}}}) {" +
+                "        name" +
+                "        primaryFunction {" +
+                "          function" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  } " +
+                "}";
+
+        String expected = "{Humans={select=["
+                +   "{id=1000, name=Luke Skywalker, favoriteDroid={name=C-3PO, primaryFunction={function=Protocol}}}, "
+                +   "{id=1001, name=Darth Vader, favoriteDroid=null}, "
+                +   "{id=1002, name=Han Solo, favoriteDroid=null}, "
+                +   "{id=1003, name=Leia Organa, favoriteDroid=null}, "
+                +   "{id=1004, name=Wilhuff Tarkin, favoriteDroid=null}"
+                + "]}}";
+
+        //when:
+        Object result = executor.execute(query).getData();
+
+        //then:
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
+    @Test
+    public void queryWithInLineExplicitOptionalFalseForSingularAttributeAndWhereSearchCriteria() {
+
+        //given:
+        String query = "{" +
+                "  Humans {" +
+                "    select {" +
+                "      id" +
+                "      name" +
+                "      favoriteDroid(optional: false, where: {primaryFunction: {function: {EQ: \"Protocol\"}}}) {" +
+                "        name" +
+                "        primaryFunction {" +
+                "          function" +
+                "        }" +
+                "      }" +
+                "    }" +
+                "  } " +
+                "}";
+
+        String expected = "{Humans={select=["
+                +   "{id=1000, name=Luke Skywalker, favoriteDroid={name=C-3PO, primaryFunction={function=Protocol}}}"
+                + "]}}";
+
+        //when:
+        Object result = executor.execute(query).getData();
+
+        //then:
+        assertThat(result.toString()).isEqualTo(expected);
+    }
+
 }