/* 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 }