diff --git a/aql/convertQuery.go b/aql/convertQuery.go index 707c09ed41c44bda19e7f176739543688603080c..dfdcbc189cb10f9e02a3d3d03fa4fdc873707773 100644 --- a/aql/convertQuery.go +++ b/aql/convertQuery.go @@ -6,8 +6,8 @@ This program has been developed by students from the bachelor Computer Science a package aql import ( - "errors" "fmt" + "strconv" "git.science.uu.nl/graphpolaris/query-conversion/entity" ) @@ -22,299 +22,19 @@ ConvertQuery converts an IncomingQueryJSON object into AQL 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.FromID > largestEntityID && r.FromType == "entity" || r.ToID > largestEntityID && r.ToType == "entity" { - 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") - } - } - // Don't run search if we are getting empty queries from unit tests - var listoflists []entity.PdictList - if len(JSONQuery.Entities) != 0 && len(JSONQuery.Relations) != 0 { - listoflists = search(JSONQuery, 0) - } - result := createQuery(JSONQuery, listoflists) - 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, listoflists []entity.PdictList) *string { - query := "" - for list := range listoflists { - for index := range listoflists[list] { - element := listoflists[list][index] - fmt.Println(element.Typename) - switch element.Typename { - case "entity": - entity := JSONQuery.Entities[element.Pointer] - query += entityToQuery(entity, JSONQuery) - case "relation": - relation := JSONQuery.Relations[element.Pointer] - query += relationToQuery(relation, JSONQuery) - case "groupBy": - function := JSONQuery.GroupBys[element.Pointer] - query += functionToQuery(function, JSONQuery, list == len(listoflists)-1) - case "filter": - filter := JSONQuery.Filters[element.Pointer] - query += filterToQuery(filter, JSONQuery) - } - } - } - unusedRelations := findUnusedRelations(JSONQuery) - if len(unusedRelations) >= 0 { - query += "LET nodes = first(RETURN UNION_DISTINCT(" - for i := range unusedRelations { - query += "flatten(" + unusedRelations[i] + "[**].vertices)," - } - query += "[],[]))\n" - query += "LET edges = first(RETURN UNION_DISTINCT(" - for i := range unusedRelations { - query += "flatten(" + unusedRelations[i] + "[**].edges)," - } - query += "[],[]))\nRETURN {\"vertices\":nodes, \"edges\":edges }" - } - return &query -} - -func entityToQuery(element entity.QueryEntityStruct, JSONQuery *entity.IncomingQueryJSON) string { - thisname := fmt.Sprintf("e%v", element.ID) - ret := createLetFor(thisname, element.Name) - ret += "\tRETURN x\n)\n" - return ret -} - -func relationToQuery(element entity.QueryRelationStruct, JSONQuery *entity.IncomingQueryJSON) string { - thisname := fmt.Sprintf("r%v", element.ID) - ret := createLetFor(thisname, element.Name) - filters := tryGetFilterTo("relation", element.ID, JSONQuery) - var ydefined bool = false - var zdefined bool = false - if len(filters) > 0 { - for i := range filters { - filter := filters[i] - filter2 := filter - for filter2.FromType == "filter" { - filters2 := tryGetFilterTo("filter", filter.ID, JSONQuery) - filter2 = filters2[0] - } - if filter2.FromType == element.FromType && filter2.FromID == element.FromID { - ret += fmt.Sprintf("\tFOR y in f%v\n", filter.ID) - ydefined = true - } else { - ret += fmt.Sprintf("\tFOR z in f%v\n", filter.ID) - zdefined = true - } - } - } - if !ydefined { - ret += fmt.Sprintf("\tFOR y in %v%v\n", typeToPrefix(element.FromType), element.FromID) - } - if !zdefined { - ret += fmt.Sprintf("\tFOR z in %v%v\n", typeToPrefix(element.ToType), element.ToID) - } - ret += "\tFILTER x._from == y._id AND x._to == z._id\n" - ret += "\tLET nodes = APPEND([], [y, z])\n" - ret += "\tRETURN DISTINCT {\n" - ret += "\t\"edges\": x,\n" - ret += "\t\"vertices\": nodes\n\t}\n)\n" - return ret -} - -func functionToQuery(element entity.QueryGroupByStruct, JSONQuery *entity.IncomingQueryJSON, final bool) string { - ret := getTupleVar(element, JSONQuery) - if final { - ret += createFinalGroupBy(element, JSONQuery) - } else { - ret += createGroupBy(element, JSONQuery) - } - return ret -} - -func filterToQuery(element entity.QueryFilterStruct, JSONQuery *entity.IncomingQueryJSON) string { - thisname := fmt.Sprintf("f%v", element.ID) - var filteredpill string - filters := tryGetFilterTo("filter", element.ID, JSONQuery) - if len(filters) > 0 { - filteredpill = fmt.Sprintf("f%v", filters[0].ID) - } else { - filteredpill = fmt.Sprintf("%v%v", typeToPrefix(element.FromType), element.FromID) - } - ret := createLetFor(thisname, filteredpill) - if element.FromType == "groupBy" { - ret += fmt.Sprintf("\tFILTER x.modifier %v %v\n", wordsToLogicalSign(element), element.Value) - } else { - ret += fmt.Sprintf("\tFILTER x.%v %v %v\n", element.Attribute, wordsToLogicalSign(element), element.Value) - } - ret += "\tRETURN x\n)\n" - return ret -} - -func createLetFor(variableName string, enumerableName string) string { - return "LET " + variableName + " = (\n\tFOR x IN " + enumerableName + "\n" -} - -func typeToPrefix(pillType string) string { - switch pillType { - case "entity": - return "e" - case "relation": - return "r" - case "groupBy": - return "g" - case "filter": - return "f" - default: - return "" - } -} - -func tryGetFilterTo(toType string, toID int, JSONQuery *entity.IncomingQueryJSON) []entity.QueryFilterStruct { - var list []entity.QueryFilterStruct - for i := range JSONQuery.Filters { - filter := JSONQuery.Filters[i] - if filter.ToType == toType && filter.ToID == toID { - list = append(list, filter) - } - } - return list -} - -func tryGetFilterFrom(fromType string, fromID int, JSONQuery *entity.IncomingQueryJSON) []entity.QueryFilterStruct { - var list []entity.QueryFilterStruct - for i := range JSONQuery.Filters { - filter := JSONQuery.Filters[i] - if filter.FromType == fromType && filter.FromID == fromID { - list = append(list, filter) - } + shitlist, _ := createHierarchy(JSONQuery) + for i, tree := range shitlist { + fmt.Println("I am triple(from,rel,to): " + strconv.Itoa(tree.Self.FromNode.ID) + "," + strconv.Itoa(tree.Self.Rel.ID) + "," + strconv.Itoa(tree.Self.ToNode.ID)) + fmt.Println("My relation contains the following nodes(from,to): " + strconv.Itoa(tree.Self.Rel.FromID) + "," + strconv.Itoa(tree.Self.Rel.ToID)) + fmt.Println("My index is: " + strconv.Itoa(i)) + fmt.Println("My parent index is: " + strconv.Itoa(tree.Parent)) + fmt.Println("My children's indices are: ") + for j := range tree.Children { + fmt.Println(tree.Children[j]) + } + fmt.Println("Next please!") } - return list -} - -func wordsToLogicalSign(element entity.QueryFilterStruct) string { - var match string - switch element.DataType { - case "string": - switch element.MatchType { - case "NEQ": - match = "!=" - case "contains": - match = "LIKE" - case "excludes": - match = "NOT LIKE" - default: //EQ - match = "==" - } - case "int": - switch element.MatchType { - case "NEQ": - match = "!=" - case "GT": - match = ">" - case "LT": - match = "<" - case "GET": - match = ">=" - case "LET": - match = "<=" - default: //EQ - match = "==" - } - default: /*bool*/ - switch element.MatchType { - case "NEQ": - match = "!=" - default: //EQ - match = "==" - } - } - return match -} - -func getTupleVar(element entity.QueryGroupByStruct, JSONQuery *entity.IncomingQueryJSON) string { result := "" - thisname := fmt.Sprintf("gt%v", element.ID) - result += createLetFor(thisname, JSONQuery.Relations[element.RelationID].Name) - result += createSubVariable("variable_0", "variable_1", fmt.Sprintf("r%v[**].vertices[1]", element.RelationID), "_id", "x._to", "_id") - result += createSubVariable("variable_2", "variable_3", fmt.Sprintf("%v%v", typeToPrefix(element.GroupType), element.GroupID), "_id", "x._from", element.GroupAttribute) - result += "\tRETURN {\n\t\t\"variable_0\": variable_1, \n\t\t\"variable_1\": variable_3\n\t}\n)\n" - return result -} + return &result, nil -func createSubVariable(variableName string, variableName2 string, forName string, filter1 string, filter2 string, returnValue string) string { - result := "\tLET " + variableName + " = (\n\t\tFOR y IN " + forName + "\n" - return result + "\t\tFILTER y." + filter1 + " == " + filter2 + "\n\t\tRETURN y." + returnValue + "\n\t) " + - "\n\tLET " + variableName2 + " = " + variableName + "[0] \n" -} - -func createGroupBy(element entity.QueryGroupByStruct, JSONQuery *entity.IncomingQueryJSON) string { - thisname := fmt.Sprintf("g%v", element.ID) - tuplename := fmt.Sprintf("gt%v", element.ID) - result := createLetFor(thisname, tuplename) - result += createCollect(element) - result += "\tRETURN {\n\t_id: c,\n\tmodifier: variable_0\n\t}\n)\n" - return result -} - -func createFinalGroupBy(element entity.QueryGroupByStruct, JSONQuery *entity.IncomingQueryJSON) string { - tuplename := fmt.Sprintf("gt%v", element.ID) - result := "FOR x IN " + tuplename + "\n" - result += createCollect(element) - filters := tryGetFilterFrom("groupBy", element.ID, JSONQuery) - if len(filters) > 0 { - for i := range filters { - result += createFilter(filters[i]) - } - } - result += fmt.Sprintf("\tRETURN {\n\tname: c,\n\t%v: variable_0\n\t}\n)\n", element.GroupAttribute) - return result -} - -func createCollect(element entity.QueryGroupByStruct) string { - return "\tCOLLECT c = x.variable_0 INTO groups = x.variable_1\n\t" + - "LET variable_0 = " + element.AppliedModifier + "(groups) \n" -} - -func createFilter(filter entity.QueryFilterStruct) string { - return "\tFILTER variable_0 " + wordsToLogicalSign(filter) + " " + filter.Value + " \n" -} - -func findUnusedRelations(JSONQuery *entity.IncomingQueryJSON) []string { - var unused []string - for i := range JSONQuery.Relations { - relationUnused := true - relation := JSONQuery.Relations[i] - for j := range JSONQuery.GroupBys { - groupBy := JSONQuery.GroupBys[j] - if groupBy.RelationID == relation.ID { - relationUnused = false - } - } - if relationUnused { - unused = append(unused, fmt.Sprintf("r%v", relation.ID)) - } - } - return unused } diff --git a/aql/hierarchy.go b/aql/hierarchy.go index 7b8acfca47c4b84cbf193f1c4fa89287fcf719d4..3ab381457f3a3593aeb3c1c9d3b48870bb9ee05e 100644 --- a/aql/hierarchy.go +++ b/aql/hierarchy.go @@ -8,11 +8,12 @@ import ( ) // We use consts to define string to prevent typos +/* const ENTITYSTRING = "entity" const RELATIONSTRING = "relation" const GROUPBYSTRING = "groupBy" const FILTERSTRING = "filter" - +*/ var reldone map[int]bool var entdone map[int]bool var funcdone map[int]bool diff --git a/aql/newHierarchy.go b/aql/newHierarchy.go index 7188761f938200de988639b1f462cc8b5d589e5d..7848f640c54b4b25e53dedcb8ef84496ae920804 100644 --- a/aql/newHierarchy.go +++ b/aql/newHierarchy.go @@ -1 +1,113 @@ package aql + +import ( + "git.science.uu.nl/graphpolaris/query-conversion/entity" +) + +// We use consts to define string to prevent typos +const ENTITYSTRING = "entity" +const RELATIONSTRING = "relation" +const GROUPBYSTRING = "groupBy" +const FILTERSTRING = "filter" + +var relationdone map[int]bool + +func createHierarchy(JSONQuery *entity.IncomingQueryJSON) ([]entity.Tree, entity.QueryEntityStruct) { + var treeList []entity.Tree + relationdone = make(map[int]bool) + topNode := getTopNode(JSONQuery) + topTreeSelfTriple := getTripleFromNode(JSONQuery, topNode) + topTree := entity.Tree{ + Self: topTreeSelfTriple, + Parent: -1, + Children: []int{}, + } + treeList = append(treeList, topTree) + treeListIndex := len(treeList) - 1 + treeList = getChildrenFromTree(JSONQuery, treeList, treeListIndex, 0) + return treeList, topNode +} + +/* +Get a entity that has only 1 relation attached an return its index +*/ +func getTopNode(JSONQuery *entity.IncomingQueryJSON) entity.QueryEntityStruct { + indexOfNodeToReturn := -1 + for i, node := range JSONQuery.Entities { + connectionCount := 0 + for _, relation := range JSONQuery.Relations { + if (relation.FromType == ENTITYSTRING && relation.FromID == node.ID) || (relation.ToType == ENTITYSTRING && relation.ToID == node.ID) { + connectionCount++ + } + } + if connectionCount == 1 { + indexOfNodeToReturn = i + return JSONQuery.Entities[indexOfNodeToReturn] + } + } + return JSONQuery.Entities[indexOfNodeToReturn] +} + +func getTripleFromNode(JSONQuery *entity.IncomingQueryJSON, node entity.QueryEntityStruct) entity.Triple { + var tripleToReturn entity.Triple + for _, relation := range JSONQuery.Relations { + // The initial node was our From so we set the Triple accordingly + // TODO + // If the To is not an entity we might have to do something different + if (relation.FromType == ENTITYSTRING && relation.FromID == node.ID) && relation.ToType == ENTITYSTRING { + tripleToReturn.FromNode = node + tripleToReturn.Rel = relation + tripleToReturn.ToNode = JSONQuery.Entities[relation.ToID] + } else if (relation.ToType == ENTITYSTRING && relation.ToID == node.ID) && relation.FromType == ENTITYSTRING { + tripleToReturn.FromNode = JSONQuery.Entities[relation.ToID] + tripleToReturn.Rel = relation + tripleToReturn.ToNode = node + } + } + relationdone[tripleToReturn.Rel.ID] = true + return tripleToReturn +} + +func getTripleFromRelation(JSONQuery *entity.IncomingQueryJSON, relation entity.QueryRelationStruct) entity.Triple { + var tripleToReturn entity.Triple + tripleToReturn.FromNode = JSONQuery.Entities[relation.FromID] + tripleToReturn.Rel = relation + tripleToReturn.ToNode = JSONQuery.Entities[relation.ToID] + return tripleToReturn +} + +func getChildrenFromTree(JSONQuery *entity.IncomingQueryJSON, treeList []entity.Tree, treeListIndex int, parentIndex int) []entity.Tree { + var childRelationTriples []entity.Triple + for i, relation := range JSONQuery.Relations { + // We found a relation that is not our parent relation so we can check if it matches on of our nodes + // If it matches one of the nodes we can add it + if _, ok := relationdone[i]; !ok { + if relation.FromType == ENTITYSTRING && relation.FromID == treeList[parentIndex].Self.FromNode.ID { + triple := getTripleFromRelation(JSONQuery, relation) + childRelationTriples = append(childRelationTriples, triple) + relationdone[i] = true + } else if relation.ToType == ENTITYSTRING && relation.ToID == treeList[parentIndex].Self.ToNode.ID { + triple := getTripleFromRelation(JSONQuery, relation) + childRelationTriples = append(childRelationTriples, triple) + relationdone[i] = true + } + } + } + // We now have all our children, so we can now make those trees and find their children + // We can now also add the indices to the list of children from the tree calling this function + if len(childRelationTriples) != 0 { + for _, triple := range childRelationTriples { + childTree := entity.Tree{ + Self: triple, + Parent: treeListIndex, + Children: []int{}, + } + treeList = append(treeList, childTree) + // We get the new treeListIndex, which we can now add to the list of children from the tree calling this function + treeListIndex = len(treeList) - 1 + treeList[parentIndex].Children = append(treeList[parentIndex].Children, treeListIndex) + return getChildrenFromTree(JSONQuery, treeList, treeListIndex, treeListIndex) + } + } + return treeList +} diff --git a/entity/hierarchyStruct.go b/entity/hierarchyStruct.go index d97006f5f0efc25aacd9b26d3004384b20603db1..fe86cdbdb07f2426b1e76cd117cbd430fbbc1b76 100644 --- a/entity/hierarchyStruct.go +++ b/entity/hierarchyStruct.go @@ -8,9 +8,9 @@ type Pdict struct { } type Triple struct { - In int - Rel int - Out int + FromNode QueryEntityStruct + Rel QueryRelationStruct + ToNode QueryEntityStruct } type Tree struct { diff --git a/realtest.json b/realtest.json index a94ae633ed185534cbf7132ab496e6757c518a87..c007bf09ffa4f030da1fb33c36896c573ab0fb65 100644 --- a/realtest.json +++ b/realtest.json @@ -7,26 +7,86 @@ }, "entities": [ { - "name": "airports", - "ID": 0 + "name": "parliament", + "ID": 0, + "constraints": [] + }, + { + "name": "commission", + "ID": 1, + "constraints": [] + }, + { + "name": "parliament", + "ID": 2, + "constraints": [] + }, + { + "name": "parties", + "ID": 3, + "constraints": [] + } + , + { + "name": "resolutions", + "ID": 4, + "constraints": [] } ], "groupBys": [], - "relations": [], - "filters": [ - { - "ID": 0, - "fromType": "entity", - "fromID": 0, - "toType": "", - "toID": -1, - "attribute": "state", - "value": "HI", - "dataType": "string", - "matchType": "exact", - "inType": "", - "inID": -1 - } + "relations": [ + { + "ID": 0, + "name": "part_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromId": 0, + "toType": "entity", + "toID": 1, + "constraints": [] + }, + { + "ID": 1, + "name": "part_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromId": 2, + "toType": "entity", + "toID": 1, + "constraints": [] + }, + { + "ID": 2, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromId": 2, + "toType": "entity", + "toID": 3, + "constraints": [] + }, + { + "ID": 3, + "name": "submits", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromId": 2, + "toType": "entity", + "toID": 4, + "constraints": [] + } ], "limit": 5000 } \ No newline at end of file