packages feed

graphql-w-persistent 0.1.0.4 → 0.1.0.5

raw patch · 9 files changed

+168/−68 lines, 9 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

ChangeLog.md view
@@ -18,4 +18,8 @@ 
 ## 0.1.0.4 -- 2018-10-19
 
-* Fix type heirarchies and arguments+* Fix type heirarchies and arguments
+
+## 0.1.0.5 -- 2018-10-22
+
+* Add duplicated queries feature
graphql-w-persistent.cabal view
@@ -10,13 +10,13 @@ -- PVP summary:      +-+------- breaking API changes
 --                   | | +----- non-breaking API additions
 --                   | | | +--- code changes with no API change
-version:             0.1.0.4
+version:             0.1.0.5
 
 -- A short (one-line) description of the package.
 synopsis:            Haskell GraphQL query parser-interpreter-data processor.
 
 -- A longer description of the package.
-description:      This is a general purpose Haskell GraphQL query parser and interpreter. It is including data processing to return GraphQL object formats. The query parser and interpreter are universal (on available database query types). It is accepting any string query, and it is returning a list of string database-queries. The data processing unit is designed around the return values from the Yesod and Persistent interface (cast as Text data values from PersistValue). With another server that is same data representation on the returned values from the database, this entire package is applicable. To read more detailed information, you should go to the below module page.
+description:      This is a general purpose Haskell GraphQL query parser and interpreter. It is including data processing to return GraphQL object formats. The query parser and interpreter are universal (on available database query types which is only SQL for now). It is accepting any string query, and it is returning a list of string database-queries. The data processing unit is designed around the return values from the Yesod and Persistent interface (cast as Text data values from PersistValue). With another server that is same data representation on the returned values from the database, this entire package is applicable. To read more detailed information, you should go to the below module page.
 
 -- URL for the project homepage or repository.
 homepage:            https://github.com/jasonsychau/graphql-w-persistent
@@ -64,6 +64,7 @@   other-modules:       Components.DataProcessors.PersistentDataProcessor,
                        Components.ObjectHandlers.ObjectsHandler,
                        Components.ObjectHandlers.ServerObjectValidator,
+                       Components.ObjectHandlers.ServerObjectTrimmer,
                        Components.Parsers.QueryParser,
                        Components.QueryComposers.SQLQueryComposer,
                        Model.ServerObjectTypes,
src/Components/ObjectHandlers/ObjectsHandler.hs view
@@ -56,13 +56,21 @@ getTransformation (ScalarType alias name trans arg) = (trans,arg)
 getObjectName :: NestedObject -> String
 getObjectName (NestedObject alias name sobj ss sf) = name
+getObjectAlias :: NestedObject -> Alias
+getObjectAlias (NestedObject alias name sobj ss sf) = alias
 getServerObject :: NestedObject -> ServerObject
 getServerObject (NestedObject alias name sobj ss sf) = sobj
-getSubFields :: NestedObject -> SubFields
-getSubFields (NestedObject alias name sobj ss sf) = sf
+getObjectSubSelection :: NestedObject -> SubSelection
+getObjectSubSelection (NestedObject alias name sobj ss sf) = ss
 withSubSelection :: NestedObject -> Bool
 withSubSelection (NestedObject alias name sobj ss sf) = (ss/=Nothing)
 getSubSelectionField :: NestedObject -> String
 getSubSelectionField (NestedObject alias name sobj ss sf) = getScalarName $ fromJust ss
 getSubSelectionArgument :: NestedObject -> String
