From e70b0720a05ceeb3d91a232a6fb57580b61d3c6f Mon Sep 17 00:00:00 2001 From: Joris <joris.l@hotmail.com> Date: Wed, 24 Nov 2021 12:10:49 +0100 Subject: [PATCH] temp aql comment --- aql/convertQuery.go | 673 ++++---- aql/convertQueryBenchmark_test.go | 372 ++--- aql/convertQuery_test.go | 2388 ++++++++++++++--------------- cypher/convertQuery.go | 4 + 4 files changed, 1722 insertions(+), 1715 deletions(-) diff --git a/aql/convertQuery.go b/aql/convertQuery.go index c9adaa5..ba5999d 100644 --- a/aql/convertQuery.go +++ b/aql/convertQuery.go @@ -1,338 +1,341 @@ -/* -This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course. -© Copyright Utrecht University (Department of Information and Computing Sciences) -*/ - -package aql - -import ( - "errors" - "fmt" - - "git.science.uu.nl/graphpolaris/query-conversion/entity" -) - -// Version 1.13 - -/* -ConvertQuery converts an IncomingQueryJSON object into AQL - JSONQuery: *entity.IncomingQueryJSON, the query to be converted to AQL - Returns: *string, the AQL query and a possible error -*/ +// /* +// This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course. +// © Copyright Utrecht University (Department of Information and Computing Sciences) +// */ + +// package aql + +// import ( +// "errors" +// "fmt" + +// "git.science.uu.nl/graphpolaris/query-conversion/entity" +// ) + +// // Version 1.13 + +// /* +// ConvertQuery converts an IncomingQueryJSON object into AQL +// JSONQuery: *entity.IncomingQueryJSON, the query to be converted to AQL +// Returns: *string, the AQL query and a possible error +// */ +// func (s *Service) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string, error) { + +// // Check to make sure all indexes exist +// // The largest possible id for an entity +// largestEntityID := len(JSONQuery.Entities) - 1 +// // The largest possible id for a relation +// largestRelationID := len(JSONQuery.Relations) - 1 + +// // Make sure no entity should be returned that is outside the range of that list +// for _, e := range JSONQuery.Return.Entities { +// // If this entity references an entity that is outside the range +// if e > largestEntityID || e < 0 { +// return nil, errors.New("non-existing entity referenced in return") +// } +// } func (s *Service) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string, error) { - - // Check to make sure all indexes exist - // The largest possible id for an entity - largestEntityID := len(JSONQuery.Entities) - 1 - // The largest possible id for a relation - largestRelationID := len(JSONQuery.Relations) - 1 - - // Make sure no entity should be returned that is outside the range of that list - for _, e := range JSONQuery.Return.Entities { - // If this entity references an entity that is outside the range - if e > largestEntityID || e < 0 { - return nil, errors.New("non-existing entity referenced in return") - } - } - - // Make sure that no relation mentions a non-existing entity - for _, r := range JSONQuery.Relations { - if r.EntityFrom > largestEntityID || r.EntityTo > largestEntityID { - return nil, errors.New("non-exisiting entity referenced in relation") - } - } - - // Make sure no non-existing relation is tried to be returned - for _, r := range JSONQuery.Return.Relations { - if r > largestRelationID || r < 0 { - return nil, errors.New("non-existing relation referenced in return") - } - } - - result := createQuery(JSONQuery) - return result, nil + return &"", nil } -/* -createQuery generates a query based on the json file provided - JSONQuery: *entity.IncomingQueryJSON, this is a parsedJSON struct holding all the data needed to form a query, - Return: *string, a string containing the corresponding AQL query and an error -*/ -func createQuery(JSONQuery *entity.IncomingQueryJSON) *string { - // Note: Case #4, where there is an edge only query (without any entity), is not supported by frontend - - // If a modifier is used, disable the limit - if len(JSONQuery.Modifiers) > 0 { - JSONQuery.Limit = -1 - } - - var ( - relationsToReturn []string - nodesToReturn []string - nodeUnion string - relationUnion string - ) - - // Loop over all relations - ret := "" - - // Add a WITH statement for entityTo - includedTypes := make(map[string]bool) - allTypes := make(map[string]bool) - for _, relation := range JSONQuery.Relations { - if relation.EntityFrom >= 0 { - includedTypes[JSONQuery.Entities[relation.EntityFrom].Type] = true - allTypes[JSONQuery.Entities[relation.EntityFrom].Type] = true - - // If the type is in the entityTo it is a valid type but not yet included - if relation.EntityTo >= 0 { - allTypes[JSONQuery.Entities[relation.EntityTo].Type] = true - } - } - if relation.EntityFrom == -1 && relation.EntityTo >= 0 { - includedTypes[JSONQuery.Entities[relation.EntityTo].Type] = true - allTypes[JSONQuery.Entities[relation.EntityTo].Type] = true - } - } - - // Include all types that are not yet included - first := true - for k := range allTypes { - if !includedTypes[k] { - if first { - ret += fmt.Sprintf("WITH %v", k) - first = false - } else { - ret += fmt.Sprintf(", %v", k) - } - } - } - if !first { - ret += "\n" - } - - for i, relation := range JSONQuery.Relations { - - relationName := fmt.Sprintf("r%v", i) - - if relation.EntityFrom >= 0 { - // if there is a from-node - // create the let for this node - fromName := fmt.Sprintf("n%v", relation.EntityFrom) - - ret += *createNodeLet(&JSONQuery.Entities[relation.EntityFrom], &fromName) - - ret += *createRelationLetWithFromEntity(&relation, relationName, &JSONQuery.Entities, JSONQuery.Limit) - } else if relation.EntityTo >= 0 { - // if there is only a to-node - toName := fmt.Sprintf("n%v", relation.EntityTo) - - ret += *createNodeLet(&JSONQuery.Entities[relation.EntityTo], &toName) - - ret += *createRelationLetWithOnlyToEntity(&relation, relationName, &JSONQuery.Entities, JSONQuery.Limit) - // Add this relation to the list - } else { - fmt.Println("Relation-only queries are currently not supported") - continue - } - - // Add this relation to the list - relationsToReturn = append(relationsToReturn, relationName) - } - - // Add node let statements for nodes that are not yet returned - // Create a set from all the entity-from's and entity-to's, to check if they are returned - nodeSet := make(map[int]bool) - for _, relation := range JSONQuery.Relations { - nodeSet[relation.EntityFrom] = true - nodeSet[relation.EntityTo] = true - } - - // Check if the entities to return are already returned - for _, entityIndex := range JSONQuery.Return.Entities { - if !nodeSet[entityIndex] { - // If not, return this node - name := fmt.Sprintf("n%v", entityIndex) - ret += *createNodeLet(&JSONQuery.Entities[entityIndex], &name) - - // Add this node to the list - nodesToReturn = append(nodesToReturn, name) - } - } - - //If there are modifiers within the query, we run a different set of checks which focus on quantifiable aspects - if len(JSONQuery.Modifiers) > 0 { - modifier := JSONQuery.Modifiers[0] - // There is a distinction between (relations and entities) and (relations or entities) - if len(JSONQuery.Return.Relations) > 0 && len(JSONQuery.Return.Entities) > 0 { - - var pathDistinction string // .vertices or .edges - - // Select the correct addition to the return of r0[**] - if modifier.SelectedType == "entity" { - // ASSUMING THERE IS ONLY 1 RELATION - if JSONQuery.Relations[0].EntityFrom == modifier.SelectedTypeID { - // This should always be 0, because that is the start of the path - pathDistinction = ".vertices[0]" - - } else { - // Otherwise take the depth.max -1 to get the last - pathDistinction = fmt.Sprintf(".vertices[%v]", JSONQuery.Relations[0].Depth.Max) - - } - } else { - pathDistinction = ".edges[**]" - } - - // Getting the attribute if there is one - if modifier.AttributeIndex != -1 { - if modifier.SelectedType == "entity" { - pathDistinction += fmt.Sprintf(".%v", JSONQuery.Entities[modifier.SelectedTypeID].Constraints[modifier.AttributeIndex].Attribute) - - } else { - pathDistinction += fmt.Sprintf(".%v", JSONQuery.Relations[modifier.SelectedTypeID].Constraints[modifier.AttributeIndex].Attribute) - - } - } - - // If count is used it has to be replaced with Length + unique else use the modifier type - if modifier.Type == "COUNT" { - ret += fmt.Sprintf("RETURN LENGTH (unique(r0[*]%v))", pathDistinction) - - } else { - ret += fmt.Sprintf("RETURN %v (r0[*]%v)", modifier.Type, pathDistinction) - - } - - } else { - // Check if the modifier is on an attribute - if modifier.AttributeIndex == -1 { - ret += fmt.Sprintf("RETURN LENGTH (n%v)", modifier.SelectedTypeID) - } else { - var attribute string - - // Selecting the right attribute from either the entity constraint or relation constraint - if modifier.SelectedType == "entity" { - attribute = JSONQuery.Entities[modifier.SelectedTypeID].Constraints[modifier.AttributeIndex].Attribute - - } else { - attribute = JSONQuery.Relations[modifier.SelectedTypeID].Constraints[modifier.AttributeIndex].Attribute - - } - - // If count is used it has to be replaced with Length + unique else use the modifier type - if modifier.Type == "COUNT" { - ret += fmt.Sprintf("RETURN LENGTH (unique(n%v[*].%v))", modifier.SelectedTypeID, attribute) - - } else { - ret += fmt.Sprintf("RETURN %v (n%v[*].%v)", modifier.Type, modifier.SelectedTypeID, attribute) - - } - } - } - - } else { - - // Create UNION statements that create unique lists of all the nodes and relations - // Thus removing all duplicates - nodeUnion = "\nLET nodes = first(RETURN UNION_DISTINCT(" - for _, relation := range relationsToReturn { - nodeUnion += fmt.Sprintf("flatten(%v[**].vertices), ", relation) - } - - for _, node := range nodesToReturn { - nodeUnion += fmt.Sprintf("%v,", node) - } - nodeUnion += "[],[]))\n" - - relationUnion = "LET edges = first(RETURN UNION_DISTINCT(" - for _, relation := range relationsToReturn { - relationUnion += fmt.Sprintf("flatten(%v[**].edges), ", relation) - } - relationUnion += "[],[]))\n" - - ret += nodeUnion + relationUnion - ret += "RETURN {\"vertices\":nodes, \"edges\":edges }" - - } - - return &ret -} - -/* -createNodeLet generates a 'LET' statement for a node related query - node: *entity.QueryEntityStruct, node is an entityStruct containing the information of a single nod, - name: *string, is the autogenerated name of the node consisting of "n" + the index of the node, - Return: *string, a string containing a single LET-statement in AQL -*/ -func createNodeLet(node *entity.QueryEntityStruct, name *string) *string { - header := fmt.Sprintf("LET %v = (\n\tFOR x IN %v \n", *name, node.Type) - footer := "\tRETURN x\n)\n" - constraints := *createConstraintStatements(&node.Constraints, "x", false) - - ret := header + constraints + footer - return &ret -} - -/* -createRelationLetWithFromEntity generates a 'LET' statement for relations with an 'EntityFrom' property and optionally an 'EntitiyTo' property - relation: *entity.QueryRekationStruct, relation is a relation struct containing the information of a single relation, - name: string, is the autogenerated name of the node consisting of "r" + the index of the relation, - entities: *[]entity.QueryEntityStrucy, is a list of entityStructs that are needed to form the relation LET-statement, - Return: *string, a string containing a single LET-statement in AQL -*/ -func createRelationLetWithFromEntity(relation *entity.QueryRelationStruct, name string, entities *[]entity.QueryEntityStruct, limit int) *string { - header := fmt.Sprintf("LET %v = (\n\tFOR x IN n%v \n", name, relation.EntityFrom) - forStatement := fmt.Sprintf("\tFOR v, e, p IN %v..%v OUTBOUND x %s \n", relation.Depth.Min, relation.Depth.Max, relation.Type) - - // Guarantees that there is no path returned with a duplicate edge - // This way there are no cycle paths possible, TODO: more research about this needed - optionStmtn := "\tOPTIONS { uniqueEdges: \"path\" }\n" - - vFilterStmnt := "" - if relation.EntityTo != -1 { - // If there is a to-node, generate the filter statement - toConstraints := (*entities)[relation.EntityTo].Constraints - vFilterStmnt += *createConstraintStatements(&toConstraints, "v", false) - } - - relationFilterStmnt := *createConstraintStatements(&relation.Constraints, "p", true) - - // Dont use a limit on quantifing queries - footer := "" - if limit != -1 { - footer += fmt.Sprintf("\tLIMIT %v \n", limit) - } - footer += "RETURN DISTINCT p )\n" - - ret := header + forStatement + optionStmtn + vFilterStmnt + relationFilterStmnt + footer - return &ret -} - -/* -createRelationLetWithOnlyToEntity generates a 'LET' statement for relations with only an 'EntityTo' property - relation: *entity.QueryRelationStruct, relation is a relation struct containing the information of a single relation, - name: string, is the autogenerated name of the node consisting of "r" + the index of the relation, - entities: *[]entity.QueryEntityStruct, is a list of entityStructs that are needed to form the relation LET-statement, - Return: *string, a string containing a single LET-statement in AQL -*/ -func createRelationLetWithOnlyToEntity(relation *entity.QueryRelationStruct, name string, entities *[]entity.QueryEntityStruct, limit int) *string { - header := fmt.Sprintf("LET %v = (\n\tFOR x IN n%v \n", name, relation.EntityTo) - forStatement := fmt.Sprintf("\tFOR v, e, p IN %v..%v INBOUND x %s \n", relation.Depth.Min, relation.Depth.Max, relation.Type) - - // Guarantees that there is no path returned with a duplicate edge - // This way there are no cycle paths possible, TODO: more research about this needed - optionStmtn := "\tOPTIONS { uniqueEdges: \"path\" }\n" - - relationFilterStmnt := *createConstraintStatements(&relation.Constraints, "p", true) - - // Dont use a limit on quantifing queries - footer := "" - if limit != -1 { - footer += fmt.Sprintf("\tLIMIT %v \n", limit) - } - footer += "RETURN DISTINCT p )\n" - - ret := header + forStatement + optionStmtn + relationFilterStmnt + footer - return &ret -} +// // Make sure that no relation mentions a non-existing entity +// for _, r := range JSONQuery.Relations { +// if r.EntityFrom > largestEntityID || r.EntityTo > largestEntityID { +// return nil, errors.New("non-exisiting entity referenced in relation") +// } +// } + +// // Make sure no non-existing relation is tried to be returned +// for _, r := range JSONQuery.Return.Relations { +// if r > largestRelationID || r < 0 { +// return nil, errors.New("non-existing relation referenced in return") +// } +// } + +// result := createQuery(JSONQuery) +// return result, nil +// } + +// /* +// createQuery generates a query based on the json file provided +// JSONQuery: *entity.IncomingQueryJSON, this is a parsedJSON struct holding all the data needed to form a query, +// Return: *string, a string containing the corresponding AQL query and an error +// */ +// func createQuery(JSONQuery *entity.IncomingQueryJSON) *string { +// // Note: Case #4, where there is an edge only query (without any entity), is not supported by frontend + +// // If a modifier is used, disable the limit +// if len(JSONQuery.Modifiers) > 0 { +// JSONQuery.Limit = -1 +// } + +// var ( +// relationsToReturn []string +// nodesToReturn []string +// nodeUnion string +// relationUnion string +// ) + +// // Loop over all relations +// ret := "" + +// // Add a WITH statement for entityTo +// includedTypes := make(map[string]bool) +// allTypes := make(map[string]bool) +// for _, relation := range JSONQuery.Relations { +// if relation.EntityFrom >= 0 { +// includedTypes[JSONQuery.Entities[relation.EntityFrom].Type] = true +// allTypes[JSONQuery.Entities[relation.EntityFrom].Type] = true + +// // If the type is in the entityTo it is a valid type but not yet included +// if relation.EntityTo >= 0 { +// allTypes[JSONQuery.Entities[relation.EntityTo].Type] = true +// } +// } +// if relation.EntityFrom == -1 && relation.EntityTo >= 0 { +// includedTypes[JSONQuery.Entities[relation.EntityTo].Type] = true +// allTypes[JSONQuery.Entities[relation.EntityTo].Type] = true +// } +// } + +// // Include all types that are not yet included +// first := true +// for k := range allTypes { +// if !includedTypes[k] { +// if first { +// ret += fmt.Sprintf("WITH %v", k) +// first = false +// } else { +// ret += fmt.Sprintf(", %v", k) +// } +// } +// } +// if !first { +// ret += "\n" +// } + +// for i, relation := range JSONQuery.Relations { + +// relationName := fmt.Sprintf("r%v", i) + +// if relation.EntityFrom >= 0 { +// // if there is a from-node +// // create the let for this node +// fromName := fmt.Sprintf("n%v", relation.EntityFrom) + +// ret += *createNodeLet(&JSONQuery.Entities[relation.EntityFrom], &fromName) + +// ret += *createRelationLetWithFromEntity(&relation, relationName, &JSONQuery.Entities, JSONQuery.Limit) +// } else if relation.EntityTo >= 0 { +// // if there is only a to-node +// toName := fmt.Sprintf("n%v", relation.EntityTo) + +// ret += *createNodeLet(&JSONQuery.Entities[relation.EntityTo], &toName) + +// ret += *createRelationLetWithOnlyToEntity(&relation, relationName, &JSONQuery.Entities, JSONQuery.Limit) +// // Add this relation to the list +// } else { +// fmt.Println("Relation-only queries are currently not supported") +// continue +// } + +// // Add this relation to the list +// relationsToReturn = append(relationsToReturn, relationName) +// } + +// // Add node let statements for nodes that are not yet returned +// // Create a set from all the entity-from's and entity-to's, to check if they are returned +// nodeSet := make(map[int]bool) +// for _, relation := range JSONQuery.Relations { +// nodeSet[relation.EntityFrom] = true +// nodeSet[relation.EntityTo] = true +// } + +// // Check if the entities to return are already returned +// for _, entityIndex := range JSONQuery.Return.Entities { +// if !nodeSet[entityIndex] { +// // If not, return this node +// name := fmt.Sprintf("n%v", entityIndex) +// ret += *createNodeLet(&JSONQuery.Entities[entityIndex], &name) + +// // Add this node to the list +// nodesToReturn = append(nodesToReturn, name) +// } +// } + +// //If there are modifiers within the query, we run a different set of checks which focus on quantifiable aspects +// if len(JSONQuery.Modifiers) > 0 { +// modifier := JSONQuery.Modifiers[0] +// // There is a distinction between (relations and entities) and (relations or entities) +// if len(JSONQuery.Return.Relations) > 0 && len(JSONQuery.Return.Entities) > 0 { + +// var pathDistinction string // .vertices or .edges + +// // Select the correct addition to the return of r0[**] +// if modifier.SelectedType == "entity" { +// // ASSUMING THERE IS ONLY 1 RELATION +// if JSONQuery.Relations[0].EntityFrom == modifier.SelectedTypeID { +// // This should always be 0, because that is the start of the path +// pathDistinction = ".vertices[0]" + +// } else { +// // Otherwise take the depth.max -1 to get the last +// pathDistinction = fmt.Sprintf(".vertices[%v]", JSONQuery.Relations[0].Depth.Max) + +// } +// } else { +// pathDistinction = ".edges[**]" +// } + +// // Getting the attribute if there is one +// if modifier.AttributeIndex != -1 { +// if modifier.SelectedType == "entity" { +// pathDistinction += fmt.Sprintf(".%v", JSONQuery.Entities[modifier.SelectedTypeID].Constraints[modifier.AttributeIndex].Attribute) + +// } else { +// pathDistinction += fmt.Sprintf(".%v", JSONQuery.Relations[modifier.SelectedTypeID].Constraints[modifier.AttributeIndex].Attribute) + +// } +// } + +// // If count is used it has to be replaced with Length + unique else use the modifier type +// if modifier.Type == "COUNT" { +// ret += fmt.Sprintf("RETURN LENGTH (unique(r0[*]%v))", pathDistinction) + +// } else { +// ret += fmt.Sprintf("RETURN %v (r0[*]%v)", modifier.Type, pathDistinction) + +// } + +// } else { +// // Check if the modifier is on an attribute +// if modifier.AttributeIndex == -1 { +// ret += fmt.Sprintf("RETURN LENGTH (n%v)", modifier.SelectedTypeID) +// } else { +// var attribute string + +// // Selecting the right attribute from either the entity constraint or relation constraint +// if modifier.SelectedType == "entity" { +// attribute = JSONQuery.Entities[modifier.SelectedTypeID].Constraints[modifier.AttributeIndex].Attribute + +// } else { +// attribute = JSONQuery.Relations[modifier.SelectedTypeID].Constraints[modifier.AttributeIndex].Attribute + +// } + +// // If count is used it has to be replaced with Length + unique else use the modifier type +// if modifier.Type == "COUNT" { +// ret += fmt.Sprintf("RETURN LENGTH (unique(n%v[*].%v))", modifier.SelectedTypeID, attribute) + +// } else { +// ret += fmt.Sprintf("RETURN %v (n%v[*].%v)", modifier.Type, modifier.SelectedTypeID, attribute) + +// } +// } +// } + +// } else { + +// // Create UNION statements that create unique lists of all the nodes and relations +// // Thus removing all duplicates +// nodeUnion = "\nLET nodes = first(RETURN UNION_DISTINCT(" +// for _, relation := range relationsToReturn { +// nodeUnion += fmt.Sprintf("flatten(%v[**].vertices), ", relation) +// } + +// for _, node := range nodesToReturn { +// nodeUnion += fmt.Sprintf("%v,", node) +// } +// nodeUnion += "[],[]))\n" + +// relationUnion = "LET edges = first(RETURN UNION_DISTINCT(" +// for _, relation := range relationsToReturn { +// relationUnion += fmt.Sprintf("flatten(%v[**].edges), ", relation) +// } +// relationUnion += "[],[]))\n" + +// ret += nodeUnion + relationUnion +// ret += "RETURN {\"vertices\":nodes, \"edges\":edges }" + +// } + +// return &ret +// } + +// /* +// createNodeLet generates a 'LET' statement for a node related query +// node: *entity.QueryEntityStruct, node is an entityStruct containing the information of a single nod, +// name: *string, is the autogenerated name of the node consisting of "n" + the index of the node, +// Return: *string, a string containing a single LET-statement in AQL +// */ +// func createNodeLet(node *entity.QueryEntityStruct, name *string) *string { +// header := fmt.Sprintf("LET %v = (\n\tFOR x IN %v \n", *name, node.Type) +// footer := "\tRETURN x\n)\n" +// constraints := *createConstraintStatements(&node.Constraints, "x", false) + +// ret := header + constraints + footer +// return &ret +// } + +// /* +// createRelationLetWithFromEntity generates a 'LET' statement for relations with an 'EntityFrom' property and optionally an 'EntitiyTo' property +// relation: *entity.QueryRekationStruct, relation is a relation struct containing the information of a single relation, +// name: string, is the autogenerated name of the node consisting of "r" + the index of the relation, +// entities: *[]entity.QueryEntityStrucy, is a list of entityStructs that are needed to form the relation LET-statement, +// Return: *string, a string containing a single LET-statement in AQL +// */ +// func createRelationLetWithFromEntity(relation *entity.QueryRelationStruct, name string, entities *[]entity.QueryEntityStruct, limit int) *string { +// header := fmt.Sprintf("LET %v = (\n\tFOR x IN n%v \n", name, relation.EntityFrom) +// forStatement := fmt.Sprintf("\tFOR v, e, p IN %v..%v OUTBOUND x %s \n", relation.Depth.Min, relation.Depth.Max, relation.Type) + +// // Guarantees that there is no path returned with a duplicate edge +// // This way there are no cycle paths possible, TODO: more research about this needed +// optionStmtn := "\tOPTIONS { uniqueEdges: \"path\" }\n" + +// vFilterStmnt := "" +// if relation.EntityTo != -1 { +// // If there is a to-node, generate the filter statement +// toConstraints := (*entities)[relation.EntityTo].Constraints +// vFilterStmnt += *createConstraintStatements(&toConstraints, "v", false) +// } + +// relationFilterStmnt := *createConstraintStatements(&relation.Constraints, "p", true) + +// // Dont use a limit on quantifing queries +// footer := "" +// if limit != -1 { +// footer += fmt.Sprintf("\tLIMIT %v \n", limit) +// } +// footer += "RETURN DISTINCT p )\n" + +// ret := header + forStatement + optionStmtn + vFilterStmnt + relationFilterStmnt + footer +// return &ret +// } + +// /* +// createRelationLetWithOnlyToEntity generates a 'LET' statement for relations with only an 'EntityTo' property +// relation: *entity.QueryRelationStruct, relation is a relation struct containing the information of a single relation, +// name: string, is the autogenerated name of the node consisting of "r" + the index of the relation, +// entities: *[]entity.QueryEntityStruct, is a list of entityStructs that are needed to form the relation LET-statement, +// Return: *string, a string containing a single LET-statement in AQL +// */ +// func createRelationLetWithOnlyToEntity(relation *entity.QueryRelationStruct, name string, entities *[]entity.QueryEntityStruct, limit int) *string { +// header := fmt.Sprintf("LET %v = (\n\tFOR x IN n%v \n", name, relation.EntityTo) +// forStatement := fmt.Sprintf("\tFOR v, e, p IN %v..%v INBOUND x %s \n", relation.Depth.Min, relation.Depth.Max, relation.Type) + +// // Guarantees that there is no path returned with a duplicate edge +// // This way there are no cycle paths possible, TODO: more research about this needed +// optionStmtn := "\tOPTIONS { uniqueEdges: \"path\" }\n" + +// relationFilterStmnt := *createConstraintStatements(&relation.Constraints, "p", true) + +// // Dont use a limit on quantifing queries +// footer := "" +// if limit != -1 { +// footer += fmt.Sprintf("\tLIMIT %v \n", limit) +// } +// footer += "RETURN DISTINCT p )\n" + +// ret := header + forStatement + optionStmtn + relationFilterStmnt + footer +// return &ret +// } diff --git a/aql/convertQueryBenchmark_test.go b/aql/convertQueryBenchmark_test.go index 6e87714..9a60725 100644 --- a/aql/convertQueryBenchmark_test.go +++ b/aql/convertQueryBenchmark_test.go @@ -1,186 +1,186 @@ -/* -This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course. -© Copyright Utrecht University (Department of Information and Computing Sciences) -*/ - -package aql - -import ( - "encoding/json" - "testing" - - "git.science.uu.nl/graphpolaris/query-conversion/entity" -) - -/* -Benchmarks converting an empty query - t: *testing.T, makes go recognise this as a test -*/ -func BenchmarkConvertEmptyQuery(b *testing.B) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [], - "relations": [] - }, - "entities": [], - "relations": [], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - service.ConvertQuery(&JSONQuery) - } -} - -/* -Benchmarks converting a one attribute query - t: *testing.T, makes go recognise this as a test -*/ -func BenchmarkConvertOneAttributeQuery(b *testing.B) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - service.ConvertQuery(&JSONQuery) - } -} - -/* -Benchmarks converting a two relation query - t: *testing.T, makes go recognise this as a test -*/ -func BenchmarkConvertTwoRelationQuery(b *testing.B) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0, - 1, - 2 - ], - "relations": [ - 0, - 1 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "New York", - "dataType": "string", - "matchType": "exact" - } - ] - }, - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "string", - "matchType": "exact" - } - ] - }, - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 3 - }, - "entityFrom": 2, - "entityTo": 1, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "int", - "matchType": "EQ" - } - ] - }, - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": -1, - "constraints": [] - } - ], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - service.ConvertQuery(&JSONQuery) - } -} +// /* +// This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course. +// © Copyright Utrecht University (Department of Information and Computing Sciences) +// */ + +// package aql + +// import ( +// "encoding/json" +// "testing" + +// "git.science.uu.nl/graphpolaris/query-conversion/entity" +// ) + +// /* +// Benchmarks converting an empty query +// t: *testing.T, makes go recognise this as a test +// */ +// func BenchmarkConvertEmptyQuery(b *testing.B) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [], +// "relations": [] +// }, +// "entities": [], +// "relations": [], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// b.ResetTimer() + +// for i := 0; i < b.N; i++ { +// service.ConvertQuery(&JSONQuery) +// } +// } + +// /* +// Benchmarks converting a one attribute query +// t: *testing.T, makes go recognise this as a test +// */ +// func BenchmarkConvertOneAttributeQuery(b *testing.B) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ], +// "relations": [] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "state", +// "value": "HI", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// b.ResetTimer() + +// for i := 0; i < b.N; i++ { +// service.ConvertQuery(&JSONQuery) +// } +// } + +// /* +// Benchmarks converting a two relation query +// t: *testing.T, makes go recognise this as a test +// */ +// func BenchmarkConvertTwoRelationQuery(b *testing.B) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0, +// 1, +// 2 +// ], +// "relations": [ +// 0, +// 1 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "New York", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// }, +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "San Francisco", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// }, +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "state", +// "value": "HI", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 3 +// }, +// "entityFrom": 2, +// "entityTo": 1, +// "constraints": [ +// { +// "attribute": "Day", +// "value": "15", +// "dataType": "int", +// "matchType": "EQ" +// } +// ] +// }, +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": 0, +// "entityTo": -1, +// "constraints": [] +// } +// ], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// b.ResetTimer() + +// for i := 0; i < b.N; i++ { +// service.ConvertQuery(&JSONQuery) +// } +// } diff --git a/aql/convertQuery_test.go b/aql/convertQuery_test.go index 25b4358..105f900 100644 --- a/aql/convertQuery_test.go +++ b/aql/convertQuery_test.go @@ -1,1194 +1,1194 @@ -/* -This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course. -© Copyright Utrecht University (Department of Information and Computing Sciences) -*/ - -package aql - -import ( - "encoding/json" - "errors" - "strings" - "testing" - - "git.science.uu.nl/graphpolaris/query-conversion/entity" - "github.com/stretchr/testify/assert" -) - -/* -Tests an empty query - t: *testing.T, makes go recognise this as a test -*/ -func TestEmptyQueryConversion(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [], - "relations": [] - }, - "entities": [], - "relations": [], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := ` -LET nodes = first(RETURN UNION_DISTINCT([],[])) -LET edges = first(RETURN UNION_DISTINCT([],[])) -RETURN {"vertices":nodes, "edges":edges }` - assert.Equal(t, correctConvertedResult, *convertedResult) -} - -/* -Tests multiple entity types - t: *testing.T, makes go recognise this as a test -*/ -func TestMultipleEntityTypes(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "databaseName": "test", - "return": { - "entities": [ - 0, - 1 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "kamerleden", - "constraints": [ - { - "attribute": "partij", - "value": "GL", - "dataType": "text", - "matchType": "exact" - } - ] - }, - { - "type": "partijen", - "constraints": [ - { - "attribute": "zetels", - "value": "6", - "dataType": "number", - "matchType": "GT" - } - ] - } - ], - "relations": [ - { - "type": "lid_van", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": 1, - "constraints": [] - } - ], - "limit": 5000, - "modifiers": [] - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := "WITH partijen\nLET n0 = (\n\tFOR x IN kamerleden \n\tFILTER x.partij == \"GL\" \n\tRETURN x\n)\nLET r0 = (\n\tFOR x IN n0 \n\tFOR v, e, p IN 1..1 OUTBOUND x lid_van \n\tOPTIONS { uniqueEdges: \"path\" }\n\tFILTER v.zetels > 6 \n\tLIMIT 5000 \nRETURN DISTINCT p )\n\nLET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))\nLET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))\nRETURN {\"vertices\":nodes, \"edges\":edges }" - cleanedResult := strings.ReplaceAll(correctConvertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - convertedCleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - convertedCleanedResult = strings.ReplaceAll(convertedCleanedResult, "\t", "") - - assert.Equal(t, convertedCleanedResult, cleanedResult) -} - -/* -Tests a query with one attribute - t: *testing.T, makes go recognise this as a test -*/ -func TestEntityOneAttributeQuery(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))LET edges = first(RETURN UNION_DISTINCT([],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Test a relation with a constraint - t: *testing.T, makes go recognise this as a test -*/ -func TestRelationWithConstraint(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": -1, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "int", - "matchType": "EQ" - } - ] - } - ], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests the count modifier - t: *testing.T, makes go recognise this as a test -*/ -func TestModifierCountEntity(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [], - "limit": 5000, - "modifiers": [ - { - "type": "COUNT", - "selectedType": "entity", - "id": 0, - "attributeIndex": -1 - } - ] - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)RETURN LENGTH (n0)` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests the count modifer with an attribute - t: *testing.T, makes go recognise this as a test -*/ -func TestModifierCountEntityAttribute(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [], - "limit": 5000, - "modifiers": [ - { - "type": "SUM", - "selectedType": "entity", - "id": 0, - "attributeIndex": 0 - } - ] - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)RETURN SUM (n0[*].state)` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests the count modifier on a relation - t: *testing.T, makes go recognise this as a test -*/ -func TestModifierCountRelation(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": -1, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "int", - "matchType": "EQ" - } - ] - } - ], - "limit": 5000, - "modifiers": [ - { - "type": "COUNT", - "selectedType": "relation", - "id": 0, - "attributeIndex": -1 - } - ] - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 RETURN DISTINCT p )RETURN LENGTH (unique(r0[*].edges[**]))` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests the count modifier with an entity swap - t: *testing.T, makes go recognise this as a test -*/ -func TestModifierCountEntitySwap(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "databaseName": "TweedeKamer", - "return": { - "entities": [ - 0, - 1 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "partijen", - "constraints": [] - }, - { - "type": "kamerleden", - "constraints": [] - } - ], - "relations": [ - { - "type": "lid_van", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 1, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000, - "modifiers": [ - { - "type": "COUNT", - "selectedType": "entity", - "selectedTypeId": 1, - "attributeIndex": -1 - } - ] - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `WITH partijenLET n1 = (FOR x IN kamerleden RETURN x)LET r0 = (FOR x IN n1 FOR v, e, p IN 1..1 OUTBOUND x lid_van OPTIONS { uniqueEdges: "path" }RETURN DISTINCT p )RETURN LENGTH (unique(r0[*].vertices[0]))` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests the count modifier on a relation and attribute - t: *testing.T, makes go recognise this as a test -*/ -func TestModifierCountRelationAttribute(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": -1, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "int", - "matchType": "EQ" - } - ] - } - ], - "limit": 5000, - "modifiers": [ - { - "type": "AVG", - "selectedType": "relation", - "id": 0, - "attributeIndex": 0 - } - ] - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 RETURN DISTINCT p )RETURN AVG (r0[*].edges[**].Day)` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests a relation with an in out constraint - t: *testing.T, makes go recognise this as a test -*/ -func TestRelationWithInOutConstraint(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0, - 1 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "string", - "matchType": "exact" - } - ] - }, - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 3 - }, - "entityFrom": 1, - "entityTo": 0, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "int", - "matchType": "EQ" - } - ] - } - ], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n1 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n1 FOR v, e, p IN 1..3 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER v.city == "San Francisco" FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests two relations - t: *testing.T, makes go recognise this as a test -*/ -func TestTwoRelations(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0, - 1, - 2 - ], - "relations": [ - 0, - 1 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "New York", - "dataType": "string", - "matchType": "exact" - } - ] - }, - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "string", - "matchType": "exact" - } - ] - }, - { - "type": "airports", - "constraints": [ - { - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 3 - }, - "entityFrom": 2, - "entityTo": 1, - "constraints": [ - { - "attribute": "Day", - "value": "15", - "dataType": "int", - "matchType": "EQ" - } - ] - }, - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": -1, - "constraints": [] - } - ], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n2 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n2 FOR v, e, p IN 1..3 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER v.city == "San Francisco" FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET n0 = (FOR x IN airports FILTER x.city == "New York" RETURN x)LET r1 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), flatten(r1[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), flatten(r1[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests a relation with only a to node - t: *testing.T, makes go recognise this as a test -*/ -func TestRelationWithOnlyToNode(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": -1, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.city == "San Francisco" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 INBOUND x flights OPTIONS { uniqueEdges: "path" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests too manu return entities - t: *testing.T, makes go recognise this as a test -*/ -func TestTooManyReturnEntities(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0, - 1, - 2 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": -1, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - _, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.Equal(t, errors.New("non-existing entity referenced in return"), err) -} - -/* -Tests too manu return relations - t: *testing.T, makes go recognise this as a test -*/ -func TestTooManyReturnRelations(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0, - 1, - 2 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": -1, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - _, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.Equal(t, errors.New("non-existing relation referenced in return"), err) -} - -/* -Tests negative return entities - t: *testing.T, makes go recognise this as a test -*/ -func TestNegativeReturnEntities(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0, - -1 - ], - "relations": [ - 0, - 1, - 2 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": -1, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - _, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.Equal(t, errors.New("non-existing entity referenced in return"), err) -} - -/* -Tests a query with no relation field - t: *testing.T, makes go recognise this as a test -*/ -func TestNoRelationsField(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.city == "San Francisco" RETURN x)LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))LET edges = first(RETURN UNION_DISTINCT([],[]))RETURN {"vertices":nodes, "edges":edges }` - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests a query with double WITH - t: *testing.T, makes go recognise this as a test -*/ -func TestDoubleWITH(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "databaseName": "test", - "return": { - "entities": [ - 0, - 1, - 2, - 3 - ], - "relations": [ - 0, - 1 - ] - }, - "entities": [ - { - "type": "kamerleden", - "constraints": [] - }, - { - "type": "partijen", - "constraints": [] - } - , - { - "type": "kamerleden", - "constraints": [] - }, - { - "type": "commissies", - "constraints": [] - } - ], - "relations": [ - { - "type": "lid_van", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 0, - "entityTo": 1, - "constraints": [] - }, - { - "type": "onderdeel_van", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": 2, - "entityTo": 3, - "constraints": [] - } - ], - "limit": 5000, - "modifiers": [] - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) - - // Assert that the result and the expected result are the same - correctConvertedResult := "WITH partijen, commissiesLET n0 = (FOR x IN kamerleden RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x lid_van OPTIONS { uniqueEdges: \"path\" }LIMIT 5000 RETURN DISTINCT p )LET n2 = (FOR x IN kamerleden RETURN x)LET r1 = (FOR x IN n2 FOR v, e, p IN 1..1 OUTBOUND x onderdeel_van OPTIONS { uniqueEdges: \"path\" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), flatten(r1[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), flatten(r1[**].edges), [],[]))RETURN {\"vertices\":nodes, \"edges\":edges }" - cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") - cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") - assert.Equal(t, correctConvertedResult, cleanedResult) -} - -/* -Tests an entity with a lower than -1 in a relation - t: *testing.T, makes go recognise this as a test -*/ -func TestEntityFromLowerThanNegativeOneInRelation(t *testing.T) { - // Setup for test - // Create query conversion service - service := NewService() - - query := []byte(`{ - "return": { - "entities": [ - 0 - ], - "relations": [ - 0 - ] - }, - "entities": [ - { - "type": "airports", - "constraints": [ - { - "attribute": "city", - "value": "San Francisco", - "dataType": "string", - "matchType": "exact" - } - ] - } - ], - "relations": [ - { - "type": "flights", - "depth": { - "min": 1, - "max": 1 - }, - "entityFrom": -4, - "entityTo": 0, - "constraints": [] - } - ], - "limit": 5000 - }`) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - - _, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) -} +// /* +// This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course. +// © Copyright Utrecht University (Department of Information and Computing Sciences) +// */ + +// package aql + +// import ( +// "encoding/json" +// "errors" +// "strings" +// "testing" + +// "git.science.uu.nl/graphpolaris/query-conversion/entity" +// "github.com/stretchr/testify/assert" +// ) + +// /* +// Tests an empty query +// t: *testing.T, makes go recognise this as a test +// */ +// func TestEmptyQueryConversion(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [], +// "relations": [] +// }, +// "entities": [], +// "relations": [], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := ` +// LET nodes = first(RETURN UNION_DISTINCT([],[])) +// LET edges = first(RETURN UNION_DISTINCT([],[])) +// RETURN {"vertices":nodes, "edges":edges }` +// assert.Equal(t, correctConvertedResult, *convertedResult) +// } + +// /* +// Tests multiple entity types +// t: *testing.T, makes go recognise this as a test +// */ +// func TestMultipleEntityTypes(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "databaseName": "test", +// "return": { +// "entities": [ +// 0, +// 1 +// ], +// "relations": [ +// 0 +// ] +// }, +// "entities": [ +// { +// "type": "kamerleden", +// "constraints": [ +// { +// "attribute": "partij", +// "value": "GL", +// "dataType": "text", +// "matchType": "exact" +// } +// ] +// }, +// { +// "type": "partijen", +// "constraints": [ +// { +// "attribute": "zetels", +// "value": "6", +// "dataType": "number", +// "matchType": "GT" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "lid_van", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": 0, +// "entityTo": 1, +// "constraints": [] +// } +// ], +// "limit": 5000, +// "modifiers": [] +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := "WITH partijen\nLET n0 = (\n\tFOR x IN kamerleden \n\tFILTER x.partij == \"GL\" \n\tRETURN x\n)\nLET r0 = (\n\tFOR x IN n0 \n\tFOR v, e, p IN 1..1 OUTBOUND x lid_van \n\tOPTIONS { uniqueEdges: \"path\" }\n\tFILTER v.zetels > 6 \n\tLIMIT 5000 \nRETURN DISTINCT p )\n\nLET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))\nLET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))\nRETURN {\"vertices\":nodes, \"edges\":edges }" +// cleanedResult := strings.ReplaceAll(correctConvertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// convertedCleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// convertedCleanedResult = strings.ReplaceAll(convertedCleanedResult, "\t", "") + +// assert.Equal(t, convertedCleanedResult, cleanedResult) +// } + +// /* +// Tests a query with one attribute +// t: *testing.T, makes go recognise this as a test +// */ +// func TestEntityOneAttributeQuery(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ], +// "relations": [] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "state", +// "value": "HI", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))LET edges = first(RETURN UNION_DISTINCT([],[]))RETURN {"vertices":nodes, "edges":edges }` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Test a relation with a constraint +// t: *testing.T, makes go recognise this as a test +// */ +// func TestRelationWithConstraint(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ], +// "relations": [ +// 0 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "state", +// "value": "HI", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": 0, +// "entityTo": -1, +// "constraints": [ +// { +// "attribute": "Day", +// "value": "15", +// "dataType": "int", +// "matchType": "EQ" +// } +// ] +// } +// ], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests the count modifier +// t: *testing.T, makes go recognise this as a test +// */ +// func TestModifierCountEntity(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ], +// "relations": [] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "state", +// "value": "HI", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [], +// "limit": 5000, +// "modifiers": [ +// { +// "type": "COUNT", +// "selectedType": "entity", +// "id": 0, +// "attributeIndex": -1 +// } +// ] +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)RETURN LENGTH (n0)` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests the count modifer with an attribute +// t: *testing.T, makes go recognise this as a test +// */ +// func TestModifierCountEntityAttribute(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ], +// "relations": [] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "state", +// "value": "HI", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [], +// "limit": 5000, +// "modifiers": [ +// { +// "type": "SUM", +// "selectedType": "entity", +// "id": 0, +// "attributeIndex": 0 +// } +// ] +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)RETURN SUM (n0[*].state)` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests the count modifier on a relation +// t: *testing.T, makes go recognise this as a test +// */ +// func TestModifierCountRelation(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ], +// "relations": [ +// 0 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "state", +// "value": "HI", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": 0, +// "entityTo": -1, +// "constraints": [ +// { +// "attribute": "Day", +// "value": "15", +// "dataType": "int", +// "matchType": "EQ" +// } +// ] +// } +// ], +// "limit": 5000, +// "modifiers": [ +// { +// "type": "COUNT", +// "selectedType": "relation", +// "id": 0, +// "attributeIndex": -1 +// } +// ] +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 RETURN DISTINCT p )RETURN LENGTH (unique(r0[*].edges[**]))` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests the count modifier with an entity swap +// t: *testing.T, makes go recognise this as a test +// */ +// func TestModifierCountEntitySwap(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "databaseName": "TweedeKamer", +// "return": { +// "entities": [ +// 0, +// 1 +// ], +// "relations": [ +// 0 +// ] +// }, +// "entities": [ +// { +// "type": "partijen", +// "constraints": [] +// }, +// { +// "type": "kamerleden", +// "constraints": [] +// } +// ], +// "relations": [ +// { +// "type": "lid_van", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": 1, +// "entityTo": 0, +// "constraints": [] +// } +// ], +// "limit": 5000, +// "modifiers": [ +// { +// "type": "COUNT", +// "selectedType": "entity", +// "selectedTypeId": 1, +// "attributeIndex": -1 +// } +// ] +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `WITH partijenLET n1 = (FOR x IN kamerleden RETURN x)LET r0 = (FOR x IN n1 FOR v, e, p IN 1..1 OUTBOUND x lid_van OPTIONS { uniqueEdges: "path" }RETURN DISTINCT p )RETURN LENGTH (unique(r0[*].vertices[0]))` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests the count modifier on a relation and attribute +// t: *testing.T, makes go recognise this as a test +// */ +// func TestModifierCountRelationAttribute(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ], +// "relations": [ +// 0 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "state", +// "value": "HI", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": 0, +// "entityTo": -1, +// "constraints": [ +// { +// "attribute": "Day", +// "value": "15", +// "dataType": "int", +// "matchType": "EQ" +// } +// ] +// } +// ], +// "limit": 5000, +// "modifiers": [ +// { +// "type": "AVG", +// "selectedType": "relation", +// "id": 0, +// "attributeIndex": 0 +// } +// ] +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 RETURN DISTINCT p )RETURN AVG (r0[*].edges[**].Day)` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests a relation with an in out constraint +// t: *testing.T, makes go recognise this as a test +// */ +// func TestRelationWithInOutConstraint(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0, +// 1 +// ], +// "relations": [ +// 0 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "San Francisco", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// }, +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "state", +// "value": "HI", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 3 +// }, +// "entityFrom": 1, +// "entityTo": 0, +// "constraints": [ +// { +// "attribute": "Day", +// "value": "15", +// "dataType": "int", +// "matchType": "EQ" +// } +// ] +// } +// ], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `LET n1 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n1 FOR v, e, p IN 1..3 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER v.city == "San Francisco" FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests two relations +// t: *testing.T, makes go recognise this as a test +// */ +// func TestTwoRelations(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0, +// 1, +// 2 +// ], +// "relations": [ +// 0, +// 1 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "New York", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// }, +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "San Francisco", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// }, +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "state", +// "value": "HI", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 3 +// }, +// "entityFrom": 2, +// "entityTo": 1, +// "constraints": [ +// { +// "attribute": "Day", +// "value": "15", +// "dataType": "int", +// "matchType": "EQ" +// } +// ] +// }, +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": 0, +// "entityTo": -1, +// "constraints": [] +// } +// ], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `LET n2 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n2 FOR v, e, p IN 1..3 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER v.city == "San Francisco" FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET n0 = (FOR x IN airports FILTER x.city == "New York" RETURN x)LET r1 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), flatten(r1[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), flatten(r1[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests a relation with only a to node +// t: *testing.T, makes go recognise this as a test +// */ +// func TestRelationWithOnlyToNode(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ], +// "relations": [ +// 0 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "San Francisco", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": -1, +// "entityTo": 0, +// "constraints": [] +// } +// ], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.city == "San Francisco" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 INBOUND x flights OPTIONS { uniqueEdges: "path" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests too manu return entities +// t: *testing.T, makes go recognise this as a test +// */ +// func TestTooManyReturnEntities(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0, +// 1, +// 2 +// ], +// "relations": [ +// 0 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "San Francisco", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": -1, +// "entityTo": 0, +// "constraints": [] +// } +// ], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// _, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.Equal(t, errors.New("non-existing entity referenced in return"), err) +// } + +// /* +// Tests too manu return relations +// t: *testing.T, makes go recognise this as a test +// */ +// func TestTooManyReturnRelations(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ], +// "relations": [ +// 0, +// 1, +// 2 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "San Francisco", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": -1, +// "entityTo": 0, +// "constraints": [] +// } +// ], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// _, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.Equal(t, errors.New("non-existing relation referenced in return"), err) +// } + +// /* +// Tests negative return entities +// t: *testing.T, makes go recognise this as a test +// */ +// func TestNegativeReturnEntities(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0, +// -1 +// ], +// "relations": [ +// 0, +// 1, +// 2 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "San Francisco", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": -1, +// "entityTo": 0, +// "constraints": [] +// } +// ], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// _, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.Equal(t, errors.New("non-existing entity referenced in return"), err) +// } + +// /* +// Tests a query with no relation field +// t: *testing.T, makes go recognise this as a test +// */ +// func TestNoRelationsField(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "San Francisco", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.city == "San Francisco" RETURN x)LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))LET edges = first(RETURN UNION_DISTINCT([],[]))RETURN {"vertices":nodes, "edges":edges }` +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests a query with double WITH +// t: *testing.T, makes go recognise this as a test +// */ +// func TestDoubleWITH(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "databaseName": "test", +// "return": { +// "entities": [ +// 0, +// 1, +// 2, +// 3 +// ], +// "relations": [ +// 0, +// 1 +// ] +// }, +// "entities": [ +// { +// "type": "kamerleden", +// "constraints": [] +// }, +// { +// "type": "partijen", +// "constraints": [] +// } +// , +// { +// "type": "kamerleden", +// "constraints": [] +// }, +// { +// "type": "commissies", +// "constraints": [] +// } +// ], +// "relations": [ +// { +// "type": "lid_van", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": 0, +// "entityTo": 1, +// "constraints": [] +// }, +// { +// "type": "onderdeel_van", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": 2, +// "entityTo": 3, +// "constraints": [] +// } +// ], +// "limit": 5000, +// "modifiers": [] +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// convertedResult, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) + +// // Assert that the result and the expected result are the same +// correctConvertedResult := "WITH partijen, commissiesLET n0 = (FOR x IN kamerleden RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x lid_van OPTIONS { uniqueEdges: \"path\" }LIMIT 5000 RETURN DISTINCT p )LET n2 = (FOR x IN kamerleden RETURN x)LET r1 = (FOR x IN n2 FOR v, e, p IN 1..1 OUTBOUND x onderdeel_van OPTIONS { uniqueEdges: \"path\" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), flatten(r1[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), flatten(r1[**].edges), [],[]))RETURN {\"vertices\":nodes, \"edges\":edges }" +// cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") +// cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") +// assert.Equal(t, correctConvertedResult, cleanedResult) +// } + +// /* +// Tests an entity with a lower than -1 in a relation +// t: *testing.T, makes go recognise this as a test +// */ +// func TestEntityFromLowerThanNegativeOneInRelation(t *testing.T) { +// // Setup for test +// // Create query conversion service +// service := NewService() + +// query := []byte(`{ +// "return": { +// "entities": [ +// 0 +// ], +// "relations": [ +// 0 +// ] +// }, +// "entities": [ +// { +// "type": "airports", +// "constraints": [ +// { +// "attribute": "city", +// "value": "San Francisco", +// "dataType": "string", +// "matchType": "exact" +// } +// ] +// } +// ], +// "relations": [ +// { +// "type": "flights", +// "depth": { +// "min": 1, +// "max": 1 +// }, +// "entityFrom": -4, +// "entityTo": 0, +// "constraints": [] +// } +// ], +// "limit": 5000 +// }`) + +// // Unmarshall the incoming message into an IncomingJSONQuery object +// var JSONQuery entity.IncomingQueryJSON +// json.Unmarshal(query, &JSONQuery) + +// _, err := service.ConvertQuery(&JSONQuery) + +// // Assert that there is no error +// assert.NoError(t, err) +// } diff --git a/cypher/convertQuery.go b/cypher/convertQuery.go index 25c6b51..397966b 100644 --- a/cypher/convertQuery.go +++ b/cypher/convertQuery.go @@ -61,6 +61,7 @@ func createCypher(JSONQuery *entity.IncomingQueryJSON) (*string, error) { func createReturnStatement(JSONQuery *entity.IncomingQueryJSON, parts entity.Query) (*string, error) { var retStatement string + var retType string // This is a marker attached to the end, for ease of parsing in the executor // First check to see if the return is a table (due to a groupby at the end) or if it is nodelink data numOfParts := len(parts) @@ -73,6 +74,7 @@ func createReturnStatement(JSONQuery *entity.IncomingQueryJSON, parts entity.Que byName := strings.Replace(by, ".", "_", 1) retStatement = fmt.Sprintf("RETURN %v, %v", byName, gName) + retType = ";table" } else { // Return is nodelink // Loop through the parts of the query from back to front @@ -114,6 +116,8 @@ func createReturnStatement(JSONQuery *entity.IncomingQueryJSON, parts entity.Que break } } + + retType = ";nodelink" } retStatement = retStatement + "\n" + fmt.Sprintf("LIMIT %v", JSONQuery.Limit) -- GitLab