From 12e74013953d36bd017da0248946b2c2e93743ff Mon Sep 17 00:00:00 2001
From: Kieran van Gaalen <kieran.van.gaalen@casema.nl>
Date: Tue, 16 Nov 2021 12:09:22 +0100
Subject: [PATCH] Implemented modifiers

---
 aql/convertQuery.go   | 58 ++++++++++++++++++++++++++++++++++++++++---
 entity/queryStruct.go |  2 +-
 main/main.go          |  2 +-
 realtest.json         | 10 +++++++-
 4 files changed, 65 insertions(+), 7 deletions(-)

diff --git a/aql/convertQuery.go b/aql/convertQuery.go
index 4d57360..9764071 100644
--- a/aql/convertQuery.go
+++ b/aql/convertQuery.go
@@ -87,6 +87,14 @@ createQuery generates a query based on the json file provided
 	Return: *string, a string containing the corresponding AQL query and an error
 */
 func createQuery(JSONQuery *entity.IncomingQueryJSON, tree []entity.Tree, topNode entity.QueryEntityStruct) *string {
+	modName := ""
+	if len(JSONQuery.Modifiers) > 0 {
+		modifier := JSONQuery.Modifiers[0]
+		fmt.Println(modifier.SelectedType + ", " + strconv.Itoa(modifier.SelectedTypeID))
+		if modifier.SelectedType == "entity" && modifier.SelectedTypeID == topNode.ID {
+			modName = fmt.Sprintf("e_%v", topNode.ID)
+		}
+	}
 	output := createLetFor("result", fmt.Sprintf("e_%v", topNode.ID), topNode.Name, 0)
 	for constraint := range topNode.Constraints {
 		output += createFilter(topNode.Constraints[constraint], fmt.Sprintf("e_%v", topNode.ID))
@@ -95,15 +103,42 @@ func createQuery(JSONQuery *entity.IncomingQueryJSON, tree []entity.Tree, topNod
 	subNames := []string{subName}
 	output += subQuery
 	output += createZeroFilter(append(subNames, fmt.Sprintf("e_%v", topNode.ID)))
-	output += createReturn(fmt.Sprintf("e_%v", topNode.ID), "", subNames)
+	output += createReturn(fmt.Sprintf("e_%v", topNode.ID), "", modName, subNames)
 	output += ")\n"
-	output += "let nodes = union_distinct(flatten(result[**].nodes),[])\nlet edges = union_distinct(flatten(result[**].rel),[])\nreturn {\"vertices\":nodes,\"edges\":edges}"
+	//output += "LET nodes = union_distinct(flatten(result[**].nodes),[])\nLET edges = union_distinct(flatten(result[**].rel),[])\nLET modif = union_distinct(flatten(result[**].mod),[])\n"
+	if len(JSONQuery.Modifiers) == 0 {
+		output += "LET nodes = union_distinct(flatten(result[**].nodes),[])\nLET edges = union_distinct(flatten(result[**].rel),[])\nRETURN {\"vertices\":nodes,\"edges\":edges}"
+	} else {
+		output += "LET modif = union_distinct(flatten(result[**].mod),[])\n"
+		modifier := JSONQuery.Modifiers[0]
+		if modifier.AttributeIndex == -1 {
+			output += "RETURN " + getModifierType(modifier) + "(modif)"
+		} else {
+			var attribute string
+			// Selecting the right attribute from either the entity constraint or relation constraint
+			if modifier.SelectedType == "entity" {
+				attribute = JSONQuery.Entities[modifier.SelectedTypeID].Constraints[modifier.AttributeIndex].Attribute
+			} else {
+				attribute = JSONQuery.Relations[modifier.SelectedTypeID].Constraints[modifier.AttributeIndex].Attribute
+			}
+			output += "RETURN " + getModifierType(modifier) + "(modif[**]." + attribute + ")"
+		}
+	}
 	return &output
 }
 
 func createQueryRecurse(JSONQuery *entity.IncomingQueryJSON, tree []entity.Tree, currentindex int, topNode entity.QueryEntityStruct, indentindex int) (string, string) {
 	currentTree := tree[currentindex]
 	newNode := getTreeNewNode(currentTree, tree, topNode)
+	modName := ""
+	if len(JSONQuery.Modifiers) > 0 {
+		modifier := JSONQuery.Modifiers[0]
+		if modifier.SelectedType == "entity" && modifier.SelectedTypeID == newNode.ID {
+			modName = fmt.Sprintf("e_%v", newNode.ID)
+		} else if modifier.SelectedType == "relation" && modifier.SelectedTypeID == currentTree.Self.Rel.ID {
+			modName = fmt.Sprintf("r%v", currentTree.Self.Rel.ID)
+		}
+	}
 	output := ""
 	output += fixIndent(createLetFor(fmt.Sprintf("e%v", newNode.ID), fmt.Sprintf("e_%v", newNode.ID), newNode.Name, indentindex), indentindex)
 	output += fixIndent(fmt.Sprintf("\tFOR r%v IN %v\n", currentTree.Self.Rel.ID, currentTree.Self.Rel.Name), indentindex)
@@ -121,7 +156,7 @@ func createQueryRecurse(JSONQuery *entity.IncomingQueryJSON, tree []entity.Tree,
 		subNames = append(subNames, subName)
 	}
 	output += fixIndent(createZeroFilter(append(subNames, fmt.Sprintf("e_%v", newNode.ID), fmt.Sprintf("r%v", currentTree.Self.Rel.ID))), indentindex)
-	output += fixIndent(createReturn(fmt.Sprintf("e_%v", newNode.ID), fmt.Sprintf("r%v", currentTree.Self.Rel.ID), subNames), indentindex)
+	output += fixIndent(createReturn(fmt.Sprintf("e_%v", newNode.ID), fmt.Sprintf("r%v", currentTree.Self.Rel.ID), modName, subNames), indentindex)
 	output += fixIndent(")\n", indentindex)
 	return output, fmt.Sprintf("e%v", newNode.ID)
 }
@@ -184,7 +219,7 @@ func createZeroFilter(subNames []string) string {
 	return output
 }
 
-func createReturn(nodeName string, relName string, subNames []string) string {
+func createReturn(nodeName string, relName string, modName string, subNames []string) string {
 	output := "\tRETURN {\"nodes\":union_distinct("
 	for i := range subNames {
 		output += fmt.Sprintf("flatten(%v[**].nodes), ", subNames[i])
@@ -201,6 +236,14 @@ func createReturn(nodeName string, relName string, subNames []string) string {
 	if len(subNames) == 0 {
 		output += ", []"
 	}
+	output += "), \"mod\": union_distinct("
+	for i := range subNames {
+		output += fmt.Sprintf("flatten(%v[**].mod), ", subNames[i])
+	}
+	output += "[" + modName + "]"
+	if len(subNames) == 0 {
+		output += ", []"
+	}
 	output += ")}\n"
 	return output
 }
@@ -253,3 +296,10 @@ func fixIndent(input string, indentCount int) string {
 	output += input
 	return output
 }
+
+func getModifierType(modifier entity.QueryModifierStruct) string {
+	if modifier.Type == "COUNT" {
+		return "LENGTH"
+	}
+	return modifier.Type
+}
diff --git a/entity/queryStruct.go b/entity/queryStruct.go
index 9cf011e..b74e4f5 100644
--- a/entity/queryStruct.go
+++ b/entity/queryStruct.go
@@ -75,7 +75,7 @@ type QueryMLStruct struct {
 // QueryModifierStruct encapsulates a single modifier with its corresponding constraints
 type QueryModifierStruct struct {
 	Type           string // SUM COUNT AVG
-	SelectedType   string // node relation
+	SelectedType   string // entity relation
 	SelectedTypeID int    // ID of the enitity or relation
 	AttributeIndex int    // = -1 if its the node or relation, = > -1 if an attribute is selected
 }
diff --git a/main/main.go b/main/main.go
index 90f0c6e..18a0f92 100644
--- a/main/main.go
+++ b/main/main.go
@@ -20,7 +20,7 @@ The main function that calls the appropriate functions
 */
 func main() {
 	queryservice := aql.NewService()
-	jsonFile, err := os.Open("../test.json")
+	jsonFile, err := os.Open("../realtest.json")
 	// if we os.Open returns an error then handle it
 	if err != nil {
 		log.Println(err)
diff --git a/realtest.json b/realtest.json
index 5aa9061..b4122c1 100644
--- a/realtest.json
+++ b/realtest.json
@@ -136,5 +136,13 @@
             "constraints": []
         }
     ],
-    "limit": 5000
+    "limit": 5000,
+    "modifiers": [
+        {
+            "type": "SUM",
+            "selectedType": "entity",
+            "selectedTypeID": 3,
+            "attributeIndex": 0
+        }
+    ]
 }
\ No newline at end of file
-- 
GitLab