-getSubSelectionArgument (NestedObject alias name sobj ss sf) = getScalarArgument $ fromJust ss+getSubSelectionArgument (NestedObject alias name sobj ss sf) = getScalarArgument $ fromJust ss
+getSubFields :: NestedObject -> SubFields
+getSubFields (NestedObject alias name sobj ss sf) = sf
+isSameObjectReference :: NestedObject -> NestedObject -> Bool
+isSameObjectReference (NestedObject alias1 name1 sobj1 ss1 sfs1) (NestedObject alias2 name2 sobj2 ss2 sfs2) = alias1==alias2&&name1==name2&&sobj1==sobj2
+isSameObjectSubSelection :: NestedObject -> NestedObject -> Bool
+isSameObjectSubSelection (NestedObject alias1 name1 sobj1 ss1 sfs1) (NestedObject alias2 name2 sobj2 ss2 sfs2) = ss1==ss2
+ src/Components/ObjectHandlers/ServerObjectTrimmer.hs view
@@ -0,0 +1,71 @@+module Components.ObjectHandlers.ServerObjectTrimmer (mergeDuplicatedRootObjects) where
+
+import Data.Either
+import qualified Control.Exception as E
+import Model.ServerObjectTypes
+import Model.ServerExceptions
+import Components.ObjectHandlers.ObjectsHandler
+
+
+mergeDuplicatedRootObjects :: [RootObject] -> [RootObject]
+mergeDuplicatedRootObjects [] = []
+-- TODO: replace all check-then-process to single process...
+mergeDuplicatedRootObjects robjs = mergeDuplicatedRootObjectsHelper robjs []
+mergeDuplicatedRootObjectsHelper :: [RootObject] -> [RootObject] -> [RootObject]
+mergeDuplicatedRootObjectsHelper [] rst = rst
+mergeDuplicatedRootObjectsHelper (h:t) rst = mergeDuplicatedRootObjectsHelper differences (rst++[(mergeDuplicates h duplicates)])
+                                           where (duplicates, differences) = separateDuplicatesAndDifferences h t
+-- we want two lists of duplicates and differences
+separateDuplicatesAndDifferences :: RootObject -> [RootObject] -> ([RootObject],[RootObject])
+separateDuplicatesAndDifferences robj [] = ([],[])
+separateDuplicatesAndDifferences robj lst = separateDuplicatesAndDifferencesHelper robj lst [] []
+separateDuplicatesAndDifferencesHelper :: RootObject -> [RootObject] -> [RootObject] -> [RootObject] -> ([RootObject],[RootObject])
+separateDuplicatesAndDifferencesHelper robj [] dup diff = (dup,diff)
+separateDuplicatesAndDifferencesHelper robj (h:t) dup diff
+   | (isSameObjectReference robj h)&&(isSameObjectSubSelection robj h) = separateDuplicatesAndDifferencesHelper robj t (h:dup) diff
+   | (isSameObjectReference robj h) = E.throw DuplicateRootObjectsException
+   | otherwise = separateDuplicatesAndDifferencesHelper robj t dup (h:diff)
+-- merge together valid (same referene and same subselection) RootObjects
+mergeDuplicates :: RootObject -> [RootObject] -> RootObject
+mergeDuplicates robj [] = (mergeSubFields robj)
+mergeDuplicates (NestedObject alias name sobj ss sf1) ((NestedObject _ _ _ _ sf2):t) = mergeDuplicates (NestedObject alias name sobj ss (mergeFields sf1 sf2)) t
+-- merge SubFields in one RootObject
+mergeSubFields :: RootObject -> RootObject
+mergeSubFields (NestedObject alias name sobj ss sfs) = (NestedObject alias name sobj ss (compressSubSelections sfs))
+-- with a list of Fields, we want to merge duplicate Fields
+compressSubSelections :: [Field] -> [Field]
+compressSubSelections [] = []
+compressSubSelections lst = compressSubSelectionsHelper lst []
+-- We want a removed-duplicate set with a list of Fields, and empty list/.
+compressSubSelectionsHelper :: [Field] -> [Field] -> [Field]
+compressSubSelectionsHelper [] rst = rst
+compressSubSelectionsHelper (h:t) rst
+    | (isLeft h) = compressSubSelectionsHelper t (combineScalarTypeWithPresent rst st)
+    | otherwise = compressSubSelectionsHelper t (combineNestedObjectWithPresent rst no)
+  where
+    st = (fromLeft (E.throw InvalidScalarException) h)
+    no = (fromRight (E.throw InvalidObjectException) h)
+-- with two sets of fields, we want a union that is no duplicates
+mergeFields :: [Field] -> [Field] -> [Field]
+mergeFields lst [] = lst
+mergeFields lst (h:t)
+    | (isLeft h)==True = mergeFields (combineScalarTypeWithPresent lst (fromLeft (E.throw InvalidScalarException) h)) t
+    | otherwise = mergeFields (combineNestedObjectWithPresent lst (fromRight (E.throw InvalidObjectException) h)) t
+-- with list of Field and one ScalarType, we want a union set of list and new ScalarType
+combineScalarTypeWithPresent :: [Field] -> ScalarType -> [Field]
+combineScalarTypeWithPresent [] st = (Left st):[]
+combineScalarTypeWithPresent (h:t) st
+    | (isLeft h)&&(fromLeft (E.throw InvalidScalarException) h)==st = h:t
+    | otherwise = h:(combineScalarTypeWithPresent t st)
+-- with list of Field and one NestedObject, we want a union set of list and new NestedObject
+combineNestedObjectWithPresent :: [Field] -> NestedObject -> [Field]
+combineNestedObjectWithPresent [] no = (Right no):[]
+combineNestedObjectWithPresent (h:t) no
+    | (isRight h)&&(isSameObjectReference nobj no)&&(isSameObjectSubSelection nobj no) = (Right $ mergeObjects nobj no):t
+    | (isRight h)&&(isSameObjectReference nobj no) = E.throw DuplicateRootObjectsException
+    | otherwise = h:(combineNestedObjectWithPresent t no)
+  where
+    nobj = (fromRight (E.throw InvalidObjectException) h)
+-- we want one NestedObject that is same reference, same SubSelection and union set subfields with two NestObjects that are same reference (alias, name, and ServerObject) and same SubSelection.
+mergeObjects :: NestedObject -> NestedObject -> NestedObject
+mergeObjects (NestedObject alias1 name1 sobj1 ss1 sfs1) (NestedObject alias2 name2 sobj2 ss2 sfs2) = if (alias1==alias2&&name1==name2&&sobj1==sobj2&&ss1==ss2) then (NestedObject alias1 name1 sobj1 ss1 (mergeFields sfs1 sfs2)) else (E.throw FailedObjectEqualityException)
src/Components/ObjectHandlers/ServerObjectValidator.hs view
@@ -22,7 +22,6 @@ isValidSubField obj (Left sf) sss sos = (isValidServerObjectScalarField obj sname sss)  -- &&(isValidScalarTransformation obj sname trans arg)
   where
     sname = getScalarName sf
