package aql import ( "errors" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestEmptyQueryConversion(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [], "relations": [] }, "entities": [], "relations": [], "limit": 5000 }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := ` LET nodes = first(RETURN UNION_DISTINCT([],[])) LET edges = first(RETURN UNION_DISTINCT([],[])) RETURN {"vertices":nodes, "edges":edges }` assert.Equal(t, correctConvertedResult, *convertedResult) } func TestEntityOneAttributeQuery(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0 ], "relations": [] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "state", "value": "HI", "dataType": "text", "matchType": "exact" } ] } ], "relations": [], "limit": 5000 }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))LET edges = first(RETURN UNION_DISTINCT([],[]))RETURN {"vertices":nodes, "edges":edges }` cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") assert.Equal(t, correctConvertedResult, cleanedResult) } func TestRelationWithConstraint(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0 ], "relations": [ 0 ] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "state", "value": "HI", "dataType": "text", "matchType": "exact" } ] } ], "relations": [ { "type": "flights", "depth": { "min": 1, "max": 1 }, "entityFrom": 0, "entityTo": -1, "constraints": [ { "attribute": "Day", "value": "15", "dataType": "number", "matchType": "EQ" } ] } ], "limit": 5000 }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") assert.Equal(t, correctConvertedResult, cleanedResult) } func TestModifierCountEntity(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0 ], "relations": [] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "state", "value": "HI", "dataType": "text", "matchType": "exact" } ] } ], "relations": [], "limit": 5000, "modifiers": [ { "type": "COUNT", "selectedType": "entity", "id": 0, "attributeIndex": -1 } ] }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)RETURN LENGTH (n0)` cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") assert.Equal(t, correctConvertedResult, cleanedResult) } func TestModifierCountEntityAttribute(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0 ], "relations": [] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "state", "value": "HI", "dataType": "text", "matchType": "exact" } ] } ], "relations": [], "limit": 5000, "modifiers": [ { "type": "SUM", "selectedType": "entity", "id": 0, "attributeIndex": 0 } ] }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)RETURN SUM (n0[*].state)` cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") assert.Equal(t, correctConvertedResult, cleanedResult) } func TestModifierCountRelation(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0 ], "relations": [ 0 ] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "state", "value": "HI", "dataType": "text", "matchType": "exact" } ] } ], "relations": [ { "type": "flights", "depth": { "min": 1, "max": 1 }, "entityFrom": 0, "entityTo": -1, "constraints": [ { "attribute": "Day", "value": "15", "dataType": "number", "matchType": "EQ" } ] } ], "limit": 5000, "modifiers": [ { "type": "COUNT", "selectedType": "relation", "id": 0, "attributeIndex": -1 } ] }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 RETURN DISTINCT p )RETURN LENGTH (unique(r0[*].edges[**]))` cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") assert.Equal(t, correctConvertedResult, cleanedResult) } func TestModifierCountRelationAttribute(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0 ], "relations": [ 0 ] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "state", "value": "HI", "dataType": "text", "matchType": "exact" } ] } ], "relations": [ { "type": "flights", "depth": { "min": 1, "max": 1 }, "entityFrom": 0, "entityTo": -1, "constraints": [ { "attribute": "Day", "value": "15", "dataType": "number", "matchType": "EQ" } ] } ], "limit": 5000, "modifiers": [ { "type": "AVG", "selectedType": "relation", "id": 0, "attributeIndex": 0 } ] }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 RETURN DISTINCT p )RETURN AVG (r0[*].edges[**].Day)` cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") assert.Equal(t, correctConvertedResult, cleanedResult) } func TestRelationWithInOutConstraint(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0, 1 ], "relations": [ 0 ] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "city", "value": "San Francisco", "dataType": "text", "matchType": "exact" } ] }, { "type": "airports", "constraints": [ { "attribute": "state", "value": "HI", "dataType": "text", "matchType": "exact" } ] } ], "relations": [ { "type": "flights", "depth": { "min": 1, "max": 3 }, "entityFrom": 1, "entityTo": 0, "constraints": [ { "attribute": "Day", "value": "15", "dataType": "number", "matchType": "EQ" } ] } ], "limit": 5000 }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := `LET n1 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n1 FOR v, e, p IN 1..3 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER v.city == "San Francisco" FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") assert.Equal(t, correctConvertedResult, cleanedResult) } func TestTwoRelations(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0, 1, 2 ], "relations": [ 0, 1 ] }, "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", "constraints": [ { "attribute": "state", "value": "HI", "dataType": "text", "matchType": "exact" } ] } ], "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 }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := `LET n2 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n2 FOR v, e, p IN 1..3 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER v.city == "San Francisco" FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET n0 = (FOR x IN airports FILTER x.city == "New York" RETURN x)LET r1 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), flatten(r1[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), flatten(r1[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") assert.Equal(t, correctConvertedResult, cleanedResult) } func TestRelationWithOnlyToNode(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0 ], "relations": [ 0 ] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "city", "value": "San Francisco", "dataType": "text", "matchType": "exact" } ] } ], "relations": [ { "type": "flights", "depth": { "min": 1, "max": 1 }, "entityFrom": -1, "entityTo": 0, "constraints": [] } ], "limit": 5000 }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.city == "San Francisco" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 INBOUND x flights OPTIONS { uniqueEdges: "path" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }` cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") assert.Equal(t, correctConvertedResult, cleanedResult) } func TestTooManyReturnEntities(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0, 1, 2 ], "relations": [ 0 ] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "city", "value": "San Francisco", "dataType": "text", "matchType": "exact" } ] } ], "relations": [ { "type": "flights", "depth": { "min": 1, "max": 1 }, "entityFrom": -1, "entityTo": 0, "constraints": [] } ], "limit": 5000 }`) _, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.Equal(t, errors.New("non-existing entity referenced in return"), err) } func TestTooManyReturnRelations(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0 ], "relations": [ 0, 1, 2 ] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "city", "value": "San Francisco", "dataType": "text", "matchType": "exact" } ] } ], "relations": [ { "type": "flights", "depth": { "min": 1, "max": 1 }, "entityFrom": -1, "entityTo": 0, "constraints": [] } ], "limit": 5000 }`) _, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.Equal(t, errors.New("non-existing relation referenced in return"), err) } func TestNegativeReturnEntities(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0, -1 ], "relations": [ 0, 1, 2 ] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "city", "value": "San Francisco", "dataType": "text", "matchType": "exact" } ] } ], "relations": [ { "type": "flights", "depth": { "min": 1, "max": 1 }, "entityFrom": -1, "entityTo": 0, "constraints": [] } ], "limit": 5000 }`) _, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.Equal(t, errors.New("non-existing entity referenced in return"), err) } func TestNoRelationsField(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0 ] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "city", "value": "San Francisco", "dataType": "text", "matchType": "exact" } ] } ], "limit": 5000 }`) convertedResult, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) // Assert that the result and the expected result are the same correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.city == "San Francisco" RETURN x)LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))LET edges = first(RETURN UNION_DISTINCT([],[]))RETURN {"vertices":nodes, "edges":edges }` cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "") cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "") assert.Equal(t, correctConvertedResult, cleanedResult) } func TestEntityFromLowerThanNegativeOneInRelation(t *testing.T) { // Setup for test // Create query conversion service service := NewService() query := []byte(`{ "return": { "entities": [ 0 ], "relations": [ 0 ] }, "entities": [ { "type": "airports", "constraints": [ { "attribute": "city", "value": "San Francisco", "dataType": "text", "matchType": "exact" } ] } ], "relations": [ { "type": "flights", "depth": { "min": 1, "max": 1 }, "entityFrom": -4, "entityTo": 0, "constraints": [] } ], "limit": 5000 }`) _, _, err := service.ConvertQuery(&query) // Assert that there is no error assert.NoError(t, err) }