package aql import ( "fmt" "strconv" "git.science.uu.nl/graphpolaris/query-conversion/entity" ) type pdict struct { typename string pointer int } var listoflists [][]pdict var reldone map[int]bool var entdone map[int]bool var funcdone map[int]bool 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 initent := pdict{ typename: "entity", pointer: 0, } s = append(s, initent) listoflists = append(listoflists, s) EntToRel(JSONQuery, initent) for i := range listoflists { for j := range listoflists[i] { fmt.Println(listoflists[i][j]) } fmt.Println("") } AddFilters(JSONQuery) fmt.Println(listoflists) } /* 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].FromID == i && JSONQuery.Relations[rel.pointer].FromType == "entity" { fromentity := pdict{ typename: "entity", pointer: i, } newlist = append(newlist, fromentity) } else if JSONQuery.Relations[rel.pointer].ToID == i && JSONQuery.Relations[rel.pointer].ToType == "entity" { toentity := pdict{ typename: "entity", pointer: i, } newlist = append(newlist, toentity) } } } // 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 if layercounter == 0 { listoflists = prepend(listoflists, newlist) fmt.Println("RelToEnt Layercounter 0 prepend entity") } else { layercounter-- for i := range newlist { listoflists[layercounter] = append(listoflists[layercounter], newlist[i]) fmt.Println("RelToEnt Layercounter " + strconv.Itoa(layercounter) + " append to layer above us, appending type: " + newlist[i].typename + " with pointer: " + strconv.Itoa(newlist[i].pointer)) } } // 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)) EntToRel(JSONQuery, newlist[i]) } } } /* 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].FromID == ent.pointer && JSONQuery.Relations[i].FromType == "entity" { rel := pdict{ typename: "relation", pointer: i, } newlist = append(newlist, rel) } else if JSONQuery.Relations[i].ToID == ent.pointer && JSONQuery.Relations[i].ToType == "entity" { rel := pdict{ typename: "relation", pointer: 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 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)) } } // 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)) 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.GroupBys { if _, ok := relfuncdone[rel.pointer]; !ok { if _, ok := funcdone[i]; !ok { if JSONQuery.GroupBys[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].FromID == i && JSONQuery.Relations[rel.pointer].FromType == "groupBy" { fromfunc := pdict{ typename: "function", pointer: i, } functowhichrelapplies = append(functowhichrelapplies, fromfunc) fmt.Println("I AM HERE 2") } else if JSONQuery.Relations[rel.pointer].ToID == i && JSONQuery.Relations[rel.pointer].ToType == "groupBy" { 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.GroupBys[function.pointer].RelationID == i { funcrel := pdict{ typename: "relation", pointer: i, } funcappliedtosubquery = append(funcappliedtosubquery, funcrel) } if JSONQuery.Relations[i].FromID == function.pointer && JSONQuery.Relations[i].FromType == "groupBy" { fromrel := pdict{ typename: "relation", pointer: i, } relattachedtofunc = append(relattachedtofunc, fromrel) } else if JSONQuery.Relations[i].ToID == function.pointer && JSONQuery.Relations[i].ToType == "groupBy" { 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]) } } } func AddFilters(JSONQuery *entity.IncomingQueryJSON) { filterDone := make([]bool, len(JSONQuery.Filters)) for i, filter := range JSONQuery.Filters { if !filterDone[i] { p := pdict{ typename: filter.FilteredType, pointer: filter.FilteredID, } f := pdict{ typename: "filter", pointer: filter.ID, } addOneFilter(f, JSONQuery, p, &filterDone) } } } func addOneFilter(filterPDict pdict, JSONQuery *entity.IncomingQueryJSON, p pdict, filterDone *[]bool) { if p.typename == "filter" && (*filterDone)[p.pointer] { l := FindCurrentLayer(listoflists, p) k := []pdict{} if len(listoflists) > l+1 && listoflists[l+1][0].typename == "filter" { listoflists[l+1] = append(listoflists[l+1], filterPDict) } else { BelowAppend(l, k) } (*filterDone)[filterPDict.pointer] = true } else if p.typename == "filter" { pnew := pdict{ typename: JSONQuery.Filters[p.pointer].FilteredType, pointer: JSONQuery.Filters[p.pointer].FilteredID, } addOneFilter(p, JSONQuery, pnew, filterDone) l := FindCurrentLayer(listoflists, p) k := []pdict{filterPDict} if len(listoflists) > l+1 && listoflists[l+1][0].typename == "filter" { listoflists[l+1] = append(listoflists[l+1], filterPDict) } else { BelowAppend(l, k) } (*filterDone)[filterPDict.pointer] = true } else { l := FindCurrentLayer(listoflists, p) k := []pdict{filterPDict} if len(listoflists) > l+1 && listoflists[l+1][0].typename == "filter" { listoflists[l+1] = append(listoflists[l+1], filterPDict) } else { BelowAppend(l, k) } (*filterDone)[filterPDict.pointer] = true } } // A function that appends 1 level above (if index is 0 this won't work) func AboveAppend(index int, value []pdict) [][]pdict { if index == 0 { return prepend(listoflists, value) } return BelowAppend(index-1, value) } // A function that appends 1 level below (thanks to wasmup on stackoverflow) func BelowAppend(index int, value []pdict) [][]pdict { if len(listoflists) == index { // nil or empty slice or after last element return append(listoflists, value) } listoflists = append(listoflists[:index+1], listoflists[index:]...) // index < len(a) listoflists[index] = value 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 [][]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{ typename: "dummy", pointer: -1, } dummylist = append(dummylist, dummy) list = append(list, dummylist) copy(list[1:], list) list[0] = element return list }