-    (trans, arg) = getTransformation sf
 isValidSubField obj sf sss sos = (isValidServerObjectNestedObjectField obj ofname sos)&&(hasValidAttributes nestedObjectField sss sos)
   where
     nestedObjectField = fromRight (E.throw InvalidObjectException) sf
src/Components/Parsers/QueryParser.hs view
@@ -241,8 +241,10 @@ separateRootObjectsHelper :: String -> String -> [(String,[String])] -> [RootObject]
 separateRootObjectsHelper [] _ _ = []
 separateRootObjectsHelper (h:t) acc svrobjs
- | h=='{' = (((createNestedObject (acc++[h]++(extractLevel t)) svrobjs) :: RootObject):separateRootObjectsHelper (removeLevel t) "" svrobjs)
- | otherwise = separateRootObjectsHelper t (acc++[h]) svrobjs
+    | h=='{' = (((createNestedObject (acc++[h]++level) svrobjs) :: RootObject):separateRootObjectsHelper levelTail "" svrobjs)
+    | otherwise = separateRootObjectsHelper t (acc++[h]) svrobjs
+  where
+    (level,levelTail) = splitLevel t "" 0
 -- create root object from block
 -- EFFECTS: passing code block to parseSubFields where block is not including root object opening and closing curly brackets.
 createNestedObject :: String -> [(String,[String])] -> NestedObject
@@ -294,18 +296,20 @@ parseSubFieldsHelper [] acc [] _ = [Left $ createScalarType acc :: Field]
 parseSubFieldsHelper [] acc1 acc2 _ = (Left $ createScalarType (acc2++acc1)):[]
 parseSubFieldsHelper (h:t) acc1 acc2 svrobjs
