diff --git a/aql/hierarchy.go b/aql/hierarchy.go index d8c3cfd9e92b22eb4e47a430ea8917e493d93214..9e95d20b32035c3673fc750b502d2831bed91a8a 100644 --- a/aql/hierarchy.go +++ b/aql/hierarchy.go @@ -16,22 +16,27 @@ var listoflists [][]pdict var reldone map[int]bool var entdone map[int]bool var funcdone map[int]bool -var layercounter int +var relfuncdone map[int]bool func search(JSONQuery *entity.IncomingQueryJSON) { reldone = make(map[int]bool) entdone = make(map[int]bool) + funcdone = make(map[int]bool) + relfuncdone = make(map[int]bool) + var s []pdict //printSlice(s) + //layercounter = 0 - initval := pdict{ - typename: "relation", - pointer: 0, + initent := pdict{ + typename: "entity", + pointer: 3, } - s = append(s, initval) + + s = append(s, initent) listoflists = append(listoflists, s) - layercounter = 0 - RelToEnt(JSONQuery, initval) + EntToRel(JSONQuery, initent) + for i := range listoflists { for j := range listoflists[i] { fmt.Println(listoflists[i][j]) @@ -41,8 +46,18 @@ func search(JSONQuery *entity.IncomingQueryJSON) { } +/* +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 +*/ func RelToEnt(JSONQuery *entity.IncomingQueryJSON, rel pdict) { var newlist []pdict + 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].EntityFrom == i { @@ -60,29 +75,32 @@ func RelToEnt(JSONQuery *entity.IncomingQueryJSON, rel pdict) { } } } - + // This relation has found all its entities so we can set it's ID to true reldone[rel.pointer] = true - // If we are in the formost list inside the listoflists (index 0) then we have to prepend an entirely new list into listoflists - // Otherwise, we just add the values inside our newlist to the list infront of our current list - // We only decrease the layercounter if we are not already on layer 0 (index 0) + // 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 if layercounter == 0 { listoflists = prepend(listoflists, newlist) - fmt.Println("Layercounter 0 prepend") + fmt.Println("RelToEnt Layercounter 0 prepend entity") } else { layercounter-- for i := range newlist { listoflists[layercounter] = append(listoflists[layercounter], newlist[i]) - fmt.Println("Layercounter " + strconv.Itoa(layercounter) + " append to layer above us") + fmt.Println("RelToEnt Layercounter " + strconv.Itoa(layercounter) + " append to layer above us, appending type: " + newlist[i].typename + " with pointer: " + strconv.Itoa(newlist[i].pointer)) } } - // Recursion here + // 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(i)) + fmt.Println("EntToRel being called with index?: " + strconv.Itoa(newlist[i].pointer)) EntToRel(JSONQuery, newlist[i]) } @@ -90,8 +108,18 @@ func RelToEnt(JSONQuery *entity.IncomingQueryJSON, rel pdict) { } +/* +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, ent pdict) { var newlist []pdict + 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].EntityFrom == ent.pointer { @@ -109,29 +137,269 @@ func EntToRel(JSONQuery *entity.IncomingQueryJSON, ent pdict) { } } } + // This entity has found all its relations so we can set it's ID to true entdone[ent.pointer] = true - // If the layercounter points to the last index we simply append the whole list behind (relations always get added behind entities) - // Otherwise we append each item to the existing list + 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 if layercounter == len(listoflists)-1 { listoflists = append(listoflists, newlist) layercounter++ + fmt.Println("EntToRel Layercounter last appending below: type relation") } else { layercounter++ for i := range newlist { listoflists[layercounter] = append(listoflists[layercounter], newlist[i]) + fmt.Println("EntToRel Layercounter " + strconv.Itoa(layercounter) + " append to layer below us, appending type: " + newlist[i].typename + " with pointer: " + strconv.Itoa(newlist[i].pointer)) } } - // Recursion here + // 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(i)) + fmt.Println("RelToEnt being called with index?: " + strconv.Itoa(newlist[i].pointer)) RelToEnt(JSONQuery, newlist[i]) + fmt.Println("RelToAllFunc being called with index?: " + strconv.Itoa(newlist[i].pointer)) + RelToAllFunc(JSONQuery, newlist[i]) + + } + } +} + +/* +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, rel pdict) { + var funcappliedtosubquery []pdict + var functowhichrelapplies []pdict + 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.Functions { + if _, ok := relfuncdone[rel.pointer]; !ok { + if _, ok := funcdone[i]; !ok { + if JSONQuery.Functions[i].RelationID == rel.pointer { + relfunc := pdict{ + typename: "function", + pointer: i, + } + funcappliedtosubquery = append(funcappliedtosubquery, relfunc) + fmt.Println("I AM HERE 1") + } + + if JSONQuery.Relations[rel.pointer].FunctionPointer.From == i { + fromfunc := pdict{ + typename: "function", + pointer: i, + } + functowhichrelapplies = append(functowhichrelapplies, fromfunc) + fmt.Println("I AM HERE 2") + + } else if JSONQuery.Relations[rel.pointer].FunctionPointer.To == i { + tofunc := pdict{ + typename: "function", + pointer: 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 { + + if layercountertwo == 0 { + listoflists = prepend(listoflists, functowhichrelapplies) + fmt.Println("RellToAllFunc Layercounter 0 prepend, prepending functowhichrelapplies") + + } else { + layercountertwo-- + for i := range functowhichrelapplies { + listoflists[layercountertwo] = append(listoflists[layercountertwo], functowhichrelapplies[i]) + fmt.Println("RellToAllFunc Layercounter " + strconv.Itoa(layercountertwo) + " append to layer below us, appending type: " + functowhichrelapplies[i].typename + " with pointer: " + strconv.Itoa(functowhichrelapplies[i].pointer)) + } + + } + + for i := range functowhichrelapplies { + fmt.Println("FuncToAllRell being called with index?: " + strconv.Itoa(functowhichrelapplies[i].pointer)) + FuncToAllRel(JSONQuery, functowhichrelapplies[i]) + + } + } + + if len(funcappliedtosubquery) != 0 { + //newlayercounter := layercounter + if layercounterthree == len(listoflists)-1 { + listoflists = append(listoflists, funcappliedtosubquery) + layercounterthree++ + fmt.Println("RellToAllFunc Layercounter last prepend, appending funcappliedtosubquery") + } else { + layercounterthree++ + for i := range funcappliedtosubquery { + listoflists[layercounterthree] = append(listoflists[layercounterthree], funcappliedtosubquery[i]) + fmt.Println("RellToAllFunc Layercounter " + strconv.Itoa(layercounterthree) + " append to layer below us, appending type: " + funcappliedtosubquery[i].typename + " with pointer: " + strconv.Itoa(funcappliedtosubquery[i].pointer)) + + } + + } + + for i := range funcappliedtosubquery { + fmt.Println("FuncToAllRel being called with index?: " + strconv.Itoa(funcappliedtosubquery[i].pointer)) + FuncToAllRel(JSONQuery, funcappliedtosubquery[i]) + } + } + +} + +/* +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, function pdict) { + var funcappliedtosubquery []pdict + var relattachedtofunc []pdict + 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.Functions[function.pointer].RelationID == i { + funcrel := pdict{ + typename: "relation", + pointer: i, + } + funcappliedtosubquery = append(funcappliedtosubquery, funcrel) + + } + + if JSONQuery.Relations[i].FunctionPointer.From == function.pointer { + fromrel := pdict{ + typename: "relation", + pointer: i, + } + relattachedtofunc = append(relattachedtofunc, fromrel) + + } else if JSONQuery.Relations[i].FunctionPointer.To == function.pointer { + torel := pdict{ + typename: "relation", + pointer: i, + } + relattachedtofunc = append(relattachedtofunc, torel) + + } + + } + } + } + funcdone[function.pointer] = true + + layercountertwo := layercounter + layercounterthree := layercounter + if len(funcappliedtosubquery) != 0 { + //newlayercounter := layercounter + if layercountertwo == 0 { + listoflists = prepend(listoflists, funcappliedtosubquery) + fmt.Println("FuncToAllRel Layercounter 0 prepend, prepending funcappliedtosubquery") + + } else { + layercountertwo-- + for i := range funcappliedtosubquery { + listoflists[layercountertwo] = append(listoflists[layercountertwo], funcappliedtosubquery[i]) + + fmt.Println("FuncToAllRel Layercounter " + strconv.Itoa(layercountertwo) + " append to layer below us, appending type: " + funcappliedtosubquery[i].typename + " with pointer: " + strconv.Itoa(funcappliedtosubquery[i].pointer)) + } + + } + + for i := range funcappliedtosubquery { + fmt.Println("RelToEnt being called with index?: " + strconv.Itoa(funcappliedtosubquery[i].pointer)) + RelToEnt(JSONQuery, funcappliedtosubquery[i]) + fmt.Println("RelToAllFunc being called with index?: " + strconv.Itoa(funcappliedtosubquery[i].pointer)) + RelToAllFunc(JSONQuery, funcappliedtosubquery[i]) + + } + } + + if len(relattachedtofunc) != 0 { + + if layercounterthree == len(listoflists)-1 { + listoflists = append(listoflists, relattachedtofunc) + layercounterthree++ + fmt.Println("FuncToAllRel Layercounter last append, appending relattachedtofunc") + + } else { + layercounterthree++ + for i := range relattachedtofunc { + listoflists[layercounterthree] = append(listoflists[layercounterthree], relattachedtofunc[i]) + fmt.Println("FuncToAllRel Layercounter " + strconv.Itoa(layercounterthree) + " append to layer below us, appending type: " + relattachedtofunc[i].typename + " with pointer: " + strconv.Itoa(relattachedtofunc[i].pointer)) + } + + } + + for i := range relattachedtofunc { + fmt.Println("RelToEnt being called with index?: " + strconv.Itoa(relattachedtofunc[i].pointer)) + RelToEnt(JSONQuery, relattachedtofunc[i]) + fmt.Println("RelToAllFunc being called with index?: " + strconv.Itoa(relattachedtofunc[i].pointer)) + RelToAllFunc(JSONQuery, relattachedtofunc[i]) + } + } +} + +// TODO +// Write a function that appends 1 level above +func AboveAppend() { + +} + +// TODO +// Write a function that appends 1 level below +func BelowAppend() { + +} + +// 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 [][]pdict, element 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 + } } } + 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 [][]pdict, element []pdict) [][]pdict { var dummylist []pdict dummy := pdict{ diff --git a/aql/mockConvertQuery.go b/aql/mockConvertQuery.go index 8a23e4c8a1896fdab0475a361da09a3f6716e4b6..91139b5c0ecc1beef29e76d2648298b6092e2df4 100644 --- a/aql/mockConvertQuery.go +++ b/aql/mockConvertQuery.go @@ -39,7 +39,7 @@ func (s *MockService) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string if !s.throwError { return &mockQuery, nil } - return nil, errors.New("Failed to convert query") + return nil, errors.New("failed to convert query") } /* diff --git a/entity/queryStruct.go b/entity/queryStruct.go index 91f80e98a380222271731a2e309de874fa0e2f54..284746e6335f9eb7a02f3a1b3f34d11e455198fc 100644 --- a/entity/queryStruct.go +++ b/entity/queryStruct.go @@ -27,11 +27,12 @@ type QueryEntityStruct struct { // QueryRelationStruct encapsulates a single relation with its corresponding constraints type QueryRelationStruct struct { - Type string - EntityFrom int - EntityTo int - Depth QuerySearchDepthStruct - Constraints []QueryConstraintStruct + Type string + EntityFrom int + EntityTo int + Depth QuerySearchDepthStruct + Constraints []QueryConstraintStruct + FunctionPointer QueryFunctionPointerStruct } type QueryFunctionStruct struct { @@ -74,3 +75,8 @@ type QueryConstraintStruct struct { MatchType string FunctionPointer int } + +type QueryFunctionPointerStruct struct { + From int + To int +} diff --git a/test.json b/test.json index 9d12f5f07c237bf885e24f11ab12490d72ace4bc..26fb03a9dc65c462249f385d46e4ba42deee2ba0 100644 --- a/test.json +++ b/test.json @@ -3,16 +3,22 @@ "entities": [ 0, 1, - 2 + 2, + 3 ], "relations": [ 0, - 1 + 1, + 2 ] }, "entities": [ { - "type": "parties", + "type": "parliament", + "constraints": [] + }, + { + "type": "commissions", "constraints": [] }, { @@ -20,24 +26,25 @@ "constraints": [] }, { - "type": "commissions", + "type": "resolutions", "constraints": [] } ], "relations": [ { - "type": "member_of", + "type": "part_of", "depth": { "min": 1, "max": 1 }, - "entityFrom": 1, - "entityTo": 0, + "entityFrom": 0, + "entityTo": 1, "constraints": [], "functionPointer": { "from": -1, "to": -1 } + }, { "type": "part_of", @@ -45,17 +52,83 @@ "min": 1, "max": 1 }, - "entityFrom": 1, + "entityFrom": -1, "entityTo": 2, "constraints": [], + "functionPointer": { + "from": 0, + "to": -1 + } + + }, + { + "type": "submits", + "depth": { + "min": 1, + "max": 1 + }, + "entityFrom": 2, + "entityTo": 3, + "constraints": [], "functionPointer": { "from": -1, "to": -1 } } ], - "functions": [], + "functions": [ + { + "type": "groupBy", + "typeID": 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 + } + + } + ] + }, + { + "type": "groupBy", + "typeID": 1, + "groupType": "entity", + "groupID": 2, + "groupAttribute": "age", + "byType": "entity", + "byID": 3, + "byAttribute": "submitter", + "appliedModifier": "AVG", + "relationID": 2, + "constraints": [ + { + "attribute": "age", + "value": "45", + "dataType": "number", + "matchType": "GT", + "functionPointer": { + "from": -1, + "to": -1 + } + + } + ] + } + ], "limit": 5000, "modifiers": [], - "databaseName": "Tweede Kamer Dataset" + "databaseName": "TweedeKamer" }