/* 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 cypherv2 import ( "encoding/json" "fmt" "strings" "testing" "git.science.uu.nl/graphpolaris/query-conversion/entityv2" "github.com/stretchr/testify/assert" ) // All these tests test the entire flow func fixCypherSpaces(cypher *string) string { trimmedCypher := strings.Replace(*cypher, "\n", " ", -1) trimmedCypher = strings.Replace(trimmedCypher, " ", " ", -1) trimmedCypher = strings.Replace(trimmedCypher, "\t", "", -1) return trimmedCypher } func TestV2NoLogic(t *testing.T) { query := []byte(`{ "databaseName": "Movies3", "return": ["*"], "query": [ { "id": "path1", "node": { "label": "Person", "id": "p1", "relation": { "label": "DIRECTED", "direction": "TO", "depth": { "min": 1, "max": 1 }, "node": { "label": "Movie", "id": "m1" } } } }, { "id": "path2", "node": { "label": "Person", "id": "p1", "relation": { "label": "IN_GENRE", "direction": "TO", "depth": { "min": 1, "max": 1 }, "node": { "label": "Genre", "id": "g1" } } } } ], "limit": 5000 } `) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie)) MATCH path2 = ((p1:Person)-[:IN_GENRE*1..1]->(g1:Genre)) RETURN * LIMIT 5000` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2Simple(t *testing.T) { query := []byte(`{ "databaseName": "Movies3", "return": ["*"], "logic": ["!=", "@p1.name", "\"Raymond Campbell\""], "query": [ { "id": "path1", "node": { "label": "Person", "id": "p1", "relation": { "label": "DIRECTED", "direction": "TO", "depth": { "min": 1, "max": 1 }, "node": { "label": "Movie", "id": "m1" } } } }, { "id": "path2", "node": { "label": "Person", "id": "p1", "relation": { "label": "IN_GENRE", "direction": "TO", "depth": { "min": 1, "max": 1 }, "node": { "label": "Genre", "id": "g1" } } } } ], "limit": 5000 } `) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie)) MATCH path2 = ((p1:Person)-[:IN_GENRE*1..1]->(g1:Genre)) WHERE (p1.name <> "Raymond Campbell") RETURN * LIMIT 5000` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2GroupBy(t *testing.T) { query := []byte(`{ "databaseName": "TweedeKamer", "limit": 5000, "logic": ["AND", ["<", "@movie.imdbRating", 7.5], ["==", "p2.age", "p1.age"]], "query": [ { "ID": "path1", "node": { "label": "Person", "ID": "p1", "relation": { "ID": "acted", "label": "ACTED_IN", "depth": { "min": 1, "max": 1 }, "direction": "TO", "node": { "label": "Movie", "ID": "movie" } } } }, { "ID": "path2", "node": { "label": "Person", "ID": "p2" } } ], "return": ["@path2"] }`) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie)) MATCH path2 = ((p2:Person)) WHERE ((movie.imdbRating < 7.500000) and (p2.age = p1.age)) RETURN path2 LIMIT 5000` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2NoLabel(t *testing.T) { query := []byte(`{ "databaseName": "TweedeKamer", "limit": 5000, "logic": ["<", ["-", "@movie.year", "p1.year"], 10], "query": [ { "ID": "path1", "node": { "ID": "p1", "filter": [], "relation": { "ID": "acted", "depth": { "min": 1, "max": 1 }, "direction": "TO", "node": { "label": "Movie", "ID": "movie" } } } } ], "return": ["*"] }`) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path1 = ((p1)-[acted*1..1]->(movie:Movie)) WHERE ((movie.year - p1.year) < 10.000000) RETURN * LIMIT 5000` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2NoDepth(t *testing.T) { query := []byte(`{ "databaseName": "TweedeKamer", "limit": 5000, "logic": ["AND", ["<", "@movie.imdbRating", 7.5], ["==", "p2.age", "p1.age"]], "query": [ { "ID": "path1", "node": { "ID": "p1", "relation": { "ID": "acted", "direction": "TO", "node": { "label": "Movie", "ID": "movie" } } } }, { "ID": "path2", "node": { "ID": "p2" } } ], "return": ["*"] }`) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } fmt.Printf("%+v\n", JSONQuery) s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path1 = ((p1)-[acted]->(movie:Movie)) MATCH path2 = ((p2)) WHERE ((movie.imdbRating < 7.500000) and (p2.age = p1.age)) RETURN * LIMIT 5000` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2WithAverage(t *testing.T) { query := []byte(`{ "databaseName": "TweedeKamer", "limit": 5000, "logic": ["<", "@p1.age", ["Avg", "@p1.age"]], "query": [ { "ID": "path1", "node": { "label": "Person", "ID": "p1", "relation": { "ID": "acted", "label": "ACTED_IN", "depth": { "min": 1, "max": 1 }, "direction": "TO", "node": { "label": "Movie", "ID": "movie" } } } } ], "return": ["*"] }`) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie)) WITH avg(p1.age) AS p1_age_avg, p1, movie, acted MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie)) WHERE (p1.age < p1_age_avg) RETURN * LIMIT 5000` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2WithAverage2Paths(t *testing.T) { query := []byte(`{ "databaseName": "TweedeKamer", "limit": 5000, "logic": ["<", "@p1.age", ["Avg", "@p1.age"]], "query": [ { "ID": "path1", "node": { "label": "Person", "ID": "p1", "relation": { "ID": "acted", "label": "ACTED_IN", "depth": { "min": 1, "max": 1 }, "direction": "TO", "node": { "label": "Movie", "ID": "movie" } } } },{ "ID": "path2", "node": { "label": "Person", "ID": "p2", "relation": { "ID": "acted", "label": "ACTED_IN", "depth": { "min": 1, "max": 1 }, "direction": "TO", "node": { "label": "Movie", "ID": "movie" } } } } ], "return": ["*"] }`) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie)) MATCH path2 = ((p2:Person)-[acted:ACTED_IN*1..1]->(movie:Movie)) WITH avg(p1.age) AS p1_age_avg, p2, movie, acted MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie)) MATCH path2 = ((p2:Person)-[acted:ACTED_IN*1..1]->(movie:Movie)) WHERE (p1.age < p1_age_avg) RETURN * LIMIT 5000` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2SingleEntityWithLowerLike(t *testing.T) { query := []byte(`{ "databaseName": "TweedeKamer", "limit": 5000, "logic": ["Like", ["Lower", "@p1.name"], "\"john\""], "query": [ { "ID": "path1", "node": { "label": "Person", "ID": "p1" } } ], "return": ["*"] }`) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path1 = ((p1:Person)) WHERE (toLower(p1.name) =~ (".*" + "john" + ".*")) RETURN * LIMIT 5000` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2Like(t *testing.T) { query := []byte(`{ "databaseName": "neo4j", "query": [ { "ID": "path_0", "node": { "ID": "id_1691576718400", "label": "Employee", "relation": { "ID": "id_1691576720177", "label": "REPORTS_TO", "direction": "TO", "node": {} } } } ], "limit": 500, "return": [ "*" ], "logic": [ "Like", "@id_1691576718400.title", "\"ale\"" ] }`) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path_0 = ((id_1691576718400:Employee)-[id_1691576720177:REPORTS_TO]->()) WHERE (id_1691576718400.title =~ (".*" + "ale" + ".*")) RETURN * LIMIT 500` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2Both(t *testing.T) { query := []byte(`{ "databaseName": "neo4j", "query": [ { "ID": "path_0", "node": { "ID": "id_1691576718400", "label": "Employee", "relation": { "ID": "id_1691576720177", "label": "REPORTS_TO", "direction": "BOTH", "node": {} } } } ], "limit": 500, "return": [ "*" ], "logic": [ "Like", "@id_1691576718400.title", "\"ale\"" ] }`) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path_0 = ((id_1691576718400:Employee)-[id_1691576720177:REPORTS_TO]-()) WHERE (id_1691576718400.title =~ (".*" + "ale" + ".*")) RETURN * LIMIT 500` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2RelationLogic(t *testing.T) { query := []byte(`{ "databaseName": "neo4j", "query": [ { "ID": "path_0", "node": { "relation": { "ID": "id_1698231933579", "label": "CONTAINS", "depth": { "min": 0, "max": 1 }, "direction": "TO", "node": {} } } } ], "limit": 500, "return": [ "*" ], "logic": [ "<", "@id_1698231933579.unitPrice", "10" ] }`) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path_0 = (()-[id_1698231933579:CONTAINS*0..1]->()) WHERE ALL(path_0_rel_id_1698231933579 in id_1698231933579 WHERE (path_0_rel_id_1698231933579.unitPrice < 10)) RETURN * LIMIT 500` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2RelationLogic2(t *testing.T) { query := []byte(`{ "databaseName": "neo4j", "query": [ { "ID": "path_0", "node": { "ID": "id_1700302584692", "label": "Employee", "relation": { "depth": { "min": 1, "max": 1 }, "direction": "BOTH", "node": { "ID": "id_1700302388489", "label": "Order", "relation": { "ID": "id_1698231933579", "label": "CONTAINS", "depth": { "min": 1, "max": 1 }, "direction": "TO", "node": {} } } } } } ], "limit": 500, "return": [ "*" ], "logic": [ "<", "@id_1698231933579.unitPrice", "10" ] }`) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path_0 = ((id_1700302584692:Employee)-[*1..1]-(id_1700302388489:Order)-[id_1698231933579:CONTAINS*1..1]->()) WHERE ALL(path_0_rel_id_1698231933579 in id_1698231933579 WHERE (path_0_rel_id_1698231933579.unitPrice < 10)) RETURN * LIMIT 500` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) } func TestV2Count(t *testing.T) { query := []byte(`{ "databaseName": "Movies3", "return": ["*"], "logic": [">", ["Count", "@p1"], "1"], "query": [ { "id": "path1", "node": { "label": "Person", "id": "p1", "relation": { "label": "DIRECTED", "direction": "TO", "depth": { "min": 1, "max": 1 }, "node": { "label": "Movie", "id": "m1" } } } } ], "limit": 5000 } `) var JSONQuery entityv2.IncomingQueryJSON err := json.Unmarshal(query, &JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } s := NewService() cypher, _, err := s.ConvertQuery(&JSONQuery) if err != nil { fmt.Println(err) t.Log(err) } t.Log(*cypher) answer := `MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie)) WITH count(p1) AS p1_count, m1 MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie)) WHERE (p1_count > 1) RETURN * LIMIT 5000` fmt.Printf("Cypher: %s\n", answer) trimmedCypher := fixCypherSpaces(cypher) trimmedAnswer := fixCypherSpaces(&answer) assert.Equal(t, trimmedAnswer, trimmedCypher) }