- | h==':' = parseSubFieldsHelper (removeLeadingSpaces t) (acc2++acc1++[h]) [] svrobjs
- | h==' '&&(length acc1)>0 = parseSubFieldsHelper t [] acc1 svrobjs
- | h==' ' = parseSubFieldsHelper t acc1 acc2 svrobjs
- | h==','&&(length acc1)>0 = (Left $ createScalarType acc1 :: Field):parseSubFieldsHelper t [] [] svrobjs
- | h==','&&(length acc2)>0 = (Left $ createScalarType acc2 :: Field):parseSubFieldsHelper t [] [] svrobjs
- | h=='('&&(length acc1)>0 = parseSubFieldsHelper (removeSubSelection t) (acc1++(getSubSelection t)) [] svrobjs
- | h=='('&&(length acc2)>0 = parseSubFieldsHelper (removeSubSelection t) (acc2++(getSubSelection t)) [] svrobjs
- | h=='{'&&(length acc1)>0 = (Right $ (createNestedObject (acc1++[h]++(extractLevel t)) svrobjs) :: Field):parseSubFieldsHelper (removeLevel t) [] [] svrobjs
- | h=='{'&&(length acc2)>0 = ((Right $ (createNestedObject (acc2++[h]++(extractLevel t))) svrobjs) :: Field):parseSubFieldsHelper (removeLevel t) [] [] svrobjs
- | h=='}' = parseSubFieldsHelper t acc1 acc2 svrobjs
- | (length acc2)>0 = (Left $ createScalarType acc2 :: Field):parseSubFieldsHelper t (acc1++[h]) [] svrobjs
- | otherwise = parseSubFieldsHelper t (acc1++[h]) [] svrobjs
+    | h==':' = parseSubFieldsHelper (removeLeadingSpaces t) (acc2++acc1++[h]) [] svrobjs
+    | h==' '&&(length acc1)>0 = parseSubFieldsHelper t [] acc1 svrobjs
+    | h==' ' = parseSubFieldsHelper t acc1 acc2 svrobjs
+    | h==','&&(length acc1)>0 = (Left $ createScalarType acc1 :: Field):parseSubFieldsHelper t [] [] svrobjs
+    | h==','&&(length acc2)>0 = (Left $ createScalarType acc2 :: Field):parseSubFieldsHelper t [] [] svrobjs
+    | h=='('&&(length acc1)>0 = parseSubFieldsHelper (removeSubSelection t) (acc1++(getSubSelection t)) [] svrobjs
+    | h=='('&&(length acc2)>0 = parseSubFieldsHelper (removeSubSelection t) (acc2++(getSubSelection t)) [] svrobjs
+    | h=='{'&&(length acc1)>0 = (Right $ (createNestedObject (acc1++[h]++level) svrobjs) :: Field):parseSubFieldsHelper levelTail [] [] svrobjs
+    | h=='{'&&(length acc2)>0 = (Right $ (createNestedObject (acc2++[h]++level) svrobjs) :: Field):parseSubFieldsHelper levelTail [] [] svrobjs
+    | h=='}' = parseSubFieldsHelper t acc1 acc2 svrobjs
+    | (length acc2)>0 = (Left $ createScalarType acc2 :: Field):parseSubFieldsHelper t (acc1++[h]) [] svrobjs
+    | otherwise = parseSubFieldsHelper t (acc1++[h]) [] svrobjs
+  where
+    (level,levelTail) = splitLevel t "" 0
 removeLeadingSpaces :: String -> String
 removeLeadingSpaces [] = []
 removeLeadingSpaces (h:t) = if h==' ' then removeLeadingSpaces t else (h:t)
@@ -317,6 +321,14 @@ getSubSelection :: String -> String
 getSubSelection [] = []
 getSubSelection (h:t) = if h==')' then [] else h:getSubSelection t
