Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
Q
query-conversion
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
GraphPolaris
query-conversion
Commits
c4fd8401
Commit
c4fd8401
authored
3 years ago
by
Fjodor
Browse files
Options
Downloads
Patches
Plain Diff
Added relations, relations constraints, removed modifiers @Lorenzo
parent
4ad036ac
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
cypher/convertQuery.go
+103
-170
103 additions, 170 deletions
cypher/convertQuery.go
cypher/createConstraints.go
+5
-8
5 additions, 8 deletions
cypher/createConstraints.go
main/main.go
+59
-4
59 additions, 4 deletions
main/main.go
with
167 additions
and
182 deletions
cypher/convertQuery.go
+
103
−
170
View file @
c4fd8401
...
@@ -3,6 +3,7 @@ package cypher
...
@@ -3,6 +3,7 @@ package cypher
import
(
import
(
"errors"
"errors"
"fmt"
"fmt"
"strings"
"git.science.uu.nl/datastrophe/query-conversion/entity"
"git.science.uu.nl/datastrophe/query-conversion/entity"
)
)
...
@@ -46,6 +47,23 @@ func (s *Service) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string, er
...
@@ -46,6 +47,23 @@ func (s *Service) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string, er
return
result
,
nil
return
result
,
nil
}
}
func
sliceContains
(
s
[]
int
,
e
int
)
bool
{
for
_
,
a
:=
range
s
{
if
a
==
e
{
return
true
}
}
return
false
}
/*TrimSuffix trims the final character of a string */
func
TrimSuffix
(
s
,
suffix
string
)
string
{
if
strings
.
HasSuffix
(
s
,
suffix
)
{
s
=
s
[
:
len
(
s
)
-
len
(
suffix
)]
}
return
s
}
/* createQuery generates a query based on the json file provided
/* createQuery generates a query based on the json file provided
Parameters: jsonQuery is a parsedJSON struct holding all the data needed to form a query
Parameters: jsonQuery is a parsedJSON struct holding all the data needed to form a query
...
@@ -64,42 +82,84 @@ func createQuery(JSONQuery *entity.IncomingQueryJSON) *string {
...
@@ -64,42 +82,84 @@ func createQuery(JSONQuery *entity.IncomingQueryJSON) *string {
nodesToReturn
[]
string
nodesToReturn
[]
string
nodeUnion
string
nodeUnion
string
relationUnion
string
relationUnion
string
queryList
[][][]
int
entityList
[]
int
ret
string
)
)
// Loop over all relations
ret
:=
""
for
i
,
relation
:=
range
JSONQuery
.
Relations
{
for
i
,
relation
:=
range
JSONQuery
.
Relations
{
var
contains
bool
contains
=
false
for
j
:=
range
queryList
{
if
sliceContains
(
queryList
[
j
][
0
],
relation
.
EntityFrom
)
||
sliceContains
(
queryList
[
j
][
0
],
relation
.
EntityTo
)
{
if
!
sliceContains
(
queryList
[
j
][
0
],
relation
.
EntityFrom
)
{
queryList
[
j
][
0
]
=
append
(
queryList
[
j
][
0
],
relation
.
EntityFrom
)
entityList
=
append
(
entityList
,
relation
.
EntityFrom
)
}
if
!
sliceContains
(
queryList
[
j
][
0
],
relation
.
EntityTo
)
{
queryList
[
j
][
0
]
=
append
(
queryList
[
j
][
0
],
relation
.
EntityTo
)
entityList
=
append
(
entityList
,
relation
.
EntityTo
)
}
queryList
[
j
][
1
]
=
append
(
queryList
[
j
][
1
],
i
)
contains
=
true
}
}
if
!
contains
{
queryList
=
append
(
queryList
,
[][]
int
{{
relation
.
EntityFrom
,
relation
.
EntityTo
},
{
i
}})
}
}
relationName
:=
fmt
.
Sprintf
(
"r%v"
,
i
)
for
i
:=
range
queryList
{
//reset variables for the next query
if
relation
.
EntityFrom
>=
0
{
nodeUnion
=
""
// if there is a from-node
relationUnion
=
""
// create the let for this node
relationsToReturn
=
[]
string
{}
fromName
:=
fmt
.
Sprintf
(
"n%v"
,
relation
.
EntityFrom
)
for
j
,
relationID
:=
range
queryList
[
i
][
1
]
{
relationName
:=
fmt
.
Sprintf
(
"r%v"
,
j
)
relation
:=
JSONQuery
.
Relations
[
relationID
]
pathName
:=
fmt
.
Sprintf
(
"p%v"
,
j
)
relationsToReturn
=
append
(
relationsToReturn
,
pathName
)
if
relation
.
EntityFrom
>=
0
{
// if there is a from-node
// create the let for this node
fromName
:=
fmt
.
Sprintf
(
"n%v"
,
relation
.
EntityFrom
)
ret
+=
*
createNodeMatch
(
&
JSONQuery
.
Entities
[
relation
.
EntityFrom
],
&
fromName
)
ret
+=
*
createRelationMatch
(
&
relation
,
relationName
,
pathName
,
&
JSONQuery
.
Entities
,
JSONQuery
.
Limit
,
true
)
}
else
if
relation
.
EntityTo
>=
0
{
// if there is only a to-node
toName
:=
fmt
.
Sprintf
(
"n%v"
,
relation
.
EntityTo
)
ret
+=
*
createNodeMatch
(
&
JSONQuery
.
Entities
[
relation
.
EntityTo
],
&
toName
)
ret
+=
*
createRelationMatch
(
&
relation
,
relationName
,
pathName
,
&
JSONQuery
.
Entities
,
JSONQuery
.
Limit
,
false
)
// Add this relation to the list
}
else
{
fmt
.
Println
(
"Relation-only queries are currently not supported"
)
continue
}
}
ret
+=
*
createNodeLet
(
&
JSONQuery
.
Entities
[
relation
.
EntityFrom
],
&
fromName
)
// Create UNION statements that create unique lists of all the nodes and relations
ret
+=
*
createRelationLetWithFromEntity
(
&
relation
,
relationName
,
&
JSONQuery
.
Entities
,
JSONQuery
.
Limit
)
// Thus removing all duplicates
}
else
if
relation
.
EntityTo
>=
0
{
nodeUnion
=
"RETURN "
// if there is only a to-node
toName
:=
fmt
.
Sprintf
(
"n%v"
,
relation
.
EntityTo
)
ret
+=
*
createNodeLet
(
&
JSONQuery
.
Entities
[
relation
.
EntityTo
],
&
toName
)
for
_
,
entityID
:=
range
queryList
[
i
][
0
]
{
if
sliceContains
(
JSONQuery
.
Return
.
Entities
,
entityID
)
{
nodeUnion
+=
fmt
.
Sprintf
(
"n%v,"
,
entityID
)
}
}
ret
+=
*
createRelationLetWithOnlyToEntity
(
&
relation
,
relationName
,
&
JSONQuery
.
Entities
,
JSONQuery
.
Limit
)
for
_
,
relation
:=
range
relationsToReturn
{
// Add this relation to the list
relationUnion
+=
fmt
.
Sprintf
(
"%v,"
,
relation
)
}
else
{
fmt
.
Println
(
"Relation-only queries are currently not supported"
)
continue
}
}
// Add this relation to the list
relationUnion
=
TrimSuffix
(
relationUnion
,
","
)
re
lationsToReturn
=
append
(
relationsToReturn
,
relationName
)
re
t
+=
nodeUnion
+
relationUnion
+
";
\n
"
}
}
// Add node let statements for nodes that are not yet returned
// Create a set from all the entity-from's and entity-to's, to check if they are returned
nodeSet
:=
make
(
map
[
int
]
bool
)
nodeSet
:=
make
(
map
[
int
]
bool
)
for
_
,
relation
:=
range
JSONQuery
.
Relations
{
for
_
,
relation
:=
range
JSONQuery
.
Relations
{
nodeSet
[
relation
.
EntityFrom
]
=
true
nodeSet
[
relation
.
EntityFrom
]
=
true
...
@@ -111,103 +171,13 @@ func createQuery(JSONQuery *entity.IncomingQueryJSON) *string {
...
@@ -111,103 +171,13 @@ func createQuery(JSONQuery *entity.IncomingQueryJSON) *string {
if
!
nodeSet
[
entityIndex
]
{
if
!
nodeSet
[
entityIndex
]
{
// If not, return this node
// If not, return this node
name
:=
fmt
.
Sprintf
(
"n%v"
,
entityIndex
)
name
:=
fmt
.
Sprintf
(
"n%v"
,
entityIndex
)
ret
+=
*
createNode
Let
(
&
JSONQuery
.
Entities
[
entityIndex
],
&
name
)
ret
+=
*
createNode
Match
(
&
JSONQuery
.
Entities
[
entityIndex
],
&
name
)
// Add this node to the list
// Add this node to the list
nodesToReturn
=
append
(
nodesToReturn
,
name
)
nodesToReturn
=
append
(
nodesToReturn
,
name
)
}
}
}
}
//If there are modifiers within the query, we run a different set of checks which focus on quantifiable aspects
if
len
(
JSONQuery
.
Modifiers
)
>
0
{
modifier
:=
JSONQuery
.
Modifiers
[
0
]
// There is a distinction between (relations and entities) and (relations or entities)
if
len
(
JSONQuery
.
Return
.
Relations
)
>
0
&&
len
(
JSONQuery
.
Return
.
Entities
)
>
0
{
var
pathDistinction
string
// .vertices or .edges
// Select the correct addition to the return of r0[**]
if
modifier
.
SelectedType
==
"entity"
{
// ASSUMING THERE IS ONLY 1 RELATION
if
JSONQuery
.
Relations
[
0
]
.
EntityFrom
==
modifier
.
ID
{
pathDistinction
=
fmt
.
Sprintf
(
".vertices[%v]"
,
JSONQuery
.
Relations
[
0
]
.
Depth
.
Min
-
1
)
}
else
{
pathDistinction
=
fmt
.
Sprintf
(
".vertices[%v]"
,
JSONQuery
.
Relations
[
0
]
.
Depth
.
Max
)
}
}
else
{
pathDistinction
=
".edges[**]"
}
// Getting the attribute if there is one
if
modifier
.
AttributeIndex
!=
-
1
{
if
modifier
.
SelectedType
==
"entity"
{
pathDistinction
+=
fmt
.
Sprintf
(
".%v"
,
JSONQuery
.
Entities
[
modifier
.
ID
]
.
Constraints
[
modifier
.
AttributeIndex
]
.
Attribute
)
}
else
{
pathDistinction
+=
fmt
.
Sprintf
(
".%v"
,
JSONQuery
.
Relations
[
modifier
.
ID
]
.
Constraints
[
modifier
.
AttributeIndex
]
.
Attribute
)
}
}
// If count is used it has to be replaced with Length + unique else use the modifier type
if
modifier
.
Type
==
"COUNT"
{
ret
+=
fmt
.
Sprintf
(
"RETURN LENGTH (unique(r0[*]%v))"
,
pathDistinction
)
}
else
{
ret
+=
fmt
.
Sprintf
(
"RETURN %v (r0[*]%v)"
,
modifier
.
Type
,
pathDistinction
)
}
}
else
{
// Check if the modifier is on an attribute
if
modifier
.
AttributeIndex
==
-
1
{
ret
+=
fmt
.
Sprintf
(
"RETURN LENGTH (n%v)"
,
modifier
.
ID
)
}
else
{
var
attribute
string
// Selecting the right attribute from either the entity constraint or relation constraint
if
modifier
.
SelectedType
==
"entity"
{
attribute
=
JSONQuery
.
Entities
[
modifier
.
ID
]
.
Constraints
[
modifier
.
AttributeIndex
]
.
Attribute
}
else
{
attribute
=
JSONQuery
.
Relations
[
modifier
.
ID
]
.
Constraints
[
modifier
.
AttributeIndex
]
.
Attribute
}
// If count is used it has to be replaced with Length + unique else use the modifier type
if
modifier
.
Type
==
"COUNT"
{
ret
+=
fmt
.
Sprintf
(
"RETURN LENGTH (unique(n%v[*].%v))"
,
modifier
.
ID
,
attribute
)
}
else
{
ret
+=
fmt
.
Sprintf
(
"RETURN %v (n%v[*].%v)"
,
modifier
.
Type
,
modifier
.
ID
,
attribute
)
}
}
}
}
else
{
// Create UNION statements that create unique lists of all the nodes and relations
// Thus removing all duplicates
nodeUnion
=
"
\n
RETURN "
for
_
,
node
:=
range
nodesToReturn
{
nodeUnion
+=
fmt
.
Sprintf
(
"%v,"
,
node
)
}
// RETURN n0, n1, n2, nn, r0, r1, r2, r3, rn
relationUnion
=
"LET edges = first(RETURN UNION_DISTINCT("
for
_
,
relation
:=
range
relationsToReturn
{
relationUnion
+=
fmt
.
Sprintf
(
"flatten(%v[**].edges), "
,
relation
)
}
relationUnion
+=
"[],[]))
\n
"
ret
+=
nodeUnion
+
relationUnion
ret
+=
"RETURN {
\"
vertices
\"
:nodes,
\"
edges
\"
:edges }"
}
return
&
ret
return
&
ret
}
}
...
@@ -217,9 +187,9 @@ name is the autogenerated name of the node consisting of "n" + the index of the
...
@@ -217,9 +187,9 @@ name is the autogenerated name of the node consisting of "n" + the index of the
Return: a string containing a single LET-statement in AQL
Return: a string containing a single LET-statement in AQL
*/
*/
func
createNode
Let
(
node
*
entity
.
QueryEntityStruct
,
name
*
string
)
*
string
{
func
createNode
Match
(
node
*
entity
.
QueryEntityStruct
,
name
*
string
)
*
string
{
header
:=
fmt
.
Sprintf
(
"MATCH (%v
:
%v)
\n
"
,
*
name
,
node
.
Type
)
header
:=
fmt
.
Sprintf
(
"MATCH (%v
:
%v)
\n
"
,
*
name
,
node
.
Type
)
constraints
:=
*
createConstraintStatements
(
&
node
.
Constraints
,
*
name
,
false
)
constraints
:=
*
createConstraintStatements
(
&
node
.
Constraints
,
*
name
)
ret
:=
header
+
constraints
ret
:=
header
+
constraints
return
&
ret
return
&
ret
}
}
...
@@ -231,63 +201,26 @@ entities is a list of entityStructs that are needed to form the relation LET-sta
...
@@ -231,63 +201,26 @@ entities is a list of entityStructs that are needed to form the relation LET-sta
Return: a string containing a single LET-statement in AQL
Return: a string containing a single LET-statement in AQL
*/
*/
func
createRelationLetWithFromEntity
(
relation
*
entity
.
QueryRelationStruct
,
name
string
,
entities
*
[]
entity
.
QueryEntityStruct
,
limit
int
)
*
string
{
func
createRelationMatch
(
relation
*
entity
.
QueryRelationStruct
,
relationName
string
,
pathName
string
,
entities
*
[]
entity
.
QueryEntityStruct
,
limit
int
,
outbound
bool
)
*
string
{
header
:=
fmt
.
Sprintf
(
"LET %v = (
\n\t
FOR x IN n%v
\n
"
,
name
,
relation
.
EntityFrom
)
relationReturn
:=
""
forStatement
:=
fmt
.
Sprintf
(
"
\t
FOR v, e, p IN %v..%v OUTBOUND x %s
\n
"
,
relation
.
Depth
.
Min
,
relation
.
Depth
.
Max
,
relation
.
Type
)
var
relationBounds
int
if
outbound
{
// Guarantees that there is no path returned with a duplicate edge
relationReturn
=
fmt
.
Sprintf
(
"MATCH %v = (n%v)-[%v:%v*%v..%v]->("
,
pathName
,
relation
.
EntityFrom
,
relationName
,
relation
.
Type
,
relation
.
Depth
.
Min
,
relation
.
Depth
.
Max
)
// This way there are no cycle paths possible, TODO: more research about this needed
relationBounds
=
relation
.
EntityTo
optionStmtn
:=
"
\t
OPTIONS { uniqueEdges:
\"
path
\"
}
\n
"
vFilterStmnt
:=
""
if
relation
.
EntityTo
!=
-
1
{
// If there is a to-node, generate the filter statement
toConstraints
:=
(
*
entities
)[
relation
.
EntityTo
]
.
Constraints
vFilterStmnt
+=
*
createConstraintStatements
(
&
toConstraints
,
"v"
,
false
)
// Add a WITH statement if the collection of entityTo is not yet included
if
(
*
entities
)[(
*
relation
)
.
EntityFrom
]
.
Type
!=
(
*
entities
)[(
*
relation
)
.
EntityTo
]
.
Type
{
header
=
fmt
.
Sprintf
(
"WITH %v
\n
%v"
,
(
*
entities
)[(
*
relation
)
.
EntityTo
]
.
Type
,
header
)
}
}
relationFilterStmnt
:=
*
createConstraintStatements
(
&
relation
.
Constraints
,
"p"
,
true
)
// Dont use a limit on quantifing queries
}
else
{
footer
:=
""
relationReturn
=
fmt
.
Sprintf
(
"MATCH %v = (n%v)-[%v:%v*%v..%v]->("
,
pathName
,
relation
.
EntityTo
,
relationName
,
relation
.
Type
,
relation
.
Depth
.
Min
,
relation
.
Depth
.
Max
)
if
limit
!=
-
1
{
relationBounds
=
relation
.
EntityFrom
footer
+=
fmt
.
Sprintf
(
"
\t
LIMIT %v
\n
"
,
limit
)
}
}
footer
+=
"RETURN DISTINCT p )
\n
"
ret
:=
header
+
forStatement
+
optionStmtn
+
vFilterStmnt
+
relationFilterStmnt
+
footer
return
&
ret
}
/* createRelationLetWithOnlyToEntity generates a 'LET' statement for relations with only an 'EntityTo' property
Parameters: relation is a relation struct containing the information of a single relation,
name is the autogenerated name of the node consisting of "r" + the index of the relation,
entities is a list of entityStructs that are needed to form the relation LET-statement
Return: a string containing a single LET-statement in AQL
if
relationBounds
!=
-
1
{
*/
relationReturn
+=
fmt
.
Sprintf
(
"n%v"
,
relationBounds
)
func
createRelationLetWithOnlyToEntity
(
relation
*
entity
.
QueryRelationStruct
,
name
string
,
entities
*
[]
entity
.
QueryEntityStruct
,
limit
int
)
*
string
{
}
header
:=
fmt
.
Sprintf
(
"LET %v = (
\n\t
FOR x IN n%v
\n
"
,
name
,
relation
.
EntityTo
)
relationReturn
+=
")"
forStatement
:=
fmt
.
Sprintf
(
"
\t
FOR v, e, p IN %v..%v INBOUND x %s
\n
"
,
relation
.
Depth
.
Min
,
relation
.
Depth
.
Max
,
relation
.
Type
)
// Guarantees that there is no path returned with a duplicate edge
// This way there are no cycle paths possible, TODO: more research about this needed
optionStmtn
:=
"
\t
OPTIONS { uniqueEdges:
\"
path
\"
}
\n
"
relationFilterStmnt
:=
*
createConstraintStatements
(
&
relation
.
Constraints
,
"p"
,
tru
e
)
constraintReturn
:=
*
createConstraintStatements
(
&
relation
.
Constraints
,
relationNam
e
)
// Dont use a limit on quantifing queries
ret
:=
relationReturn
+
"
\n
"
+
constraintReturn
footer
:=
""
if
limit
!=
-
1
{
footer
+=
fmt
.
Sprintf
(
"
\t
LIMIT %v
\n
"
,
limit
)
}
footer
+=
"RETURN DISTINCT p )
\n
"
ret
:=
header
+
forStatement
+
optionStmtn
+
relationFilterStmnt
+
footer
return
&
ret
return
&
ret
}
}
This diff is collapsed.
Click to expand it.
cypher/createConstraints.go
+
5
−
8
View file @
c4fd8401
...
@@ -13,7 +13,7 @@ isRelation is a boolean specifying if this constraint comes from a node or relat
...
@@ -13,7 +13,7 @@ isRelation is a boolean specifying if this constraint comes from a node or relat
Return: a string containing a FILTER-statement with all the constraints
Return: a string containing a FILTER-statement with all the constraints
*/
*/
func
createConstraintStatements
(
constraints
*
[]
entity
.
QueryConstraintStruct
,
name
string
,
isRelation
bool
)
*
string
{
func
createConstraintStatements
(
constraints
*
[]
entity
.
QueryConstraintStruct
,
name
string
)
*
string
{
s
:=
""
s
:=
""
if
len
(
*
constraints
)
==
0
{
if
len
(
*
constraints
)
==
0
{
return
&
s
return
&
s
...
@@ -22,7 +22,7 @@ func createConstraintStatements(constraints *[]entity.QueryConstraintStruct, nam
...
@@ -22,7 +22,7 @@ func createConstraintStatements(constraints *[]entity.QueryConstraintStruct, nam
newLineStatement
:=
"
\t
WHERE"
newLineStatement
:=
"
\t
WHERE"
for
_
,
v
:=
range
*
constraints
{
for
_
,
v
:=
range
*
constraints
{
s
+=
fmt
.
Sprintf
(
"%v
%v
\n
"
,
newLineStatement
,
*
createConstraintBoolExpression
(
&
v
,
name
,
isRelation
))
s
+=
fmt
.
Sprintf
(
"%v%v
\n
"
,
newLineStatement
,
*
createConstraintBoolExpression
(
&
v
,
name
))
newLineStatement
=
"
\t
AND"
newLineStatement
=
"
\t
AND"
}
}
...
@@ -38,7 +38,7 @@ isRelation is a boolean specifying if this constraint comes from a node or relat
...
@@ -38,7 +38,7 @@ isRelation is a boolean specifying if this constraint comes from a node or relat
Return: a string containing an boolean expression of a single constraint
Return: a string containing an boolean expression of a single constraint
*/
*/
func
createConstraintBoolExpression
(
constraint
*
entity
.
QueryConstraintStruct
,
name
string
,
isRelation
bool
)
*
string
{
func
createConstraintBoolExpression
(
constraint
*
entity
.
QueryConstraintStruct
,
name
string
)
*
string
{
var
(
var
(
match
string
match
string
value
string
value
string
...
@@ -95,10 +95,7 @@ func createConstraintBoolExpression(constraint *entity.QueryConstraintStruct, na
...
@@ -95,10 +95,7 @@ func createConstraintBoolExpression(constraint *entity.QueryConstraintStruct, na
}
}
}
}
if
isRelation
{
line
=
fmt
.
Sprintf
(
"%s %s.%s %s %s"
,
neq
,
name
,
constraint
.
Attribute
,
match
,
value
)
line
=
fmt
.
Sprintf
(
"%s.edges[*].%s ALL %s %s"
,
name
,
constraint
.
Attribute
,
match
,
value
)
}
else
{
line
=
fmt
.
Sprintf
(
"%s %s.%s %s %s"
,
neq
,
name
,
constraint
.
Attribute
,
match
,
value
)
}
return
&
line
return
&
line
}
}
This diff is collapsed.
Click to expand it.
main/main.go
+
59
−
4
View file @
c4fd8401
...
@@ -14,11 +14,38 @@ func main() {
...
@@ -14,11 +14,38 @@ func main() {
js
:=
[]
byte
(
`{
js
:=
[]
byte
(
`{
"return": {
"return": {
"entities": [
"entities": [
0
0,
1,
2
],
],
"relations": []
"relations": [
0,
1
]
},
},
"entities": [
"entities": [
{
"type": "airports",
"constraints": [
{
"attribute": "city",
"value": "New York",
"dataType": "text",
"matchType": "exact"
}
]
},
{
"type": "airports",
"constraints": [
{
"attribute": "city",
"value": "San Francisco",
"dataType": "text",
"matchType": "exact"
}
]
},
{
{
"type": "airports",
"type": "airports",
"constraints": [
"constraints": [
...
@@ -31,9 +58,37 @@ func main() {
...
@@ -31,9 +58,37 @@ func main() {
]
]
}
}
],
],
"relations": [],
"relations": [
{
"type": "flights",
"depth": {
"min": 1,
"max": 3
},
"entityFrom": 2,
"entityTo": 1,
"constraints": [
{
"attribute": "Day",
"value": "15",
"dataType": "number",
"matchType": "EQ"
}
]
},
{
"type": "flights",
"depth": {
"min": 1,
"max": 1
},
"entityFrom": 0,
"entityTo": -1,
"constraints": []
}
],
"limit": 5000
"limit": 5000
}`
)
}`
)
var
inc
entity
.
IncomingQueryJSON
var
inc
entity
.
IncomingQueryJSON
json
.
Unmarshal
(
js
,
&
inc
)
json
.
Unmarshal
(
js
,
&
inc
)
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment