Newer
Older
/*
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 (
"errors"
"fmt"
"git.science.uu.nl/graphpolaris/query-conversion/entity"
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) {
// Check to make sure all indexes exist
largestEntityID := len(JSONQuery.Entities) - 1
largestRelationID := len(JSONQuery.Relations) - 1
// Make sure no entity should be returned that is outside the range of that list
for _, e := range JSONQuery.Return.Entities {
// If this entity references an entity that is outside the range
return nil, errors.New("non-existing entity referenced in return")
}
}
// Make sure that no relation mentions a non-existing entity
for _, r := range JSONQuery.Relations {
Kieran van Gaalen
committed
if r.FromID > largestEntityID && r.FromType == "entity" || r.ToID > largestEntityID && r.ToType == "entity" {
return nil, errors.New("non-exisiting entity referenced in relation")
}
}
// Make sure no non-existing relation is tried to be returned
for _, r := range JSONQuery.Return.Relations {
return nil, errors.New("non-existing relation referenced in return")
Geurtjens,D. (Douwe Geurtjens)
committed
// Don't run search if we are getting empty queries from unit tests
if len(JSONQuery.Entities) != 0 && len(JSONQuery.Relations) != 0 {
search(JSONQuery, 0)
}
result := createQuery(JSONQuery)
/*
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) *string {
Kieran van Gaalen
committed
for list := range listoflists {
for index := range listoflists[list] {
element := listoflists[list][index]
fmt.Println(element.Typename)
switch element.Typename {
Kieran van Gaalen
committed
case "entity":
entity := JSONQuery.Entities[element.Pointer]
Kieran van Gaalen
committed
query += entityToQuery(entity, JSONQuery)
case "relation":
relation := JSONQuery.Relations[element.Pointer]
Kieran van Gaalen
committed
query += relationToQuery(relation, JSONQuery)
Kieran van Gaalen
committed
case "groupBy":
function := JSONQuery.GroupBys[element.Pointer]
Kieran van Gaalen
committed
query += functionToQuery(function, JSONQuery, list == len(listoflists)-1)
case "filter":
filter := JSONQuery.Filters[element.Pointer]
Kieran van Gaalen
committed
query += filterToQuery(filter, JSONQuery)
Geurtjens,D. (Douwe Geurtjens)
committed
}
unusedRelations := findUnusedRelations(JSONQuery)
if len(unusedRelations) >= 0 {
query += "LET nodes = first(RETURN UNION_DISTINCT("
for i := range unusedRelations {
query += "flatten(" + unusedRelations[i] + "[**].vertices),"
}
query += "[],[]))\n"
query += "LET edges = first(RETURN UNION_DISTINCT("
for i := range unusedRelations {
query += "flatten(" + unusedRelations[i] + "[**].edges),"
}
query += "[],[]))\nRETURN {\"vertices\":nodes, \"edges\":edges }"
}
Kieran van Gaalen
committed
return &query
}
Kieran van Gaalen
committed
func entityToQuery(element entity.QueryEntityStruct, JSONQuery *entity.IncomingQueryJSON) string {
thisname := fmt.Sprintf("e%v", element.ID)
ret := createLetFor(thisname, element.Name)
Kieran van Gaalen
committed
return ret
}
Kieran van Gaalen
committed
func relationToQuery(element entity.QueryRelationStruct, JSONQuery *entity.IncomingQueryJSON) string {
thisname := fmt.Sprintf("r%v", element.ID)
ret := createLetFor(thisname, element.Name)
filters := tryGetFilterTo("relation", element.ID, JSONQuery)
var ydefined bool = false
var zdefined bool = false
if len(filters) > 0 {
for i := range filters {
filter := filters[i]
filter2 := filter
for filter2.FromType == "filter" {
filters2 := tryGetFilterTo("filter", filter.ID, JSONQuery)
filter2 = filters2[0]
}
if filter2.FromType == element.FromType && filter2.FromID == element.FromID {
Kieran van Gaalen
committed
ret += fmt.Sprintf("\tFOR y in f%v\n", filter.ID)
ydefined = true
Kieran van Gaalen
committed
ret += fmt.Sprintf("\tFOR z in f%v\n", filter.ID)
zdefined = true
Kieran van Gaalen
committed
if !ydefined {
ret += fmt.Sprintf("\tFOR y in %v%v\n", typeToPrefix(element.FromType), element.FromID)
Geurtjens,D. (Douwe Geurtjens)
committed
}
Kieran van Gaalen
committed
if !zdefined {
ret += fmt.Sprintf("\tFOR z in %v%v\n", typeToPrefix(element.ToType), element.ToID)
Geurtjens,D. (Douwe Geurtjens)
committed
}
Kieran van Gaalen
committed
ret += "\tFILTER x._from == y._id AND x._to == z._id\n"
Kieran van Gaalen
committed
ret += "\tRETURN DISTINCT {\n"
ret += "\t\"edges\": x,\n"
Kieran van Gaalen
committed
return ret
Geurtjens,D. (Douwe Geurtjens)
committed
}
Kieran van Gaalen
committed
func functionToQuery(element entity.QueryGroupByStruct, JSONQuery *entity.IncomingQueryJSON, final bool) string {
ret := getTupleVar(element, JSONQuery)
Kieran van Gaalen
committed
if final {
ret += createFinalGroupBy(element, JSONQuery)
} else {
ret += createGroupBy(element, JSONQuery)
}
Kieran van Gaalen
committed
return ret
Geurtjens,D. (Douwe Geurtjens)
committed
}
Kieran van Gaalen
committed
func filterToQuery(element entity.QueryFilterStruct, JSONQuery *entity.IncomingQueryJSON) string {
thisname := fmt.Sprintf("f%v", element.ID)
var filteredpill string
filters := tryGetFilterTo("filter", element.ID, JSONQuery)
if len(filters) > 0 {
filteredpill = fmt.Sprintf("f%v", filters[0].ID)
} else {
filteredpill = fmt.Sprintf("%v%v", typeToPrefix(element.FromType), element.FromID)
}
ret := createLetFor(thisname, filteredpill)
Kieran van Gaalen
committed
if element.FromType == "groupBy" {
ret += fmt.Sprintf("\tFILTER x.modifier %v %v\n", wordsToLogicalSign(element), element.Value)
Kieran van Gaalen
committed
} else {
ret += fmt.Sprintf("\tFILTER x.%v %v %v\n", element.Attribute, wordsToLogicalSign(element), element.Value)
Kieran van Gaalen
committed
}
Kieran van Gaalen
committed
return ret
Kieran van Gaalen
committed
func createLetFor(variableName string, enumerableName string) string {
return "LET " + variableName + " = (\n\tFOR x IN " + enumerableName + "\n"
Kieran van Gaalen
committed
func typeToPrefix(pillType string) string {
switch pillType {
case "entity":
return "e"
case "relation":
return "r"
case "groupBy":
return "g"
case "filter":
return "f"
default:
return ""
}
Kieran van Gaalen
committed
func tryGetFilterTo(toType string, toID int, JSONQuery *entity.IncomingQueryJSON) []entity.QueryFilterStruct {
var list []entity.QueryFilterStruct
for i := range JSONQuery.Filters {
filter := JSONQuery.Filters[i]
if filter.ToType == toType && filter.ToID == toID {
list = append(list, filter)
}
}
Kieran van Gaalen
committed
return list
}
func tryGetFilterFrom(fromType string, fromID int, JSONQuery *entity.IncomingQueryJSON) []entity.QueryFilterStruct {
var list []entity.QueryFilterStruct
for i := range JSONQuery.Filters {
filter := JSONQuery.Filters[i]
if filter.FromType == fromType && filter.FromID == fromID {
list = append(list, filter)
}
}
Kieran van Gaalen
committed
return list
}
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
func wordsToLogicalSign(element entity.QueryFilterStruct) 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 = "=="
}
Kieran van Gaalen
committed
}
Kieran van Gaalen
committed
func getTupleVar(element entity.QueryGroupByStruct, JSONQuery *entity.IncomingQueryJSON) string {
Kieran van Gaalen
committed
thisname := fmt.Sprintf("gt%v", element.ID)
result += createLetFor(thisname, JSONQuery.Relations[element.RelationID].Name)
result += createSubVariable("variable_0", "variable_1", fmt.Sprintf("r%v[**].vertices[1]", element.RelationID), "_id", "x._to", "_id")
Kieran van Gaalen
committed
result += createSubVariable("variable_2", "variable_3", fmt.Sprintf("%v%v", typeToPrefix(element.GroupType), element.GroupID), "_id", "x._from", element.GroupAttribute)
Kieran van Gaalen
committed
result += "\tRETURN {\n\t\t\"variable_0\": variable_1, \n\t\t\"variable_1\": variable_3\n\t}\n)\n"
return result
}
func createSubVariable(variableName string, variableName2 string, forName string, filter1 string, filter2 string, returnValue string) string {
result := "\tLET " + variableName + " = (\n\t\tFOR y IN " + forName + "\n"
Kieran van Gaalen
committed
return result + "\t\tFILTER y." + filter1 + " == " + filter2 + "\n\t\tRETURN y." + returnValue + "\n\t) " +
"\n\tLET " + variableName2 + " = " + variableName + "[0] \n"
}
Kieran van Gaalen
committed
func createGroupBy(element entity.QueryGroupByStruct, JSONQuery *entity.IncomingQueryJSON) string {
Kieran van Gaalen
committed
thisname := fmt.Sprintf("g%v", element.ID)
tuplename := fmt.Sprintf("gt%v", element.ID)
result := createLetFor(thisname, tuplename)
result += createCollect(element)
result += "\tRETURN {\n\t_id: c,\n\tmodifier: variable_0\n\t}\n)\n"
Kieran van Gaalen
committed
return result
}
func createFinalGroupBy(element entity.QueryGroupByStruct, JSONQuery *entity.IncomingQueryJSON) string {
tuplename := fmt.Sprintf("gt%v", element.ID)
result := "FOR x IN " + tuplename + "\n"
result += createCollect(element)
Kieran van Gaalen
committed
filters := tryGetFilterFrom("groupBy", element.ID, JSONQuery)
if len(filters) > 0 {
for i := range filters {
result += createFilter(filters[i])
}
}
Kieran van Gaalen
committed
result += fmt.Sprintf("\tRETURN {\n\tname: c,\n\t%v: variable_0\n\t}\n)\n", element.GroupAttribute)
Kieran van Gaalen
committed
return result
Kieran van Gaalen
committed
func createCollect(element entity.QueryGroupByStruct) string {
return "\tCOLLECT c = x.variable_0 INTO groups = x.variable_1\n\t" +
Kieran van Gaalen
committed
"LET variable_0 = " + element.AppliedModifier + "(groups) \n"
Kieran van Gaalen
committed
func createFilter(filter entity.QueryFilterStruct) string {
return "\tFILTER variable_0 " + wordsToLogicalSign(filter) + " " + filter.Value + " \n"
}
func findUnusedRelations(JSONQuery *entity.IncomingQueryJSON) []string {
var unused []string
for i := range JSONQuery.Relations {
relationUnused := true
relation := JSONQuery.Relations[i]
for j := range JSONQuery.GroupBys {
groupBy := JSONQuery.GroupBys[j]
if groupBy.RelationID == relation.ID {
relationUnused = false
}
}
if relationUnused {
unused = append(unused, fmt.Sprintf("r%v", relation.ID))
}
}
return unused
}