+-- split level at and without uneven brace.
+splitLevel :: String -> String -> Int -> (String,String)
+splitLevel [] acc _ = (acc,[])
+splitLevel (h:t) acc l
+    | l<0 = (acc,(h:t))
+    | h=='{' = splitLevel t (acc++[h]) (l+1)
+    | h=='}' = splitLevel t (acc++[h]) (l-1)
+    | otherwise = splitLevel t (acc++[h]) l
 -- pull level and leave out closing brace.
 extractLevel :: String -> String
 extractLevel [] = []
@@ -328,17 +340,17 @@  | h=='}'&&l==0 = []
  | h=='}' = '}':extractLevelHelper t (l-1)
  | otherwise = h:extractLevelHelper t l
--- remove level and leave out closing brace
-removeLevel :: String -> String
-removeLevel [] = []
-removeLevel str = removeLevelHelper str 0
-removeLevelHelper :: String -> Int -> String
-removeLevelHelper [] _ = []
-removeLevelHelper (h:t) l
- | l<0 = h:t
- | h=='{' = removeLevelHelper t (l+1)
- | h=='}' = removeLevelHelper t (l-1)
- | otherwise = removeLevelHelper t l
+-- -- remove level and leave out closing brace
+-- removeLevel :: String -> String
+-- removeLevel [] = []
+-- removeLevel str = removeLevelHelper str 0
+-- removeLevelHelper :: String -> Int -> String
+-- removeLevelHelper [] _ = []
+-- removeLevelHelper (h:t) l
+--  | l<0 = h:t
+--  | h=='{' = removeLevelHelper t (l+1)
+--  | h=='}' = removeLevelHelper t (l-1)
+--  | otherwise = removeLevelHelper t l
 removeSpaces :: String -> String
 removeSpaces str = [x | x <- str, x/=' ']
 createScalarType :: String -> ScalarType
src/GraphQL.hs view
@@ -5,7 +5,7 @@ Maintainer  : jasonsychau@live.ca
 Stability   : provisional
 
-<https://graphql.github.io/ Here> is a link to the official documentations. You can learn and get a feel on how can you implement this package which is a GraphQL-to-SQL translator with Persist return value processing to make a GraphQL format return object string.
+<https://graphql.github.io/ Here> is a link to the official documentations. You can learn and get a feel on how can you implement this package which is a GraphQL-to-SQL translator with Persistent package return value processing to make a GraphQL format return object string.
 
 This module is made to enable interpreting your GraphQL queries. The expected query type is a single string to comprise all your GraphQL queries and fragments.
 
@@ -15,15 +15,33 @@ 
 __Instructions to make server schema:__
 
