From fa73ce7dfde2a3cbdff5a75228c415f4f039c3ec Mon Sep 17 00:00:00 2001
From: Douwe <d.geurtjens@students.uu.nl>
Date: Mon, 1 Nov 2021 15:10:20 +0100
Subject: [PATCH] ADDED: Functions are now propely loaded into the hierarchy

---
 aql/hierarchy.go        | 306 +++++++++++++++++++++++++++++++++++++---
 aql/mockConvertQuery.go |   2 +-
 entity/queryStruct.go   |  16 ++-
 test.json               |  93 ++++++++++--
 4 files changed, 382 insertions(+), 35 deletions(-)

diff --git a/aql/hierarchy.go b/aql/hierarchy.go
index d8c3cfd..9e95d20 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 8a23e4c..91139b5 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 91f80e9..284746e 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 9d12f5f..26fb03a 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"
 }
-- 
GitLab