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

Big rewrite of Query service

parent c046b638
No related branches found
No related tags found
No related merge requests found
package main
import (
"fmt"
"query-service/internal/adapters/brokeradapter"
"query-service/internal/drivers/brokerdriver"
"query-service/internal/drivers/keyvaluedriver"
"query-service/internal/usecases/consume"
"query-service/internal/usecases/convertquery"
"query-service/internal/usecases/produce"
"query-service/internal/usecases/request"
"query-service/pkg/logger"
)
func main() {
// logger.Start()
logger.Start()
// // MARK: Create relevant services
// redisService := keyvaluedriver.NewRedisDriver()
// MARK: Create relevant services
redisService := keyvaluedriver.NewRedisDriver()
// // MARK: Create alice RabbitMQ services
// brokerGateway := brokeradapter.CreateGateway()
// aliceBroker := brokerdriver.CreateAliceBroker(brokerGateway)
// MARK: Create alice RabbitMQ services
brokerGateway := brokeradapter.CreateGateway()
aliceBroker := brokerdriver.CreateAliceBroker(brokerGateway)
// // Instantiate an implementation of the produce UseCase
// produceService := produce.NewService(aliceBroker, redisService)
// Instantiate an implementation of the produce UseCase
produceService := produce.NewService(aliceBroker, redisService)
// // MARK: Create relevant services for consuming a message
// MARK: Create relevant services for consuming a message
convertQueryService := convertquery.NewService()
json := []byte(`{
"return": {
"entities": [
0
],
"relations": [
]
},
"Entities": [
{
"Type": "airports",
"Constraints": [
{
"Attribute": "city",
"Value": "New York",
"DataType": "text",
"MatchType": "exact"
}
]
}
],
"Relations": [
]
}
`)
res, _ := convertQueryService.ConvertQuery(&json)
fmt.Println(*res)
// requestSenderService := request.NewService()
// consumeService := consume.NewService(aliceBroker, produceService, convertQueryService, requestSenderService)
// // MARK: Start services
// redisService.Start()
// produceService.Start()
// go consumeService.Start()
// select {}
requestSenderService := request.NewService()
consumeService := consume.NewService(aliceBroker, produceService, convertQueryService, requestSenderService)
// MARK: Start services
redisService.Start()
produceService.Start()
go consumeService.Start()
select {}
}
......@@ -3,8 +3,6 @@ package convertquery
import (
"encoding/json"
"fmt"
"strconv"
"strings"
)
// Service is a model for the convertquery use case
......@@ -16,79 +14,6 @@ func NewService() *Service {
return &Service{}
}
/*
// Query format for exporting to JSON
export type JSONFormat = {
Return: {
Entities: number[];
Relations: number[];
};
Entities: Entity[];
Relations: Relation[];
};
type Entity = {
Type: string;
Constraints: Constraint[];
};
type Relation = {
Type: string;
EntityFrom: number;
EntityTo: number;
Depth: { min: number; max: number };
Constraints: Constraint[];
};
type Constraint = {
Attribute: string;
Value: string;
// Constraint datatypes
// text MatchTypes: exact/contains/startswith/endswith
// number MatchTypes: GT/LT/EQ
// bool MatchTypes: EQ/NEQ
DataType: string;
MatchType: string;
};
{
"Return":{
"Entities":[
0
],
"Relations":[
0
]
},
"Entities":[
{
"Type":"Airport",
"Constraints":[
{
"Attribute":"month",
"Value":"8",
"DataType":"text",
"MatchType":"exact"
}
]
}
],
"Relations":[
{
"Type":"Flight",
"Depth":{
"min":1,
"max":3
},
"EntityFrom":0,
"EntityTo":-1,
"Constraints":[
]
}
]
}
*/
// Constraint datatypes
// text MatchTypes: exact/contains/startswith/endswith
// number MatchTypes: GT/LT/EQ
......@@ -145,345 +70,17 @@ func (s *Service) ConvertQuery(jsonMsg *[]byte) (*string, error) {
//relations koppelen ze samen
//return statement
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
ret string
)
ret += "RETURN { \n"
for _, entityIndex := range returnEntitiesIndices {
nodeID := fmt.Sprintf("n%s", strconv.Itoa(entityIndex))
nodeQueryString := *createNodeQuery(&entities[entityIndex], nodeID)
ret += "\t" + nodeID + ":" + nodeID + ",\n"
result += fmt.Sprintf(" \n%s", nodeQueryString)
}
ret = strings.TrimSuffix(ret, ",\n")
ret += "\n}"
result += "\n" + ret
return &result
}
// createNodeQuery converts the node part of the json to a subquery
func createNodeQuery(node *entityStruct, name string) *string {
/*
LET alices = (
FOR x IN female
FILTER x.name == "Alice" AND x.birth_year > 1997
RETURN x
)
NAAR -->
LET {NAAM**} = (
FOR x IN {NODETYPE}
FILTER x.{CONSTRAINT[0]} {{CONSTRAINT[0]}.MATCHTYPE} {CONSTRAINT[0].VALUE}
AND x.{CONSTRAINT[1]} {{CONSTRAINT[1]}.MATCHTYPE} {CONSTRAINT[1].VALUE}
RETURN x
)
*/
letStatement := fmt.Sprintf("LET %s = (\n", name)
forStatement := fmt.Sprintf("\tFOR x IN %s \n", node.Type)
// Generate all constraints as FILTER statements
first := true
filter := "\tFILTER "
for _, constraint := range node.Constraints {
constraint := createQueryConstraint(&constraint)
if first {
filter += fmt.Sprintf("\t%s ", *constraint)
first = false
} else {
filter += fmt.Sprintf("AND\n\t\t%s", *constraint)
}
}
returnStatement := "\n\tRETURN x\n)"
// Concatenate all the statements
result := letStatement + forStatement + filter + returnStatement
return &result
}
/*
{
"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
*/
// 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)
}
query += *createLetStatementForRelation(&q.Relations[0], name) //DIT GAAN WE EVEN NEGEREN
// var result *string
// if len(jsonStruct.Return.Relations) > 0 {
// result = createEdgeQuery(jsonStruct, "e0")
// } else {
//ZEER GEHARDCODED ivm demo
// finalReturn := "\nRETURN { "
// for x := range q.Return.Entities {
// finalReturn += fmt.Sprintf("n%v, ", x)
// result = createAllNodesQuery(jsonStruct.Return.Entities, jsonStruct.Entities)
// }
// finalReturn += fmt.Sprintf("e%v }", q.Return.Relations[0])
// query += finalReturn
query += "\nRETURN {e0: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
// bool MatchTypes: EQ/NEQ
// createQueryConstraint creates a sinlge line of AQL filtering/constraint
func createQueryConstraint(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("x.%s %s %s", constraint.Attribute, match, value)
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
result := createQuery(jsonStruct)
return result, nil
}
// 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)
......@@ -495,145 +92,81 @@ func convertJSONToStruct(jsonMsg *[]byte) (*parsedJSON, error) {
return &jsonStruct, nil
}
/*
desired output
WITH male
FOR a1 IN female
FILTER a1.name == "Alice"
(FOR r1v,r1e,r1p IN 1..1 OUTBOUND a1 relation
FILTER r1v.name == "Bob")
OR
(FOR r2v,r2e,r2p IN 1..1 OUTBOUND a1 relation
FILTER r2v.name == "Martin" && r2v.hasdog == true)
// constraints for Bob
OR r1v.name == "Martin"
FILTER r1e.type == "married"
FOR r2v,r2e,r2p IN 1..1 OUTBOUND r1v relation
FILTER r2v.name == "Figo"
FILTER r2e.type == "has_dog"
RETURN {a1,r1v,r2v}
of
LET alices = (
FOR x IN female
FILTER x.name == "Alice"
RETURN x
)
LET bobs = (
FOR x IN male
FILTER x.name == "Bob"
RETURN x
)
LET alices = (
FOR alice IN alices
FOR r1v,r1e,r1p IN 1..1 OUTBOUND alice relation
FILTER r1v IN bobs AND r1e.type == "married"
RETURN {alice}
)
LET alices = (
FOR alice IN alices
FOR r1v,r1e,r1p IN 1..1 OUTBOUND alice relation
FILTER r1v IN marting AND r1e.type == "friend"
RETURN {alice}
)
FOR a2 IN male
FILTER a2.name == "Bob"
FOR r1v,r1e,r1p IN 1..1 OUTBOUND a1 relation
FILTER r1v._id == a2._id
FILTER r1e.type == "married"
RETURN {a1,a2,r1e}
*/
/*
{
"NodeType": "female",
"Constraints":
{
"name": { "Value": "Alice", "DataType": "text", "MatchType": "exact" },
"birth_year": { "Value": "1997", "DataType": "number", "MatchType": "GT" }
}
}
NAAR -->
LET alices = (
FOR x IN female
FILTER x.name == "Alice" AND x.birth_year > 1997
RETURN x
)
*/
//Nog een manier vinden om namen te syncen over de queries heen **
// func forGlory() *constraintStruct {
// var yeet constraintStruct
// yeet = constraintStruct{
// ConstraintName: "name",
// Value: "Alice",
// MatchType: "exact",
// DataType: "text",
// }
// return &yeet
// }
// func alsoForGlory() {
// con := forGlory()
// toPrint := createQueryConstraint(*con)
// fmt.Println(*toPrint)
// }
func createQuery(jsQuery *parsedJSON) *string {
// GROTE SIDENOTE:
// Vrij zeker dat een query waar alléén edges worden opgevraagd (#4)
// niet wordt gesupport door zowel de result parser als de frontend reciever
// jsonQuery := *jsQuery
jsonQuery := *jsQuery
// TODO:
// NODES
// EDGES (als ze er zijn)
// RETURN STATEMENT
r := "hoi"
return &r
var ret string
for k, v := range jsonQuery.Entities {
name := fmt.Sprintf("n%v", k)
ret += *createNodeLet(&v, &name)
}
var onlyEdge bool
if len(jsonQuery.Entities) == 0 {
onlyEdge = true
}
for k, v := range jsonQuery.Relations {
name := fmt.Sprintf("e%v", k)
ret += *createEdgeLet(&v, &name, onlyEdge)
}
finalReturn := "RETURN {"
// If there is an edge you can only return the edge, because the nodes are included.
// If there are no edges, return the nodes
if l := len(jsonQuery.Relations); l > 0 {
for k := range jsonQuery.Relations {
finalReturn += fmt.Sprintf("e%v:e%v", k, k)
if k < l-1 {
finalReturn += ","
}
}
} else {
for k := range jsonQuery.Entities {
finalReturn += fmt.Sprintf("n%v:n%v", k, k)
if k < l-1 {
finalReturn += ","
}
}
}
finalReturn += "}"
ret += finalReturn
return &ret
}
func createNodeLet(node *entityStruct, name *string) *string {
header := fmt.Sprintf("LET %s = (\n\tFOR x IN %s \n", name, node.Type)
footer := "\nLIMIT 100\n\tRETURN x\n)\n"
header := fmt.Sprintf("LET %v = (\n\tFOR x IN %v \n", *name, node.Type)
footer := "\tRETURN x\n)\n"
constraints := *createConstraints(&node.Constraints, false)
constraints := *createConstraints(&node.Constraints, false, false)
ret := header + constraints + footer
return &ret
}
func createEdgeLet(edge *relationStruct, name *string) *string {
func createEdgeLet(edge *relationStruct, name *string, onlyEdge bool) *string {
var (
header string
forEdge string
forSecondNode string
)
footer := "\n\tLIMIT 100\n\tRETURN { vertices: p.vertices[*], edges: p.edges[*] }\n)"
footer := "\n\tLIMIT 100\n\tRETURN { vertices: p.vertices[*], edges: p.edges[*] }\n)\n"
// WICKED SWITCHES LETSAGO
if edge.EntityFrom != -1 {
// # 1 (2)
header = fmt.Sprintf("LET %s = (\n\tFOR x IN n%s \n", name, edge.EntityFrom)
header = fmt.Sprintf("LET %v = (\n\tFOR x IN n%v \n", *name, edge.EntityFrom)
forEdge = fmt.Sprintf("\tFOR v, e, p IN %v..%v OUTBOUND x %s \n", edge.Depth.Min, edge.Depth.Max, edge.Type)
if edge.EntityTo != -1 {
// # 2
......@@ -642,16 +175,18 @@ func createEdgeLet(edge *relationStruct, name *string) *string {
} else {
if edge.EntityTo != -1 {
// # 3
header = fmt.Sprintf("LET %s = (\n\tFOR x IN n%v \n", name, edge.EntityTo)
header = fmt.Sprintf("LET %v = (\n\tFOR x IN n%v \n", *name, edge.EntityTo)
forEdge = fmt.Sprintf("\tFOR v, e, p IN %v..%v INBOUND x %s \n", edge.Depth.Min, edge.Depth.Max, edge.Type)
} else {
// # 4
header = fmt.Sprintf("LET %s = (\n\tFOR x IN %v \n", name, edge.Type)
footer = "\n\tLIMIT 100\n\tRETURN x\n)"
header = fmt.Sprintf("LET %v = (\n\tFOR x IN %v \n", *name, edge.Type)
footer = "\tLIMIT 100\n\tRETURN x\n)\n"
}
}
ret := header + forEdge + forSecondNode + footer
constraints := *createConstraints(&edge.Constraints, true, onlyEdge)
ret := header + forEdge + forSecondNode + constraints + footer
return &ret
}
......
......@@ -2,7 +2,7 @@ package convertquery
import "fmt"
func createConstraints(constraints *[]constraintStruct, isRelation bool) *string {
func createConstraints(constraints *[]constraintStruct, isRelation bool, onlyEdge bool) *string {
s := ""
if len(*constraints) == 0 {
return &s
......@@ -11,14 +11,14 @@ func createConstraints(constraints *[]constraintStruct, isRelation bool) *string
newLineStatement := "\tFILTER"
for _, v := range *constraints {
s += fmt.Sprintf("%v %v \n", newLineStatement, *createConstraintLine(&v, isRelation))
s += fmt.Sprintf("%v %v \n", newLineStatement, *createConstraintLine(&v, isRelation, onlyEdge))
newLineStatement = "\tAND"
}
return &s
}
func createConstraintLine(constraint *constraintStruct, isRelation bool) *string {
func createConstraintLine(constraint *constraintStruct, isRelation bool, onlyEdge bool) *string {
var (
match string
value string
......@@ -65,7 +65,7 @@ func createConstraintLine(constraint *constraintStruct, isRelation bool) *string
}
}
if isRelation {
if isRelation && !onlyEdge {
line = fmt.Sprintf("p.edges[*].%s ALL %s %s", constraint.Attribute, match, value)
} else {
line = fmt.Sprintf("x.%s %s %s", constraint.Attribute, match, value)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment