-
Geurtjens,D. (Douwe Geurtjens) authoredGeurtjens,D. (Douwe Geurtjens) authored
convertQuery.go 10.52 KiB
/*
This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/
package aql
import (
//"encoding/json"
"encoding/json"
"errors"
"fmt"
"strconv"
"git.science.uu.nl/graphpolaris/query-conversion/entity"
)
// Version 1.13
/*
ConvertQuery converts an IncomingQueryJSON object into AQL
JSONQuery: *entity.IncomingQueryJSON, the query to be converted to AQL
Returns: *string, the AQL query and a possible error
*/
func (s *Service) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string, error) {
// TODO: MICHAEL WANT A SINGLE ENTITY TO RETURN A SUMMARY OF ATTRIBUTE VALUES (HISTOGRAM THINGIES)
// Check to make sure all indexes exist
// The count of entities
entityCount := len(JSONQuery.Entities)
// The count of relations
relationCount := len(JSONQuery.Relations)
// There are no entities or relations, our query is empty
if entityCount <= 0 && relationCount <= 0 {
fmt.Println("Empty query sent, returning default response")
return defaultReturn()
}
// There are no enities or there are no relations
if entityCount <= 0 || relationCount <= 0 {
return nil, errors.New("no relations or entities sent")
}
potentialErrors := entity.ValidateStruct(*JSONQuery)
// If we find the JSONQuery to be invalid we return a error
if len(potentialErrors) != 0 {
for _, err := range potentialErrors {
fmt.Printf("err: %v\n", err)
}
return nil, errors.New("JSONQuery invalid")
}
entityMap, relationMap, _ := entity.FixIndices(JSONQuery)
var tree []entity.Tree
var topNode entity.QueryEntityStruct
if len(JSONQuery.Entities) != 0 && len(JSONQuery.Relations) != 0 {
tree, topNode = createHierarchy(JSONQuery, entityMap, relationMap)
}
for i, treeElement := range tree {
fmt.Println("I am triple(from,rel,to): " + strconv.Itoa(treeElement.Self.FromNode.ID) + "," + strconv.Itoa(treeElement.Self.Rel.ID) + "," + strconv.Itoa(treeElement.Self.ToNode.ID))
fmt.Println("My relation contains the following nodes(from,to): " + strconv.Itoa(treeElement.Self.Rel.FromID) + "," + strconv.Itoa(treeElement.Self.Rel.ToID))
fmt.Println("My index is: " + strconv.Itoa(i))
fmt.Println("My parent index is: " + strconv.Itoa(treeElement.Parent))
fmt.Println("My children's indices are: ")
for j := range treeElement.Children {
fmt.Println(treeElement.Children[j])
}
fmt.Println("Next please!")
}
fuckshitprinter, _ := json.Marshal(tree)
fmt.Println(string(fuckshitprinter))
result := createQuery(JSONQuery, tree, topNode, entityMap, relationMap)
return result, nil
}
/*
createQuery generates a query based on the json file provided
JSONQuery: *entity.IncomingQueryJSON, this is a parsedJSON struct holding all the data needed to form a query,
Return: *string, a string containing the corresponding AQL query and an error
*/
func createQuery(JSONQuery *entity.IncomingQueryJSON, tree []entity.Tree, topNode entity.QueryEntityStruct, entityMap map[int]int, relationMap map[int]int) *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))
}
subQuery, subName := createQueryRecurse(JSONQuery, tree, 0, topNode, 1)
subNames := []string{subName}
output += subQuery
output += createZeroFilter(append(subNames, fmt.Sprintf("e_%v", topNode.ID)))
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),[])\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[entityMap[modifier.SelectedTypeID]].Constraints[modifier.AttributeIndex].Attribute
} else {
attribute = JSONQuery.Relations[relationMap[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)
for constraint := range newNode.Constraints {
output += fixIndent(createFilter(newNode.Constraints[constraint], fmt.Sprintf("e_%v", newNode.ID)), indentindex)
}
for constraint := range currentTree.Self.Rel.Constraints {
output += fixIndent(createFilter(currentTree.Self.Rel.Constraints[constraint], fmt.Sprintf("r%v", currentTree.Self.Rel.ID)), indentindex)
}
output += fixIndent(fmt.Sprintf("\tFILTER r%v._from == e_%v._id AND r%v._to == e_%v._id\n", currentTree.Self.Rel.ID, currentTree.Self.FromNode.ID, currentTree.Self.Rel.ID, currentTree.Self.ToNode.ID), indentindex)
var subNames []string
for i := range currentTree.Children {
subQuery, subName := createQueryRecurse(JSONQuery, tree, currentTree.Children[i], topNode, indentindex+1)
output += subQuery
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), modName, subNames), indentindex)
output += fixIndent(")\n", indentindex)
return output, fmt.Sprintf("e%v", newNode.ID)
}
func getTreeNewNode(currentTree entity.Tree, tree []entity.Tree, topNode entity.QueryEntityStruct) entity.QueryEntityStruct {
if currentTree.Parent < 0 {
if currentTree.Self.FromNode.ID == topNode.ID {
return currentTree.Self.ToNode
} else {
return currentTree.Self.FromNode
}
} else if currentTree.Self.FromNode.ID == tree[currentTree.Parent].Self.FromNode.ID || currentTree.Self.FromNode.ID == tree[currentTree.Parent].Self.ToNode.ID {
return currentTree.Self.ToNode
} else {
return currentTree.Self.FromNode
}
}
func createLetFor(variableName string, forName string, enumerableName string, indentindex int) string {
output := "LET " + variableName + " = (\n"
for i := 0; i < indentindex; i++ {
output += "\t"
}
output += "\tFOR " + forName + " IN " + enumerableName + "\n"
return output
}
func createFilter(constraint entity.QueryConstraintStruct, filtered string) string {
output := "\tFILTER " + filtered + "." + constraint.Attribute + " " + wordsToLogicalSign(constraint)
if constraint.DataType == "string" {
output += " \""
} else {
output += " "
}
if constraint.MatchType == "contains" {
output += "%"
}
output += constraint.Value
if constraint.MatchType == "contains" {
output += "%"
}
if constraint.DataType == "string" {
output += "\" "
} else {
output += " "
}
output += " \n"
return output
}
func createZeroFilter(subNames []string) string {
output := "\tFILTER"
for i := range subNames {
output += fmt.Sprintf(" length(%v) != 0", subNames[i])
if i < len(subNames)-1 {
output += " AND"
}
}
output += "\n"
return output
}
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])
}
output += "[" + nodeName + "]"
if len(subNames) == 0 {
output += ", []"
}
output += "), \"rel\": union_distinct("
for i := range subNames {
output += fmt.Sprintf("flatten(%v[**].rel), ", subNames[i])
}
output += "[" + relName + "]"
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
}
func wordsToLogicalSign(element entity.QueryConstraintStruct) string {
var match string
switch element.DataType {
case "string":
switch element.MatchType {
case "NEQ":
match = "!="
case "contains":
match = "LIKE"
case "excludes":
match = "NOT LIKE"
default: //EQ
match = "=="
}
case "int":
switch element.MatchType {
case "NEQ":
match = "!="
case "GT":
match = ">"
case "LT":
match = "<"
case "GET":
match = ">="
case "LET":
match = "<="
default: //EQ
match = "=="
}
default: /*bool*/
switch element.MatchType {
case "NEQ":
match = "!="
default: //EQ
match = "=="
}
}
return match
}
func fixIndent(input string, indentCount int) string {
output := ""
for i := 0; i < indentCount; i++ {
output += "\t"
}
output += input
return output
}
func getModifierType(modifier entity.QueryModifierStruct) string {
if modifier.Type == "COUNT" {
return "LENGTH"
}
return modifier.Type
}
func defaultReturn() (*string, error) {
defaultReturn := `LET nodes = first(RETURN UNION_DISTINCT([],[]))
LET edges = first(RETURN UNION_DISTINCT([],[]))
RETURN {"vertices":nodes, "edges":edges }`
return &defaultReturn, nil
}