diff --git a/aql/convertQuery.go b/aql/convertQuery.go index 4d57360895c5879fc475e096f7596e66237329ae..9764071c326f535c2d5d06628142d9c474bf9991 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 9cf011e3dea7d12c0e15f51176b7c9d159b727a3..b74e4f5ae8cba801aea1fa33d9b0a510400dfaee 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 90f0c6ea303cb0d985a6d774a00519b921f61caa..18a0f921aa06e4b0db2a9a4abf60d37bdf40bd4f 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 5aa90617af4c3d66526584965b8c2bc76f97a111..b4122c11dcc04baedb4c54dbf437fb9f799a85c4 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