-(1) make a list of all server objects in the heirarchy in separate tuples with a list to all pseudonyms
+(1) make a list of all server objects in the heirarchy in separate tuples with a list to all pseudonyms (that's including the ones that are referred by relationships)
 (2) make a list of all above mentioned server objects in separate tuples with a list to all valid scalar subfields as you read them from your database
-(3) make a list of all above mentioned server objects in separate tuples with a list to all valid nested object subfields as you want them to be named and as they are repeated in the later list
+(3) make a list of all above mentioned server objects in separate tuples with a list to all valid nested object subfields as you want them to be named (also include these in the first list)
 (4) make a list of all above mentioned server objects in separate tuples with a list to all reference database table name(s) as you read them
-(5) make a list of all database table names in separate tuples with another String (with reference to third-mentioned list on relationship-names) and a list to the SQL database tables that are between the two first tables; the list order is...
+(5) make a list of database table names in separate tuples with another database table name and a list to the SQL database tables that are between the two first tables; the list order is...
 identity table name (the first String in this tuple), identity leaving-field name, referring table name (the table that's corresponding to the second String in this tuple), referring table entering-field name, and (if present) 
-three String sequences that is itermediary-table name, intermediary-table entering-column name, and intermediate-table leaving-column name. The intermediary tables are ordered from closest to indentity table to closest to referencing table.
+three String sequences that is itermediary-table name, intermediary-table entering-column name, and intermediary-table leaving-column name. The intermediary tables are ordered from closest to indentity table to closest to referencing table.
 
 These lists are later used as arguments in a query parsing function.
+
+__To implement server type heirarchy:__
+   
+   * add new tuple to first schema argument,
+   * add new tuple to second schema argument (where list is the intersection fields on all children),
+   * add new tuple to third schema argument (where list is the intersection fields on all children),
+   * and add new tuple to fourth schema argument (where list is all children database tables).
+  
+   You should not need more cases within fifth schema argument, but you may find a child database table and and third schema argument list elements pair that is not included.
+   
+   Then, you will need to include this, or error is thrown.
+
+   As a final note, we turn our attention to what is posted on the official GraphQL docs:
+  
+   @"In contrast, GraphQL only returns the data that's explicitly requested, so new capabilities can be added via new types and new fields on those types without creating a breaking change. This has lead to a common practice of always avoiding breaking changes and serving a versionless API."@ - <https://graphql.github.io/learn/best-practices/>
+  
+   With respect to that, you should be sure to be explicit with your here representation on your data schema. You should give as much detail as possible (sorry for the bad pun), and you should make adjustments when you feel to evolve your server with your desired schema.   
+
 -}
 module GraphQL (
     -- * Functions
@@ -103,9 +121,9 @@ 
    Within every tuple,
 
-   the first String is the database table name (with type heirarchies, you make a separate tuple on every referenced database table if the to-from pair is not already mentioned as explicit pairing).
+   the first String is the identity database table name (with type heirarchies, you make a separate tuple on every referenced database table if the to-from pair is not already mentioned as explicit pairing).
 
-   The second String is the nested object subfield name that is given in the third schema argument (there is a tuple for every pairing).
+   The second String is the referencing database name.
 
    The third tuple value is a list that is showing the link between our identity table and referring table. The order is first identity database table name, second identity database leaving field name, 
    third referencing database table name, fourth referencing database table arrival name, and the remainder (if identity table and referencing table are not directly linked) is a triplet of Strings that 
@@ -140,35 +158,14 @@        ("breed","genus",["breed","species_id","genus","id","species","id","genus_id"]),
        ("breed","family",["breed","species_id","family","id","species","id","genus_id","genus","id","family_id"]),
        -- Pet
-       ("pet","owner",["pet","id","person","id","pet_ownership","animal_id","owner_id"]),
+       ("pet","person",["pet","id","person","id","pet_ownership","animal_id","owner_id"]),
        ("pet","breed",["pet","id","breed","id","pet_type","pet_id","breed_id"]),
        ("pet","species",["pet","id","species","id","pet_type","pet_id","breed_id","breed","id","species_id"]),
        ("pet","genus",["pet","id","genus","id","pet_type","pet_id","breed_id","breed","id","genus_id"]),
        ("pet","family",["pet","id","family","id","pet_type","pet_id","breed_id","breed","id","genus_id","genus","id","family_id"])]
    @
 
-
-
-   To implement server type heirarchy:
-   
-   * add new tuple to first schema argument,
-   * add new tuple to second schema argument (where list is the intersection fields on all children),
-   * add new tuple to third schema argument (where list is the intersection fields on all children),
-   * and add new tuple to fourth schema argument (where list is all children database tables).
-  
-   You should not need more cases within fifth schema argument, but you may find a child database table ane and third schema argument list elements pair that is not included.
-   
-   Then, you will need to include this, or error is thrown.
-
-   As a final note, we turn our attention to what is posted on the official GraphQL docs:
-  
-   @"In contrast, GraphQL only returns the data that's explicitly requested, so new capabilities can be added via new types and new fields on those types without creating a breaking change. This has lead to a common practice of always avoiding breaking changes and serving a versionless API."@ - <https://graphql.github.io/learn/best-practices/>
-  
-   With respect to that, you should be sure to be explicit with your here representation on your data schema. You should give as much detail as possisble (sorry for the bad pun), and you should make adjustments when they are needed to evolve your server with your desired schema.   
-
-
-
-   When an error is encountered, an uncaught exception is thrown. Exceptions thrown by the module are:
+   When an error is encountered, an uncaught exception is thrown. Exceptions thrown by this function are:
 
    * SyntaxException (when there is a problem with the given GraphQL query syntax)
    * ParseFragmentException (when there is a problem with a Fragment syntax)
@@ -179,6 +176,8 @@    * NullArgumentException (when a transformation is set but no argument is given though we do not yet support transformations) 
    * CreatingSqlQueryObjectFieldsException (when there are too many nested object subfields than nested objects to hold them or when there is a problem with the aligning of server object relationship argument - that's the fifth schema argument)
    * RelationshipConfigurationException (when the relationship schema fifth arugment is incorrect number of String values or when an unrecognized pairing is found)
+   * FailedObjectEqualityException (when we could not match identical queries)
+   * DuplicateRootObjectsException (when there is an overlapping query)
 -}
 processQueryString :: String                      -- ^ GraphQL query argument as String.
                    -> [(String,[String])]         -- ^ Server object name to list of query reference names.
@@ -187,7 +186,7 @@                    -> [(String,[String])]         -- ^ Server object name to list of database table names (which are exact references to table names).
                    -> [(String,String,[String])]  -- ^ Server object name to list of from-to-and intermediate triplet strings as described above to identify all GraphQL relationships with database sequences.
                    -> ([RootObject],[[String]])   -- ^ The return value is one tuple with server objects and list of grouped sql query strings.
-processQueryString str svrobjs sss sos sodn sor = checkObjects sss sos sodn sor $ flip checkString svrobjs $ QP.processString str
+processQueryString str svrobjs sss sos sodn sor = checkObjectsToSql sss sos sodn sor $ flip checkString svrobjs $ QP.processString str
 
 {- |
     This is the function to call after casting PersistValues to Text from processQueryString.
src/GraphQLHelper.hs view
@@ -4,11 +4,13 @@ import qualified Components.Parsers.QueryParser as QP
 import qualified Components.ObjectHandlers.ServerObjectValidator as SOV
 import qualified Components.QueryComposers.SQLQueryComposer as QC
+import qualified Components.ObjectHandlers.ServerObjectTrimmer as SOT
 import Model.ServerObjectTypes
 import Model.ServerExceptions
 
 
 checkString :: String -> [(String,[String])] -> [RootObject]
 checkString str svrobjs = if (QP.validateQuery str)==True then (QP.parseStringToObjects str svrobjs) else E.throw SyntaxException
-checkObjects :: [(String,[String])] -> [(String,[String])] -> [(String,[String])] -> [(String,String,[String])] -> [RootObject] -> ([RootObject],[[String]])
-checkObjects sss sos sodn sor ros = if (SOV.checkObjectsAttributes ros sss sos)==True then (ros,QC.makeSqlQueries ros sodn sor) else E.throw InvalidObjectSubFieldException
+checkObjectsToSql :: [(String,[String])] -> [(String,[String])] -> [(String,[String])] -> [(String,String,[String])] -> [RootObject] -> ([RootObject],[[String]])
+checkObjectsToSql sss sos sodn sor ros = if (SOV.checkObjectsAttributes ros sss sos)==True then (reducedObjects,QC.makeSqlQueries reducedObjects sodn sor) else (E.throw InvalidObjectSubFieldException)
+                                    where reducedObjects = SOT.mergeDuplicatedRootObjects ros
src/Model/ServerExceptions.hs view
@@ -3,18 +3,22 @@ import Control.Exception
 
 
-data QueryException = SyntaxException | 
+data QueryException = SyntaxException |
+                      VariableException | 
                       ParseFragmentException | 
                       EmptyQueryException | 
                       InvalidObjectException | 
-                      InvalidObjectSubFieldException |
                       InvalidScalarException |
+                      InvalidObjectSubFieldException |
                       NullArgumentException |
                       CreatingSqlQueryObjectFieldsException |
                       EOFDataProcessingException |
                       InvalidArgumentException |
-                      RelationshipConfigurationException
+                      RelationshipConfigurationException |
+                      FailedObjectEqualityException |
+                      DuplicateRootObjectsException 
   deriving Show
+
                       -- InvalidObjectNestedObjectFieldException | 
                       -- InvalidObjectScalarFieldException | 
                       -- InvalidAttributeTransformation |