Skip to content
Snippets Groups Projects
Commit 05dc5fed authored by Lelieveld,J.R.J. (Joris)'s avatar Lelieveld,J.R.J. (Joris)
Browse files

Merge branch 'feature/274-relation-support' into 'develop'

Query relation support

See merge request datastrophe/microservices-backbone/query-service!6
parents fe5fad98 bb4e0084
Branches
Tags
No related merge requests found
......@@ -19,6 +19,8 @@ import (
var producer alice.Producer
func main() {
//FORGLORY()
exchangeID := "query-requests"
routingKey := "aql-user-request"
......@@ -30,6 +32,7 @@ func main() {
redisclient.Start()
select {}
// file, _ := ioutil.ReadFile("./internal/data/jsonQuery.json")
// query, _ := aql.ConvertJSONToAQL(&file)
......@@ -92,3 +95,77 @@ func onMessageReceived(msg amqp.Delivery) {
producer.PublishMessage(querybyte, &queueID, &headers)
msg.Ack(true)
}
// func FORGLORY() {
// s := `{
// "Return": {
// "Entities": [
// 0,
// 1
// ],
// "Relations": [
// 0
// ]
// },
// "Entities": [
// {
// "Type": "airports",
// "Constraints": [
// {
// "Attribute": "country",
// "Value": "USA",
// "DataType": "text",
// "MatchType": "exact"
// }
// ]
// },
// {
// "Type": "airports",
// "Constraints": [
// {
// "Attribute": "city",
// "Value": "New York",
// "DataType": "text",
// "MatchType": "exact"
// },
// {
// "Attribute": "vip",
// "Value": "true",
// "DataType": "bool",
// "MatchType": "exact"
// }
// ]
// }
// ],
// "Relations": [
// {
// "Type": "flights",
// "Depth": {
// "min": 1,
// "max": 1
// },
// "EntityFrom": 0,
// "EntityTo": 1,
// "Constraints": [
// {
// "Attribute": "Month",
// "Value": "1",
// "DataType": "number",
// "MatchType": "exact"
// },
// {
// "Attribute": "Day",
// "Value": "15",
// "DataType": "number",
// "MatchType": "exact"
// }
// ]
// }
// ]
// }`
// s3 := []byte(s)
// yeet, _ := aql.ConvertJSONToAQL(&s3)
// fmt.Print(*yeet)
// }
......@@ -137,13 +137,24 @@ func ConvertJSONToAQL(jsonMsg *[]byte) (*string, error) {
//relations koppelen ze samen
//return statement
result := createAllNodesQuery(jsonStruct.Return.Entities, jsonStruct.Entities)
var result *string
if len(jsonStruct.Return.Relations) > 0 {
result = createEdgeQuery(jsonStruct, "e0")
} else {
result = createAllNodesQuery(jsonStruct.Return.Entities, jsonStruct.Entities)
}
return result, nil
}
// createAllNodesQuery creates node queries in AQL
func createAllNodesQuery(returnEntitiesIndices []int, entities []entityStruct) *string {
var result string
var ret string
var (
result string
ret string
)
ret += "RETURN { \n"
for _, entityIndex := range returnEntitiesIndices {
nodeID := fmt.Sprintf("n%s", strconv.Itoa(entityIndex))
......@@ -204,56 +215,155 @@ func createNodeQuery(node *entityStruct, name string) *string {
return &result
}
// func createEdgeQuery(relation *relationStruct, name string) *string {
// /*
// LET n0 = (
// FOR x IN female
// FILTER x.birth_year > 1997 AND
// x.name LIKE "%Alice"
// RETURN x
// )
// LET n1 = (
// FOR x IN male
// FILTER x.birth_year < 1997 AND
// x.name == "Bob"
// RETURN x
// )
// FOR a1 IN n0
// FOR Node, Edge, Path IN {MINDEPTH}..{MAXDEPTH} OUTBOUND a1 {RelationType}
// FILTER Node IN {bobs i guess?}
// FILTER contstrains...
// Edge.{Constraints.type} {MatchType + DataType} {Value}
// RETURN Node, a1, Edge
// "Relations": [
// {
// "RelationType": "relation",
// "NodeFrom": 0,
// "NodeTo": 1,
// "Depth": { "Min": 1, "Max": 1 },
// "Constraints": {
// "type": { "Value": "friend", "DataType": "text", "MatchType": "exact" }
// }
// }
// ]
/*
{
"Return": {
"Entities": [
0,
1
],
"Relations": [
0
]
},
"Entities": [
{
"Type": "airports",
"Constraints": [
{
"Attribute": "country",
"Value": "USA",
"DataType": "text",
"MatchType": "exact"
}
]
},
{
"Type": "airports",
"Constraints": [
{
"Attribute": "city",
"Value": "New York",
"DataType": "text",
"MatchType": "exact"
},
{
"Attribute": "vip",
"Value": "true",
"DataType": "bool",
"MatchType": "exact"
}
]
}
],
"Relations": [
{
"Type": "flights",
"Depth": {
"min": 1,
"max": 1
},
"EntityFrom": 0,
"EntityTo": 1,
"Constraints": [
{
"Attribute": "Month",
"Value": "1",
"DataType": "number",
"MatchType": "exact"
},
{
"Attribute": "Day",
"Value": "15",
"DataType": "number",
"MatchType": "exact"
}
]
}
]
}
LET n0 = (
FOR n IN airports
FILTER n.country == "USA"
RETURN n
)
// */
LET n1 = (
FOR n IN airports
FILTER n.city == "New York" AND
n.vip == true
RETURN n
)
LET e0 = (
FOR a IN n0
FOR v, e, p IN 1..1 OUTBOUND a flights
FILTER v in n1
FILTER p.edges[0].Month == 1 AND
p.edges[0].Day == 15
RETURN { vertices: p.vertices[*], edges: p.edges[*] }
)
RETURN e0
// firstForStatement := fmt.Sprintf("FOR a1 IN n%s \n", fmt.Sprint(relation.NodeFrom)) //a1 moet wss iets automatisch zijn?
// secondForStatement := fmt.Sprintf("\tFOR node, edges, path IN %s..%s OUTBOUND a1 %s\n", fmt.Sprint(relation.Depth.Min), fmt.Sprint(relation.Depth.Max), relation.RelationType)
// connectionFilterStatement := fmt.Sprintf("\t\tFILTER node IN n%s\n", fmt.Sprint(relation.NodeTo))
*/
// createEdgeQuery creates an aql query for relations
func createEdgeQuery(q *parsedJSON, name string) *string {
//Creation of n0 n1 let statements
query := ""
entityList := q.Entities
for i, x := range entityList {
query += "\n"
n := fmt.Sprintf("n%v", i)
query += *createNodeQuery(&x, n)
}
// filter := *createAllQueryConstraints(&relation.Constraints, "edges[*]")
query += *createLetStatementForRelation(&q.Relations[0], name) //DIT GAAN WE EVEN NEGEREN
// returnStatement := "\n\tRETURN { node, edges, path }\n"
//ZEER GEHARDCODED ivm demo
// finalReturn := "\nRETURN { "
// // Concatenate all the statements
// result := firstForStatement + secondForStatement + connectionFilterStatement + filter + returnStatement
// return &result
// for x := range q.Return.Entities {
// finalReturn += fmt.Sprintf("n%v, ", x)
// }
// finalReturn += fmt.Sprintf("e%v }", q.Return.Relations[0])
// query += finalReturn
query += "\nRETURN e0"
return &query
}
// createLetStatementForRelation creates the relation query
func createLetStatementForRelation(relation *relationStruct, name string) *string {
letStatement := fmt.Sprintf("\nLET %s = (\n", name)
upperForStatement := fmt.Sprintf("\tFOR x IN n%v \n", relation.EntityFrom)
edgeForStatement := fmt.Sprintf("\tFOR v, e, p IN %v..%v OUTBOUND x %s \n", relation.Depth.Min, relation.Depth.Max, relation.Type)
upperFilter := fmt.Sprintf("\tFILTER v IN n%v \n", relation.EntityTo)
// Generate all constraints as FILTER statements
first := true
nestedFilter := "\tFILTER "
for _, constraint := range relation.Constraints {
constraint := createEdgeQueryConstraint(&constraint)
if first {
nestedFilter += fmt.Sprintf("\t%s ", *constraint)
first = false
} else {
nestedFilter += fmt.Sprintf("AND\n\t\t%s", *constraint)
}
}
returnStatement := "\n\tLIMIT 100\n\tRETURN { vertices: p.vertices[*], edges: p.edges[*] }\n)"
// Concatenate all the statements
result := letStatement + upperForStatement + edgeForStatement + upperFilter + nestedFilter + returnStatement
return &result
}
// Constraint datatypes
// text MatchTypes: exact/contains/startswith/endswith
// number MatchTypes: GT/LT/EQ
......@@ -312,6 +422,60 @@ func createQueryConstraint(constraint *constraintStruct) *string {
return &line
}
// createEdgeQueryConstraint translates the constraints of an edge query to aql
func createEdgeQueryConstraint(constraint *constraintStruct) *string {
//FILTER x.{CONSTRAINT[0]} {{CONSTRAINT[0]}.MATCHTYPE} {CONSTRAINT[0].VALUE}
// name match value + dataType
var (
match string
value string
line string
)
//Wicked switches letsgo
switch constraint.DataType {
case "text":
value = fmt.Sprintf("\"%s\"", constraint.Value)
switch constraint.MatchType {
case "contains":
match = "IN"
case "startswith":
match = "LIKE"
value = fmt.Sprintf("\"%s%%\"", constraint.Value)
case "endswith":
match = "LIKE"
value = fmt.Sprintf("\"_%s\"", constraint.Value)
default: //exact
match = "=="
}
case "number":
value = constraint.Value
switch constraint.MatchType {
case "GT":
match = ">"
case "LT":
match = "<"
case "GET":
match = ">="
case "LET":
match = "<="
default: //EQ
match = "=="
}
default: /*bool*/
value = constraint.Value
switch constraint.MatchType {
case "NEQ":
match = "!="
default: //EQ
match = "=="
}
}
line = fmt.Sprintf("p.edges[*].%s ALL %s %s", constraint.Attribute, match, value)
return &line
}
// convertJSONToStruct takes the json as byte array and converts it to a struct
func convertJSONToStruct(jsonMsg *[]byte) (*parsedJSON, error) {
jsonStruct := parsedJSON{}
err := json.Unmarshal(*jsonMsg, &jsonStruct)
......
......
package aql
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
......@@ -10,3 +11,80 @@ func TestMock(t *testing.T) {
assert.True(t, true, true)
}
func TestHugeQuery(t *testing.T) {
s := `{
"Return": {
"Entities": [
0,
1
],
"Relations": [
0
]
},
"Entities": [
{
"Type": "airports",
"Constraints": [
{
"Attribute": "country",
"Value": "USA",
"DataType": "text",
"MatchType": "exact"
}
]
},
{
"Type": "airports",
"Constraints": [
{
"Attribute": "city",
"Value": "New York",
"DataType": "text",
"MatchType": "exact"
},
{
"Attribute": "vip",
"Value": "true",
"DataType": "bool",
"MatchType": "exact"
}
]
}
],
"Relations": [
{
"Type": "flights",
"Depth": {
"min": 1,
"max": 1
},
"EntityFrom": 0,
"EntityTo": 1,
"Constraints": [
{
"Attribute": "Month",
"Value": "1",
"DataType": "number",
"MatchType": "exact"
},
{
"Attribute": "Day",
"Value": "15",
"DataType": "number",
"MatchType": "exact"
}
]
}
]
}`
s3 := []byte(s)
j, _ := ConvertJSONToAQL(&s3)
fmt.Print(j)
assert.True(t, true, true)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment