From 0e17ac0a33895802008096b0e33dd3b224c17ea6 Mon Sep 17 00:00:00 2001 From: Douwe <d.geurtjens@students.uu.nl> Date: Fri, 12 Nov 2021 16:04:30 +0100 Subject: [PATCH] Hierarchy unit test fixed --- aql/convertQuery.go | 7 +- aql/hierarchy.go | 385 +++++++------------------------- aql/hierarchy_test.go | 495 +++++++++++++++++++++--------------------- aql/newHierarchy.go | 113 ---------- entity/queryStruct.go | 16 +- realtest.json | 10 +- 6 files changed, 338 insertions(+), 688 deletions(-) delete mode 100644 aql/newHierarchy.go diff --git a/aql/convertQuery.go b/aql/convertQuery.go index e6d5368..28303d7 100644 --- a/aql/convertQuery.go +++ b/aql/convertQuery.go @@ -6,6 +6,7 @@ This program has been developed by students from the bachelor Computer Science a package aql import ( + "encoding/json" "errors" "fmt" "strconv" @@ -66,6 +67,8 @@ func (s *Service) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string, er } fmt.Println("Next please!") } + fuckshitprinter, _ := json.Marshal(tree) + fmt.Println(string(fuckshitprinter)) result := createQuery(JSONQuery, tree, topNode) return result, nil } @@ -97,8 +100,8 @@ func createQueryRecurse(JSONQuery *entity.IncomingQueryJSON, tree []entity.Tree, for constraint := range newNode.Constraints { output += createFilter(newNode.Constraints[constraint], fmt.Sprintf("e_%v", newNode.ID)) } - for constraint := range currentTree.Self.Rel.QueryConstraintStruct { - output += createFilter(currentTree.Self.Rel.QueryConstraintStruct[constraint], fmt.Sprintf("r%v", currentTree.Self.Rel.ID)) + for constraint := range currentTree.Self.Rel.Constraints { + output += createFilter(currentTree.Self.Rel.Constraints[constraint], fmt.Sprintf("r%v", currentTree.Self.Rel.ID)) } output += fmt.Sprintf("FILTER r%v._from == e_%v._id AND r%v._to == e_%v._id", currentTree.Self.Rel.ID, currentTree.Self.FromNode.ID, currentTree.Self.Rel.ID, currentTree.Self.ToNode.ID) var subNames []string diff --git a/aql/hierarchy.go b/aql/hierarchy.go index 3ab3814..bdd6591 100644 --- a/aql/hierarchy.go +++ b/aql/hierarchy.go @@ -1,344 +1,113 @@ package aql import ( - "fmt" - "strconv" - "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 reldone map[int]bool -var entdone map[int]bool -var funcdone map[int]bool -var relfuncdone map[int]bool -var filterDone map[int]bool - -func search(JSONQuery *entity.IncomingQueryJSON, index int) []entity.PdictList { - var listoflists []entity.PdictList - listoflists = []entity.PdictList{} - reldone = make(map[int]bool) - entdone = make(map[int]bool) - funcdone = make(map[int]bool) - relfuncdone = make(map[int]bool) - filterDone = make(map[int]bool) - var s entity.PdictList - //printSlice(s) - //layercounter = 0 - - initent := makePdict(ENTITYSTRING, index) - s = append(s, initent) - listoflists = append(listoflists, s) - listoflists = entToRel(JSONQuery, listoflists, initent) - - for i := range listoflists { - for j := range listoflists[i] { - fmt.Println(listoflists[i][j]) - } - fmt.Println("") +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{}, } - fmt.Println(listoflists) - return listoflists + treeList = append(treeList, topTree) + treeListIndex := len(treeList) - 1 + treeList = getChildrenFromTree(JSONQuery, treeList, treeListIndex, 0) + return treeList, topNode } /* -relToEnt Get the entities connected to a relation and recursivly constructs part of the hierarchy -Entities always get added IN FRONT OF their respective relation in the hierarchy - JSONQuery: *entity.IncomingQueryJSON, the query in JSON format - rel: pdict, the relation to find all connected entities for +Get a entity that has only 1 relation attached an return its index */ -func relToEnt(JSONQuery *entity.IncomingQueryJSON, listoflists []entity.PdictList, rel entity.Pdict) []entity.PdictList { - var newlist entity.PdictList - layercounter := findCurrentLayer(listoflists, rel) - // Loop over all entities - // If an entity is already in the entdone dict we already added it to the hierarchy, so we don't have to add it again - // If an entity matches either the from or to in a relation we can add it to the newlist - for i := range JSONQuery.Entities { - if _, ok := entdone[i]; !ok { - if JSONQuery.Relations[rel.Pointer].FromID == i && JSONQuery.Relations[rel.Pointer].FromType == ENTITYSTRING { - fromentity := makePdict(ENTITYSTRING, i) - newlist = append(newlist, fromentity) - } else if JSONQuery.Relations[rel.Pointer].ToID == i && JSONQuery.Relations[rel.Pointer].ToType == ENTITYSTRING { - toentity := makePdict(ENTITYSTRING, i) - newlist = append(newlist, toentity) +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++ } } - } - // This relation has found all its entities so we can set it's ID to true - reldone[rel.Pointer] = true - // If the newlist is empty, we can just skip the recursion - // This is effectively our base case - if len(newlist) != 0 { - // If our layercounter is equal to 0 we are in the first "layer" of the hierarchy - // Because we add the entities IN FRONT OF their respective relation we don't have to move the layercounter before prepending - // If our layercounter is not equal to 0 we lower the layercounter and then add each item to the newly selected layer - listoflists = aboveAppend(listoflists, layercounter, newlist) - - // After getting a list of entities we can only go towards a list of relation - // So we recurse by calling EntToRel - for i := range newlist { - fmt.Println("EntToRel being called with index?: " + strconv.Itoa(newlist[i].Pointer)) - listoflists = entToRel(JSONQuery, listoflists, newlist[i]) - + if connectionCount == 1 { + indexOfNodeToReturn = i + return JSONQuery.Entities[indexOfNodeToReturn] } } - - return listoflists - + return JSONQuery.Entities[indexOfNodeToReturn] } -/* -entToRel Get the relations connected to a entity and recursivly constructs part of the hierarchy -Relation always get added BEHIND their respective entity in the hierarchy - JSONQuery: *entity.IncomingQueryJSON, the query in JSON format - ent: pdict, the entity to find all connected relations for -*/ -func entToRel(JSONQuery *entity.IncomingQueryJSON, listoflists []entity.PdictList, ent entity.Pdict) []entity.PdictList { - var newlist entity.PdictList - layercounter := findCurrentLayer(listoflists, ent) - // Loop over all relations - // If a relation is already in the reldone dict we already added it to the hierarchy, so we don't have to add it again - // If a relation matches either the from or to with the entity we can add it to the newlist - for i := range JSONQuery.Relations { - if _, ok := reldone[i]; !ok { - if JSONQuery.Relations[i].FromID == ent.Pointer && JSONQuery.Relations[i].FromType == ENTITYSTRING { - rel := makePdict(RELATIONSTRING, i) - newlist = append(newlist, rel) - } else if JSONQuery.Relations[i].ToID == ent.Pointer && JSONQuery.Relations[i].ToType == ENTITYSTRING { - rel := makePdict(RELATIONSTRING, i) - newlist = append(newlist, rel) - } - } - } - // This entity has found all its relations so we can set it's ID to true - entdone[ent.Pointer] = true - - if len(newlist) != 0 { - // If our layercounter is equal to the length of the hierarchy - 1 we are in the last "layer" of the hierarchy - // Because we add the relations BEHIND their respective entities we don't have to move the layercounter before appending - // TODO TAKE OUT UNNEEDED LAYERCOUNTER INCREMENTS AND DECREMENTS - // If our layercounter is any other value we increase the layercounter and then add each item to the newly selected layer - listoflists = belowAppend(listoflists, layercounter, newlist) - - // After getting a list of relations we can only go towards a list of entities or a list of functions - // So we recurse by calling RelToEnt and RelToAllFunc - for i := range newlist { - fmt.Println("RelToEnt being called with index?: " + strconv.Itoa(newlist[i].Pointer)) - listoflists = relToEnt(JSONQuery, listoflists, newlist[i]) - fmt.Println("RelToAllFunc being called with index?: " + strconv.Itoa(newlist[i].Pointer)) - listoflists = relToAllFunc(JSONQuery, listoflists, newlist[i]) - +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 } } - return listoflists + relationdone[tripleToReturn.Rel.ID] = true + return tripleToReturn } -/* -relToAllFunc Get the functions connected (both functions that are applied to a subquery a relation is a part of and functions the relation is connected to itself) - to a relation and recursivly constructs part of the hierarchy -If a function is applied to a subquery the relation is a part of, we add it BEHIND its respective relation -If a function is connected to a relation (relation uses the results from the function), we add it IN FRONT OF its respective relation - JSONQuery: *entity.IncomingQueryJSON, the query in JSON format - rel: pdict, the relation to find all connected functions for -*/ -func relToAllFunc(JSONQuery *entity.IncomingQueryJSON, listoflists []entity.PdictList, rel entity.Pdict) []entity.PdictList { - var funcappliedtosubquery entity.PdictList - var functowhichrelapplies entity.PdictList - layercounter := findCurrentLayer(listoflists, rel) - // Loop over all functions - // If a relation is already in the relfuncdone dict we already added it to the hierarchy, so we don't have to add it again - // If a function's relationID matches the current relation then the function is applied to a subquery - // If the relation's functionpointer matches a function's ID then the relation is connected to the function - // Depending on the case they get put in a different list and are put in different places in the hierarchy - for i := range JSONQuery.GroupBys { - if _, ok := relfuncdone[rel.Pointer]; !ok { - if _, ok := funcdone[i]; !ok { - if JSONQuery.GroupBys[i].RelationID == rel.Pointer { - relfunc := makePdict(GROUPBYSTRING, i) - funcappliedtosubquery = append(funcappliedtosubquery, relfunc) - fmt.Println("I AM HERE 1") - } - - if JSONQuery.Relations[rel.Pointer].FromID == i && JSONQuery.Relations[rel.Pointer].FromType == GROUPBYSTRING { - fromfunc := makePdict(GROUPBYSTRING, i) - functowhichrelapplies = append(functowhichrelapplies, fromfunc) - fmt.Println("I AM HERE 2") - - } else if JSONQuery.Relations[rel.Pointer].ToID == i && JSONQuery.Relations[rel.Pointer].ToType == GROUPBYSTRING { - tofunc := makePdict(GROUPBYSTRING, i) - functowhichrelapplies = append(functowhichrelapplies, tofunc) - fmt.Println("I AM HERE 3") - - } - - } - } - - } - relfuncdone[rel.Pointer] = true - layercountertwo := layercounter - layercounterthree := layercounter - // See main function comment to see which sublist gets put where in the hierarchy - if len(functowhichrelapplies) != 0 { - listoflists = aboveAppend(listoflists, layercountertwo, functowhichrelapplies) - - for i := range functowhichrelapplies { - fmt.Println("FuncToAllRell being called with index?: " + strconv.Itoa(functowhichrelapplies[i].Pointer)) - listoflists = funcToAllRel(JSONQuery, listoflists, functowhichrelapplies[i]) - - } - } - - if len(funcappliedtosubquery) != 0 { - //newlayercounter := layercounter - listoflists = belowAppend(listoflists, layercounterthree, funcappliedtosubquery) - - for i := range funcappliedtosubquery { - fmt.Println("FuncToAllRel being called with index?: " + strconv.Itoa(funcappliedtosubquery[i].Pointer)) - listoflists = funcToAllRel(JSONQuery, listoflists, funcappliedtosubquery[i]) - } - } - return listoflists +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 } -/* -funcToAllRel Get the relations connected (both relations that are in a subquery a function is applied to and relations that are connected to the function itself) - to a function and recursivly constructs part of the hierarchy -If a relation is in a subquery that the function is applied to, we add the relation IN FRONT OF its respective function -If a relation is connected to a function, we add the relation BEHIND its respective function - JSONQuery: *entity.IncomingQueryJSON, the query in JSON format - function: pdict, the function to find all connected relations for -*/ -func funcToAllRel(JSONQuery *entity.IncomingQueryJSON, listoflists []entity.PdictList, function entity.Pdict) []entity.PdictList { - var funcappliedtosubquery entity.PdictList - var relattachedtofunc entity.PdictList - layercounter := findCurrentLayer(listoflists, function) - for i := range JSONQuery.Relations { - if _, ok := funcdone[function.Pointer]; !ok { - if _, ok := relfuncdone[i]; !ok { - // The func is attached to this relation - if JSONQuery.GroupBys[function.Pointer].RelationID == i { - funcrel := makePdict(RELATIONSTRING, i) - funcappliedtosubquery = append(funcappliedtosubquery, funcrel) - - } - - if JSONQuery.Relations[i].FromID == function.Pointer && JSONQuery.Relations[i].FromType == GROUPBYSTRING { - fromrel := makePdict(RELATIONSTRING, i) - relattachedtofunc = append(relattachedtofunc, fromrel) - - } else if JSONQuery.Relations[i].ToID == function.Pointer && JSONQuery.Relations[i].ToType == GROUPBYSTRING { - torel := makePdict(RELATIONSTRING, i) - relattachedtofunc = append(relattachedtofunc, torel) - - } - +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 } } } - funcdone[function.Pointer] = true - - layercountertwo := layercounter - layercounterthree := layercounter - if len(funcappliedtosubquery) != 0 { - //newlayercounter := layercounter - listoflists = aboveAppend(listoflists, layercountertwo, funcappliedtosubquery) - - for i := range funcappliedtosubquery { - fmt.Println("RelToEnt being called with index?: " + strconv.Itoa(funcappliedtosubquery[i].Pointer)) - listoflists = relToEnt(JSONQuery, listoflists, funcappliedtosubquery[i]) - fmt.Println("RelToAllFunc being called with index?: " + strconv.Itoa(funcappliedtosubquery[i].Pointer)) - listoflists = relToAllFunc(JSONQuery, listoflists, funcappliedtosubquery[i]) - - } - } - - if len(relattachedtofunc) != 0 { - listoflists = belowAppend(listoflists, layercounterthree, relattachedtofunc) - - for i := range relattachedtofunc { - fmt.Println("RelToEnt being called with index?: " + strconv.Itoa(relattachedtofunc[i].Pointer)) - listoflists = relToEnt(JSONQuery, listoflists, relattachedtofunc[i]) - fmt.Println("RelToAllFunc being called with index?: " + strconv.Itoa(relattachedtofunc[i].Pointer)) - listoflists = relToAllFunc(JSONQuery, listoflists, relattachedtofunc[i]) - } - } - return listoflists -} - -// A function that appends 1 level above (if index is 0 this won't work) -func aboveAppend(listoflists []entity.PdictList, index int, values entity.PdictList) []entity.PdictList { - if index == 0 { - return prepend(listoflists, values) - } else { - index-- - for i := range values { - listoflists[index] = append(listoflists[index], values[i]) - } - return listoflists - } - -} - -// A function that appends 1 level below -func belowAppend(listoflists []entity.PdictList, index int, values entity.PdictList) []entity.PdictList { - if index == len(listoflists)-1 { - listoflists = append(listoflists, values) - return listoflists - - } else { - index++ - for i := range values { - listoflists[index] = append(listoflists[index], values[i]) - } - return listoflists - - } -} - -// A simple double-for loop that finds the layer in which an element resides in the hierarchy -// Because we only append elements relative to another element, we can freely use this to keep track of layers -func findCurrentLayer(list []entity.PdictList, element entity.Pdict) int { - currlayer := -1 - for i, sublist := range list { - for j := range sublist { - if sublist[j].Pointer == element.Pointer && sublist[j].Typename == element.Typename { - currlayer = i - //break + // 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) + treeList = getChildrenFromTree(JSONQuery, treeList, treeListIndex, treeListIndex) } } - return currlayer -} - -// Adds a list of pdicts to the hierarchy, but IN FRONT OF the current layer -// Only needed when a entire new list has to be inserted in front of the hierarcy -// Prepending to existing layers can be done by decreasing the layercounter and appending -// See XToY functions for example usage -func prepend(list []entity.PdictList, element entity.PdictList) []entity.PdictList { - var dummylist entity.PdictList - dummy := makePdict("dummy", -1) - dummylist = append(dummylist, dummy) - list = append(list, dummylist) - copy(list[1:], list) - list[0] = element - return list -} - -/* -makePdict makes a pdict based on a typename and pointer -*/ -func makePdict(typeName string, pointer int) entity.Pdict { - return entity.Pdict{ - Typename: typeName, - Pointer: pointer, - } - + return treeList } diff --git a/aql/hierarchy_test.go b/aql/hierarchy_test.go index 4b5f5d2..f3c96f9 100644 --- a/aql/hierarchy_test.go +++ b/aql/hierarchy_test.go @@ -3,7 +3,7 @@ package aql import ( "encoding/json" "fmt" - "sort" + "strings" "testing" "git.science.uu.nl/graphpolaris/query-conversion/entity" @@ -17,316 +17,307 @@ func TestHierarchyBasic(t *testing.T) { "return": { "entities": [ 0, - 1 + 1, + 2, + 3, + 4 ], "relations": [ - 0 - ], - "groupBys": [] + 0, + 1, + 2, + 3 + ] }, "entities": [ { "name": "parliament", - "ID": 0 - }, - { - "name": "parties", - "ID": 1 - } - ], - "relations": [ - { "ID": 0, - "name": "member_of", - "depth": { - "min": 1, - "max": 1 - }, - "fromType": "entity", - "fromID": 0, - "toType": "entity", - "toID": 1 - } - ], - "groupBys": [], - "filters": [ - { - "ID": 0, - "fromType": "entity", - "fromID": 0, - "toType": "relation", - "toID": 0, - "attribute": "age", - "value": "45", - "dataType": "number", - "matchType": "GT", - "inType": "", - "inID": -1 + "constraints": [ + { + "attribute": "name", + "value": "Geert", + "dataType": "string", + "matchType": "CONTAINS" + } + ] }, { + "name": "commissions", "ID": 1, - "fromType": "relation", - "fromID": 0, - "toType": "relation", - "toID": 1, - "attribute": "isChairman", - "value": "45", - "dataType": "number", - "matchType": "GT", - "inType": "", - "inID": -1 - } - ], - "limit": 5000 - } - `) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - listoflists := search(&JSONQuery, 0) - - // Assert that the result and the expected result are the same - correctResult := `[[{entity 0} {entity 1}] [{filter 0}] [{relation 0}] [{filter 1}]]` - assert.Equal(t, correctResult, fmt.Sprint(listoflists)) -} - -func TestHierarchyRandomStart(t *testing.T) { - // Setup for test - // Create query conversion service - query := []byte(`{ - "return": { - "entities": [ - 0, - 1 - ], - "relations": [ - 0 - ], - "groupBys": [] - }, - "entities": [ + "constraints": [] + }, + { + "name": "parliament", + "ID": 2, + "constraints": [] + }, { "name": "parties", - "ID": 1 + "ID": 3, + "constraints": [ + { + "attribute": "seats", + "value": "10", + "dataType": "int", + "matchType": "LT" + } + ] }, - { - "name": "parliament", - "ID": 0 + "name": "resolutions", + "ID": 4, + "constraints": [ + { + "attribute": "date", + "value": "mei", + "dataType": "string", + "matchType": "CONTAINS" + } + ] } ], + "groupBys": [], "relations": [ { "ID": 0, - "name": "member_of", + "name": "part_of", "depth": { "min": 1, "max": 1 }, "fromType": "entity", - "fromID": 0, + "fromId": 0, "toType": "entity", - "toID": 1 - } - ], - "groupBys": [], - "filters": [ - { - "ID": 0, - "fromType": "entity", - "fromID": 0, - "toType": "relation", - "toID": 0, - "attribute": "age", - "value": "45", - "dataType": "number", - "matchType": "GT", - "inType": "", - "inID": -1 - }, - { - "ID": 1, - "fromType": "relation", - "fromID": 0, - "toType": "relation", "toID": 1, - "attribute": "isChairman", - "value": "45", - "dataType": "number", - "matchType": "GT", - "inType": "", - "inID": -1 - } - ], - "limit": 5000 - } - `) - - // Unmarshall the incoming message into an IncomingJSONQuery object - var JSONQuery entity.IncomingQueryJSON - json.Unmarshal(query, &JSONQuery) - correctResult := make([]entity.PdictList, 4) - correctResult[0] = entity.PdictList{{Typename: "entity", Pointer: 0}, {Typename: "entity", Pointer: 1}} - correctResult[1] = entity.PdictList{{Typename: "filter", Pointer: 0}} - correctResult[2] = entity.PdictList{{Typename: "relation", Pointer: 0}} - correctResult[3] = entity.PdictList{{Typename: "filter", Pointer: 1}} - - for i := range JSONQuery.Entities { - listoflists := search(&JSONQuery, i) - sortedListOfLists := make([]entity.PdictList, len(listoflists)) - for i, list := range listoflists { - k := make(entity.PdictList, list.Len()) - copy(k, list) - sort.Sort(k) - sortedListOfLists[i] = k - } - assert.Equal(t, fmt.Sprint(correctResult), fmt.Sprint(sortedListOfLists)) - } - -} - -func TestHierarchyWithGroupby(t *testing.T) { - // Setup for test - // Create query conversion service - query := []byte(`{ - "return": { - "entities": [ - 0, - 1, - 2, - 3 - ], - "relations": [ - 0, - 1, - 2 - ] - }, - "entities": [ - { - "ID": 0, - "name": "parliament" + "constraints": [] }, { "ID": 1, - "name": "commissions" - }, - { - "ID": 2, - "name": "parliament" - }, - { - "ID": 3, - "name": "resolutions" - } - ], - "relations": [ - { - "type": "part_of", + "name": "part_of", "depth": { "min": 1, "max": 1 }, "fromType": "entity", - "fromId": 0, + "fromId": 2, "toType": "entity", - "toID": 1 - + "toID": 1, + "constraints": [] }, { - "type": "part_of", + "ID": 2, + "name": "member_of", "depth": { "min": 1, "max": 1 }, - "fromType": "groupBy", - "fromID": 0, + "fromType": "entity", + "fromId": 2, "toType": "entity", - "toID": 2 - + "toID": 3, + "constraints": [] }, { - "type": "submits", + "ID": 3, + "name": "submits", "depth": { "min": 1, "max": 1 }, "fromType": "entity", - "fromID": 2, + "fromId": 2, "toType": "entity", - "toID": 3 + "toID": 4, + "constraints": [] } ], - "groupBys": [ - { - "ID": 0, - "groupType": "entity", - "groupID": 0, - "groupAttribute": "age", - "byType": "entity", - "byID": 1, - "byAttribute": "name", - "appliedModifier": "AVG", - "relationID": 0, - "constraints": [ - { - "attribute": "age", - "value": "45", - "dataType": "number", - "matchType": "GT", - "functionPointer": { - "from": -1, - "to": -1 - } - - } - ] - } - ], - "filters": [ - { - "ID": 0, - "fromType": "groupBy", - "fromID": 0, - "toType": "relation", - "toID": 1, - "attribute": "age", - "value": "45", - "dataType": "number", - "matchType": "GT", - "inType": "", - "inID": -1 - } - ], - "limit": 5000, - "modifiers": [], - "databaseName": "TweedeKamer" + "limit": 5000 } `) // Unmarshall the incoming message into an IncomingJSONQuery object var JSONQuery entity.IncomingQueryJSON json.Unmarshal(query, &JSONQuery) - correctResult := make([]entity.PdictList, 5) - correctResult[0] = entity.PdictList{{Typename: "entity", Pointer: 0}, {Typename: "entity", Pointer: 1}} - correctResult[1] = entity.PdictList{{Typename: "relation", Pointer: 0}} - correctResult[2] = entity.PdictList{{Typename: "entity", Pointer: 2}, {Typename: "entity", Pointer: 3}, {Typename: "groupBy", Pointer: 0}} - correctResult[3] = entity.PdictList{{Typename: "filter", Pointer: 0}} - correctResult[4] = entity.PdictList{{Typename: "relation", Pointer: 1}, {Typename: "relation", Pointer: 2}} + // Get the hierarchy and turn it into JSON so we can turn it into strings later + treeList, topNode := createHierarchy(&JSONQuery) + jsonTopNode, err := json.Marshal(topNode) + if err != nil { + fmt.Println("Marshalling went wrong") + } + jsonTreeList, err := json.Marshal(treeList) + if err != nil { + fmt.Println("Marshalling went wrong") + } + // These are the expected (correct) outputs + correctTopNode := []byte(`{"ID":0,"Name":"parliament","Constraints":[{"Attribute":"name","Value":"Geert","DataType":"string","MatchType":"CONTAINS","InID":0,"InType":""}]}`) + correctTreeList := []byte(`[ + { + "Self": { + "FromNode": { + "ID": 0, + "Name": "parliament", + "Constraints": [ + { + "Attribute": "name", + "Value": "Geert", + "DataType": "string", + "MatchType": "CONTAINS", + "InID": 0, + "InType": "" + } + ] + }, + "Rel": { + "ID": 0, + "Name": "part_of", + "FromType": "entity", + "FromID": 0, + "ToType": "entity", + "ToID": 1, + "Depth": { + "Min": 1, + "Max": 1 + }, + "Constraints": [] + }, + "ToNode": { + "ID": 1, + "Name": "commissions", + "Constraints": [] + } + }, + "Parent": -1, + "Children": [ + 1 + ] + }, + { + "Self": { + "FromNode": { + "ID": 2, + "Name": "parliament", + "Constraints": [] + }, + "Rel": { + "ID": 1, + "Name": "part_of", + "FromType": "entity", + "FromID": 2, + "ToType": "entity", + "ToID": 1, + "Depth": { + "Min": 1, + "Max": 1 + }, + "Constraints": [] + }, + "ToNode": { + "ID": 1, + "Name": "commissions", + "Constraints": [] + } + }, + "Parent": 0, + "Children": [ + 2, + 3 + ] + }, + { + "Self": { + "FromNode": { + "ID": 2, + "Name": "parliament", + "Constraints": [] + }, + "Rel": { + "ID": 2, + "Name": "member_of", + "FromType": "entity", + "FromID": 2, + "ToType": "entity", + "ToID": 3, + "Depth": { + "Min": 1, + "Max": 1 + }, + "Constraints": [] + }, + "ToNode": { + "ID": 3, + "Name": "parties", + "Constraints": [ + { + "Attribute": "seats", + "Value": "10", + "DataType": "int", + "MatchType": "LT", + "InID": 0, + "InType": "" + } + ] + } + }, + "Parent": 1, + "Children": [] + }, + { + "Self": { + "FromNode": { + "ID": 2, + "Name": "parliament", + "Constraints": [] + }, + "Rel": { + "ID": 3, + "Name": "submits", + "FromType": "entity", + "FromID": 2, + "ToType": "entity", + "ToID": 4, + "Depth": { + "Min": 1, + "Max": 1 + }, + "Constraints": [] + }, + "ToNode": { + "ID": 4, + "Name": "resolutions", + "Constraints": [ + { + "Attribute": "date", + "Value": "mei", + "DataType": "string", + "MatchType": "CONTAINS", + "InID": 0, + "InType": "" + } + ] + } + }, + "Parent": 2, + "Children": [] + } + ]`) + // Clean up the input and expected results + cleanedTopNode := strings.ReplaceAll(string(jsonTopNode), "\n", "") + cleanedTopNode = strings.ReplaceAll(cleanedTopNode, "\t", "") + cleanedTopNode = strings.ReplaceAll(cleanedTopNode, " ", "") - for i := range JSONQuery.Entities { - listoflists := search(&JSONQuery, i) + cleanedTreeList := strings.ReplaceAll(string(jsonTreeList), "\n", "") + cleanedTreeList = strings.ReplaceAll(cleanedTreeList, "\t", "") + cleanedTreeList = strings.ReplaceAll(cleanedTreeList, " ", "") - fmt.Println(listoflists) - sortedListOfLists := make([]entity.PdictList, len(listoflists)) - for i, list := range listoflists { - k := make(entity.PdictList, list.Len()) - copy(k, list) - sort.Sort(k) - sortedListOfLists[i] = k - } - assert.Equal(t, fmt.Sprint(correctResult), fmt.Sprint(sortedListOfLists)) - } + cleanedCorrectTopNode := strings.ReplaceAll(string(correctTopNode), "\n", "") + cleanedCorrectTopNode = strings.ReplaceAll(cleanedCorrectTopNode, "\t", "") + cleanedCorrectTopNode = strings.ReplaceAll(cleanedCorrectTopNode, " ", "") + + cleanedCorrectTreeList := strings.ReplaceAll(string(correctTreeList), "\n", "") + cleanedCorrectTreeList = strings.ReplaceAll(cleanedCorrectTreeList, "\t", "") + cleanedCorrectTreeList = strings.ReplaceAll(cleanedCorrectTreeList, " ", "") + assert.Equal(t, cleanedCorrectTopNode, cleanedTopNode) + assert.Equal(t, cleanedCorrectTreeList, cleanedTreeList) } diff --git a/aql/newHierarchy.go b/aql/newHierarchy.go deleted file mode 100644 index bdd6591..0000000 --- a/aql/newHierarchy.go +++ /dev/null @@ -1,113 +0,0 @@ -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) - treeList = getChildrenFromTree(JSONQuery, treeList, treeListIndex, treeListIndex) - } - } - return treeList -} diff --git a/entity/queryStruct.go b/entity/queryStruct.go index 248b58c..9cf011e 100644 --- a/entity/queryStruct.go +++ b/entity/queryStruct.go @@ -30,14 +30,14 @@ type QueryEntityStruct struct { // QueryRelationStruct encapsulates a single relation with its corresponding constraints type QueryRelationStruct struct { - ID int - Name string - FromType string - FromID int - ToType string - ToID int - Depth QuerySearchDepthStruct - QueryConstraintStruct []QueryConstraintStruct + ID int + Name string + FromType string + FromID int + ToType string + ToID int + Depth QuerySearchDepthStruct + Constraints []QueryConstraintStruct } type QueryGroupByStruct struct { diff --git a/realtest.json b/realtest.json index 46028cc..30fc8f3 100644 --- a/realtest.json +++ b/realtest.json @@ -22,8 +22,8 @@ { "attribute": "name", "value": "Geert", - "dataType": "text", - "matchType": "like" + "dataType": "string", + "matchType": "CONTAINS" } ] }, @@ -44,7 +44,7 @@ { "attribute": "seats", "value": "10", - "dataType": "number", + "dataType": "int", "matchType": "LT" } ] @@ -56,8 +56,8 @@ { "attribute": "date", "value": "mei", - "dataType": "text", - "matchType": "like" + "dataType": "string", + "matchType": "CONTAINS" } ] } -- GitLab