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("") } fmt.Println(listoflists) return 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, 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) } } } // 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]) } } return listoflists } /* 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]) } } return listoflists } /* 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 } /* 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) } } } } 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 } } } 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, } }