diff --git a/core/src/main/java/org/polypheny/db/algebra/enumerable/RexToLixTranslator.java b/core/src/main/java/org/polypheny/db/algebra/enumerable/RexToLixTranslator.java index ba5c40be3c..d6a5d1599a 100644 --- a/core/src/main/java/org/polypheny/db/algebra/enumerable/RexToLixTranslator.java +++ b/core/src/main/java/org/polypheny/db/algebra/enumerable/RexToLixTranslator.java @@ -522,7 +522,7 @@ private Expression translateCall( RexCall call, RexImpTable.NullAs nullAs ) { final Operator operator = call.getOperator(); CallImplementor implementor = RexImpTable.INSTANCE.get( operator ); if ( implementor == null ) { - throw new GenericRuntimeException( "cannot translate call " + call ); + throw new GenericRuntimeException( "Cannot translate call " + call ); } return implementor.implement( this, call, nullAs ); } diff --git a/dbms/src/test/java/org/polypheny/db/cypher/AggregateTest.java b/dbms/src/test/java/org/polypheny/db/cypher/AggregateTest.java deleted file mode 100644 index c6632deb32..0000000000 --- a/dbms/src/test/java/org/polypheny/db/cypher/AggregateTest.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2019-2024 The Polypheny Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.polypheny.db.cypher; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.polypheny.db.cypher.helper.TestLiteral; -import org.polypheny.db.webui.models.results.GraphResult; - - -public class AggregateTest extends CypherTestTemplate { - - @BeforeEach - public void reset() { - tearDown(); - createGraph(); - } - - - @AfterEach - public void tearGraphDown() { - tearDown(); - } - - - @Test - public void singleCountAggregateTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - execute( SINGLE_EDGE_1 ); - execute( SINGLE_EDGE_2 ); - - GraphResult res = execute( "MATCH (n:Person) RETURN count(*)" ); - - containsRows( res, true, true, - Row.of( TestLiteral.from( 4 ) ) ); - - execute( SINGLE_EDGE_2 ); - - containsRows( res, true, true, - Row.of( TestLiteral.from( 6 ) ) ); - } - - - @Test - public void countFieldAggregateTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - execute( SINGLE_EDGE_1 ); - execute( SINGLE_EDGE_2 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name, count(*)" ); - - containsRows( res, true, false, - Row.of( TestLiteral.from( "Max" ), TestLiteral.from( 3 ) ), - Row.of( TestLiteral.from( "Kira" ), TestLiteral.from( 1 ) ), - Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( 2 ) ) ); - - } - - - @Test - public void countRenameFieldAggregateTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - execute( SINGLE_EDGE_1 ); - execute( SINGLE_EDGE_2 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name, count(*) AS c" ); - assert res.getHeader()[1].name.equals( "c" ); - - containsRows( res, true, false, - Row.of( TestLiteral.from( "Max" ), TestLiteral.from( 3 ) ), - Row.of( TestLiteral.from( "Kira" ), TestLiteral.from( 1 ) ), - Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( 2 ) ) ); - - } - - - @Test - public void doubleCountRenameAggregateTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - execute( SINGLE_EDGE_1 ); - execute( SINGLE_EDGE_2 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name, n.age, count(*) AS c" ); - - containsRows( res, true, false, - Row.of( TestLiteral.from( "Max" ), TestLiteral.from( null ), TestLiteral.from( 3 ) ), - Row.of( TestLiteral.from( "Kira" ), TestLiteral.from( 3 ), TestLiteral.from( 1 ) ), - Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( null ), TestLiteral.from( 1 ) ), - Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( 31 ), TestLiteral.from( 1 ) ) ); - - } - - - @Test - public void singleAvgAggregateTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - execute( SINGLE_EDGE_1 ); - execute( SINGLE_EDGE_2 ); - execute( SINGLE_NODE_PERSON_COMPLEX_1 ); - - GraphResult res = execute( "MATCH (n) RETURN avg(n.age)" ); - - containsRows( res, true, false, - Row.of( TestLiteral.from( 24 ) ) ); - - } - - - @Test - public void singleMinMaxAggregateTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - execute( SINGLE_EDGE_1 ); - execute( SINGLE_EDGE_2 ); - execute( SINGLE_NODE_PERSON_COMPLEX_1 ); - - GraphResult res = execute( "MATCH (n) RETURN min(n.age)" ); - - containsRows( res, true, false, - Row.of( TestLiteral.from( 3 ) ) ); - - res = execute( "MATCH (n) RETURN max(n.age)" ); - - containsRows( res, true, false, - Row.of( TestLiteral.from( 45 ) ) ); - - } - - - @Test - public void singleSumAggregateTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - execute( SINGLE_EDGE_1 ); - execute( SINGLE_EDGE_2 ); - - execute( "MATCH (n) RETURN sum(n.age)" ); - } - -} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/CypherTestTemplate.java b/dbms/src/test/java/org/polypheny/db/cypher/CypherTestTemplate.java index cc87204cdb..203f4a4632 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/CypherTestTemplate.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/CypherTestTemplate.java @@ -17,6 +17,8 @@ package org.polypheny.db.cypher; import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; @@ -48,6 +50,7 @@ import org.polypheny.db.util.Pair; import org.polypheny.db.webui.models.results.GraphResult; + @Slf4j public class CypherTestTemplate { @@ -58,6 +61,8 @@ public class CypherTestTemplate { protected static final String SINGLE_NODE_PERSON_COMPLEX_1 = "CREATE (p:Person {name: 'Ann', age: 45, depno: 13})"; protected static final String SINGLE_NODE_PERSON_COMPLEX_2 = "CREATE (p:Person {name: 'Bob', age: 31, depno: 13})"; + protected static final String SINGLE_NODE_PERSON_COMPLEX_3 = "CREATE (p:Person {name: 'Alex', age: 32, depno: 14})"; + protected static final String SINGLE_NODE_ANIMAL = "CREATE (a:Animal {name:'Kira', age:3, type:'dog'})"; protected static final String SINGLE_NODE_GEOM = "CREATE (c:City {name:'Basel', location:'POINT (7.586052 47.559152)'})"; protected static final String SINGLE_EDGE_1 = "CREATE (p:Person {name: 'Max'})-[rel:OWNER_OF]->(a:Animal {name:'Kira', age:3, type:'dog'})"; @@ -172,7 +177,6 @@ public boolean containsIn( GraphResult actual, boolean exclusive, int index, @Nu } return contains; - } @@ -186,13 +190,12 @@ public static boolean containsRows( GraphResult actual, boolean exclusive, boole i++; } - assert !exclusive || actual.getData().length >= rows.length; + // Use assert to validate exclusive condition + assertTrue( !exclusive || actual.getData().length >= rows.length, "Exclusive condition failed: actual data has fewer rows than expected." ); + + // Use the appropriate matching method based on the 'ordered' flag + return ordered ? matchesExactRows( parsed, rows ) : matchesUnorderedRows( parsed, rows ); - if ( ordered ) { - return matchesExactRows( parsed, rows ); - } else { - return matchesUnorderedRows( parsed, rows ); - } } catch ( Throwable t ) { fail( "Error while evaluating result: " + t.getMessage() ); throw new RuntimeException(); @@ -201,40 +204,32 @@ public static boolean containsRows( GraphResult actual, boolean exclusive, boole private static boolean matchesUnorderedRows( List> parsed, Row[] rows ) { - List used = new ArrayList<>(); - for ( Row row : rows ) { - int i = 0; + for ( Row row : rows ) { boolean matches = false; - for ( List objects : parsed ) { - if ( !matches && !used.contains( i ) ) { - if ( row.matches( objects ) ) { - used.add( i ); - matches = true; - } + for ( int i = 0; i < parsed.size(); i++ ) { + if ( !matches && !used.contains( i ) && row.matches( parsed.get( i ) ) ) { + used.add( i ); + matches = true; + break; } - - i++; - } - if ( !matches ) { - return false; } + + // Use assert to validate that each row finds a match + assertTrue( matches, "Row " + row + " could not be matched in the parsed data." ); } return true; - } private static boolean matchesExactRows( List> parsed, Row[] rows ) { - boolean matches = true; - int j = 0; - for ( Row row : rows ) { - matches &= row.matches( parsed.get( j ) ); - j++; + for ( int j = 0; j < rows.length; j++ ) { + // Use assert to ensure each row matches + assertTrue( rows[j].matches( parsed.get( j ) ), "Row " + j + " does not match the expected value." ); } - return matches; + return true; } @@ -246,14 +241,15 @@ private boolean contains( String[][] actual, boo parsed.add( PolyValue.JSON_WRAPPER.readValue( entry[index], clazz ) ); } - assert !exclusive || parsed.size() == expected.length; + // Assert that if exclusive is true, the number of parsed items equals the number of expected items + assertEquals( exclusive ? expected.length : parsed.size(), parsed.size(), "Exclusive condition failed: parsed size does not match expected length." ); - boolean contains = true; for ( TestObject node : expected ) { - contains &= parsed.stream().anyMatch( n -> node.matches( n, exclusive ) ); + // Assert that each expected node matches at least one parsed element + assertTrue( parsed.stream().anyMatch( n -> node.matches( n, exclusive ) ), "Expected node does not match any parsed element." ); } - return contains; + return true; } @@ -280,7 +276,7 @@ protected boolean is( GraphResult res, Type type, int index ) { protected void assertEmpty( GraphResult res ) { - assert res.getData().length == 0; + assertEquals( 0, res.getData().length ); } @@ -361,5 +357,4 @@ public boolean matches( List objects ) { } - } diff --git a/dbms/src/test/java/org/polypheny/db/cypher/DdlTest.java b/dbms/src/test/java/org/polypheny/db/cypher/DdlTest.java index 6f85af33b3..528f0c12ad 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/DdlTest.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/DdlTest.java @@ -16,7 +16,6 @@ package org.polypheny.db.cypher; - import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -34,8 +33,10 @@ import org.polypheny.db.catalog.Catalog; import org.polypheny.db.catalog.entity.logical.LogicalGraph; import org.polypheny.db.catalog.entity.logical.LogicalNamespace; +import org.polypheny.db.cypher.clause.write.DmlInsertTest; import org.polypheny.db.webui.models.results.GraphResult; + @Tag("adapter") @Slf4j public class DdlTest extends CypherTestTemplate { @@ -58,7 +59,6 @@ public void addGraphTest() { assertTrue( Catalog.snapshot().getNamespace( graphName ).isPresent() ); execute( "DROP DATABASE " + graphName ); - } @@ -70,7 +70,6 @@ public void createNamespaceTest( String namespaceName ) { execute( format( "CREATE %s %s", namespaceName, name ) ); execute( format( "DROP %s %s", namespaceName, name ) ); - } @@ -97,10 +96,8 @@ public void addPlacementTest() throws SQLException { execute( "DROP DATABASE " + graphName ); } finally { - removeStore( "store1" ); } - } @@ -128,7 +125,6 @@ public void initialPlacementTest() throws SQLException { } finally { removeStore( "store1" ); } - } @@ -160,13 +156,11 @@ public void deletePlacementTest() throws SQLException { } finally { removeStore( "store1" ); } - } @Test public void deletePlacementDataTest() throws SQLException { - execute( "CREATE DATABASE " + graphName + " IF NOT EXISTS" ); execute( DmlInsertTest.CREATE_COMPLEX_GRAPH_2, graphName ); @@ -191,7 +185,6 @@ public void deletePlacementDataTest() throws SQLException { } finally { removeStore( "store1" ); } - } @@ -209,9 +202,7 @@ private void removeStore( String name ) throws SQLException { try ( JdbcConnection polyphenyDbConnection = new JdbcConnection( true ) ) { Connection connection = polyphenyDbConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { - statement.executeUpdate( String.format( "ALTER ADAPTERS DROP \"%s\"", name ) ); - } } } diff --git a/dbms/src/test/java/org/polypheny/db/cypher/DmlDeleteTest.java b/dbms/src/test/java/org/polypheny/db/cypher/DmlDeleteTest.java deleted file mode 100644 index 8dc5ae6e95..0000000000 --- a/dbms/src/test/java/org/polypheny/db/cypher/DmlDeleteTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2019-2024 The Polypheny Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.polypheny.db.cypher; - -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.polypheny.db.cypher.helper.TestNode; -import org.polypheny.db.util.Pair; -import org.polypheny.db.webui.models.results.GraphResult; - -public class DmlDeleteTest extends CypherTestTemplate { - - @BeforeEach - public void reset() { - tearDown(); - createGraph(); - } - - - @Test - public void simpleEmptyNodeDeleteTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( "MATCH (p:Person)\n" - + "DELETE p" ); - GraphResult res = matchAndReturnAllNodes(); - assertEmpty( res ); - } - - - @Test - public void simpleNodeDeleteTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( "MATCH (p:Person {name: 'Max'})\n" - + "DELETE p" ); - GraphResult res = matchAndReturnAllNodes(); - assertEmpty( res ); - } - - - @Test - public void simpleFilteredNodeDeleteTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - execute( "MATCH (p:Person {name: 'Max'})\n" - + "DELETE p" ); - GraphResult res = matchAndReturnAllNodes(); - assert containsRows( res, true, false, - Row.of( TestNode.from( List.of( "Person" ), Pair.of( "name", "Hans" ) ) ) ); - } - - - @Test - public void twoNodeDeleteTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - execute( "MATCH (p:Person {name: 'Max'}), (h:Person {name: 'Hans'})\n" - + "DELETE p, h" ); - GraphResult res = matchAndReturnAllNodes(); - assertEmpty( res ); - } - - - @Test - public void simpleRelationshipDeleteTest() { - execute( SINGLE_EDGE_1 ); - execute( "MATCH (:Person {name: 'Max'})-[rel:OWNER_OF]->(:Animal {name: 'Kira'}) \n" - + "DELETE rel" ); - - GraphResult res = matchAndReturnAllNodes(); - assert containsRows( res, true, false, - Row.of( TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ) ), - Row.of( TestNode.from( - List.of( "Animal" ), - Pair.of( "name", "Kira" ), - Pair.of( "age", 3 ), - Pair.of( "type", "dog" ) ) ) ); - - } - - -} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/DmlInsertTest.java b/dbms/src/test/java/org/polypheny/db/cypher/DmlInsertTest.java deleted file mode 100644 index 87b7ead1cf..0000000000 --- a/dbms/src/test/java/org/polypheny/db/cypher/DmlInsertTest.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2019-2024 The Polypheny Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.polypheny.db.cypher; - -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.polypheny.db.cypher.helper.TestEdge; -import org.polypheny.db.cypher.helper.TestNode; -import org.polypheny.db.util.Pair; -import org.polypheny.db.webui.models.results.GraphResult; - -public class DmlInsertTest extends CypherTestTemplate { - - - public static final String CREATE_PERSON_MAX = "CREATE (p:Person {name: 'Max Muster'})"; - - public static final String CREATE_COMPLEX_GRAPH_1 = - "CREATE\n" - + " (adam:User {name: 'Adam'}),\n" - + " (pernilla:User {name: 'Pernilla'}),\n" - + " (david:User {name: 'David'}),\n" - + " (adam)-[:FRIEND]->(pernilla),\n" - + " (pernilla)-[:FRIEND]->(david)"; - - public static final String CREATE_COMPLEX_GRAPH_2 = - "CREATE (adam:User {name: 'Adam'}), (pernilla:User {name: 'Pernilla'}), (david:User {name: 'David'}), (adam)-[:FRIEND]->(pernilla), (pernilla)-[:FRIEND]->(david), (david)-[:FRIEND]->(adam)"; - - - @BeforeEach - public void reset() { - tearDown(); - createGraph(); - } - - - @Test - public void insertEmptyNode() { - execute( "CREATE (p)" ); - GraphResult res = matchAndReturnAllNodes(); - assertNode( res, 0 ); - assert containsNodes( res, true, TestNode.from( List.of() ) ); - } - - - @Test - public void insertNodeTest() { - execute( "CREATE (p:Person {name: 'Max Muster'})" ); - GraphResult res = matchAndReturnAllNodes(); - assertNode( res, 0 ); - assert containsNodes( res, true, TestNode.from( Pair.of( "name", "Max Muster" ) ) ); - } - - - @Test - public void insertTwoNodeTest() { - execute( CREATE_PERSON_MAX ); - execute( CREATE_PERSON_MAX ); - GraphResult res = matchAndReturnAllNodes(); - assertNode( res, 0 ); - assert containsNodes( res, true, - TestNode.from( Pair.of( "name", "Max Muster" ) ), - TestNode.from( Pair.of( "name", "Max Muster" ) ) ); - } - - - @Test - public void insertMultipleNodesTest() { - execute( "CREATE (p),(n),(m)" ); - GraphResult res = matchAndReturnAllNodes(); - assertNode( res, 0 ); - assert containsNodes( res, true, TestNode.from(), TestNode.from(), TestNode.from() ); - } - - - @Test - public void insertPropertyTypeTest() { - execute( "CREATE (p:Person {name: 'Max Muster', age: 13, height: 185.3, nicknames: [\"Maxi\",\"Musti\"]})" ); - GraphResult res = matchAndReturnAllNodes(); - assertNode( res, 0 ); - assert containsNodes( res, true, - TestNode.from( - Pair.of( "name", "Max Muster" ), - Pair.of( "age", 13 ), - Pair.of( "height", 185.3 ), - Pair.of( "nicknames", List.of( "Maxi", "Musti" ) ) - ) ); - } - - - @Test - @Disabled - public void insertReturnNodeTest() { - GraphResult res = execute( - "CREATE (p:Person {name: 'Max Muster'})\n" - + "RETURN p" ); - } - - - @Test - public void insertSingleHopPathTest() { - execute( "CREATE (p:Person {name: 'Max'})-[rel:OWNER_OF]->(a:Animal {name:'Kira', age:3, type:'dog'})" ); - GraphResult res = matchAndReturnAllNodes(); - assert containsNodes( res, true, - TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ), - TestNode.from( - List.of( "Animal" ), - Pair.of( "name", "Kira" ), - Pair.of( "age", 3 ), - Pair.of( "type", "dog" ) ) ); - } - - - @Test - public void insertSingleHopPathEdgesTest() { - execute( "CREATE (p:Person {name: 'Max'})-[rel:OWNER_OF]->(a:Animal {name:'Kira', age:3, type:'dog'})" ); - GraphResult res = execute( "MATCH ()-[r]->() RETURN r" ); - assert containsEdges( res, true, TestEdge.from( List.of( "OWNER_OF" ) ) ); - } - - - @Test - public void insertMultipleHopPathTest() { - execute( "CREATE (n:Person)-[f:FRIEND_OF]->(p:Person {name: 'Max'})-[rel:OWNER_OF]->(a:Animal {name:'Kira'})" ); - - // only select all nodes - GraphResult res = matchAndReturnAllNodes(); - assert containsNodes( res, true, - TestNode.from( List.of( "Person" ) ), - TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ), - TestNode.from( List.of( "Animal" ), Pair.of( "name", "Kira" ) ) ); - - // only select all edges - res = execute( "MATCH ()-[r]->() RETURN r" ); - assert containsRows( res, true, false, - Row.of( TestEdge.from( List.of( "OWNER_OF" ) ) ), - Row.of( TestEdge.from( List.of( "FRIEND_OF" ) ) ) ); - } - - - @Test - public void insertAllInOneTest() { - execute( CREATE_COMPLEX_GRAPH_1 ); - GraphResult res = execute( "MATCH (n) RETURN n" ); - assert res.getData().length == 3; - assertNode( res, 0 ); - - res = execute( "MATCH ()-[r]-() RETURN r" ); - assertEdge( res, 0 ); - // double matches of same path is correct, as no direction is specified - assert res.getData().length == 4; - - res = execute( "MATCH ()-[r]->() RETURN r" ); - assertEdge( res, 0 ); - // double matches of same path is correct, as no direction is specified - assert res.getData().length == 2; - } - - - @Test - public void insertAllInOneCircleTest() { - execute( CREATE_COMPLEX_GRAPH_2 ); - GraphResult res = execute( "MATCH (n) RETURN n" ); - assert res.getData().length == 3; - assertNode( res, 0 ); - - res = execute( "MATCH ()-[r]-() RETURN r" ); - assertEdge( res, 0 ); - assert res.getData().length == 6; - } - - - @Test - public void insertAdditionalEdgeTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - - execute( "MATCH (max:Person {name: 'Max'}), (hans:Person {name: 'Hans'})\n" - + "CREATE (max)-[:KNOWS]->(hans)" ); - - GraphResult res = execute( "MATCH ()-[r]->() RETURN r" ); - - assert containsRows( res, true, true, - Row.of( TestEdge.from( List.of( "KNOWS" ) ) ) ); - } - - - @Test - public void insertAdditionalEdgeOneSideTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - - execute( "MATCH (max:Person {name: 'Max'})\n" - + "CREATE (max)-[:KNOWS]->(hans:Person {name: 'Hans'})" ); - - GraphResult res = execute( "MATCH ()-[r]->() RETURN r" ); - - assert containsRows( res, true, true, - Row.of( TestEdge.from( List.of( "KNOWS" ) ) ) ); - - } - - - @Test - public void insertAdditionalEdgeOneSideBothSideTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - - execute( "MATCH (max:Person {name: 'Max'})\n" - + "CREATE (max)-[:KNOWS]->(hans:Person {name: 'Hans'})-[:KNOWS]->(peter:Person {name: 'Peter'})" ); - - GraphResult res = execute( "MATCH ()-[r]->() RETURN r" ); - - assert containsRows( res, true, true, - Row.of( TestEdge.from( List.of( "KNOWS" ) ) ), - Row.of( TestEdge.from( List.of( "KNOWS" ) ) ) ); - - } - -} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/OrderByTest.java b/dbms/src/test/java/org/polypheny/db/cypher/OrderByTest.java deleted file mode 100644 index 089dd2ce6e..0000000000 --- a/dbms/src/test/java/org/polypheny/db/cypher/OrderByTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2019-2024 The Polypheny Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.polypheny.db.cypher; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.polypheny.db.cypher.helper.TestLiteral; -import org.polypheny.db.webui.models.results.GraphResult; - -public class OrderByTest extends CypherTestTemplate { - - @BeforeEach - public void reset() { - tearDown(); - createGraph(); - } - - - @Test - public void singleOrderByTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name ORDER BY n.name ASC" ); - - assert containsRows( res, true, true, - Row.of( TestLiteral.from( "Hans" ) ), - Row.of( TestLiteral.from( "Max" ) ), - Row.of( TestLiteral.from( "Max" ) ) ); - - res = execute( "MATCH (n) RETURN n.name ORDER BY n.name DESC" ); - - assert containsRows( res, true, true, - Row.of( TestLiteral.from( "Max" ) ), - Row.of( TestLiteral.from( "Max" ) ), - Row.of( TestLiteral.from( "Hans" ) ) ); - - } - - - @Test - public void doubleOrderByTest() { - execute( SINGLE_NODE_PERSON_COMPLEX_1 ); - execute( SINGLE_NODE_PERSON_COMPLEX_2 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name, n.age ORDER BY n.age ASC, n.name ASC" ); - - assert containsRows( res, true, true, - Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( 31 ) ), - Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ) ) ); - - res = execute( "MATCH (n) RETURN n.depno, n.name ORDER BY n.depno ASC, n.name DESC" ); - - assert containsRows( res, true, true, - Row.of( TestLiteral.from( 13 ), TestLiteral.from( "Bob" ) ), - Row.of( TestLiteral.from( 13 ), TestLiteral.from( "Ann" ) ) ); - - } - - - @Test - public void nullOrderByTest() { - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_PERSON_1 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name, n.age ORDER BY n.age ASC" ); - - assertTrue( containsRows( res, true, true, - Row.of( TestLiteral.from( "Kira" ), TestLiteral.from( 3 ) ), - Row.of( TestLiteral.from( "Max" ), TestLiteral.from( null ) ) ) ); - - res = execute( "MATCH (n) RETURN n.name, n.age ORDER BY n.age DESC" ); - - assertTrue( containsRows( res, true, true, - Row.of( TestLiteral.from( "Max" ), TestLiteral.from( null ) ), - Row.of( TestLiteral.from( "Kira" ), TestLiteral.from( 3 ) ) ) ); - - } - - - @Test - public void limitWithoutSortTest() { - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_1 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name, n.age LIMIT 3" ); - - assert res.getData().length == 3; - } - - - @Test - public void limitWithSortTest() { - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_1 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name ORDER BY n.name DESC LIMIT 3" ); - - assert res.getData().length == 3; - - assert containsRows( res, true, true, - Row.of( TestLiteral.from( "Max" ) ), - Row.of( TestLiteral.from( "Max" ) ), - Row.of( TestLiteral.from( "Kira" ) ) ); - - } - - - @Test - public void skipWithoutSortTest() { - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_1 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name, n.age SKIP 3" ); - - assert res.getData().length == 2; - } - - - @Test - public void skipWithSortTest() { - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_1 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name ORDER BY n.name DESC SKIP 3" ); - - assert containsRows( res, true, true, - Row.of( TestLiteral.from( "Kira" ) ), - Row.of( TestLiteral.from( "Kira" ) ) ); - } - - - @Test - public void skipAndLimitWithoutSortTest() { - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_1 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name SKIP 3 LIMIT 1" ); - - assert res.getData().length == 1; - } - - - @Test - public void skipAndLimitWithSortTest() { - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_1 ); - - GraphResult res = execute( "MATCH (n) RETURN n.name ORDER BY n.name DESC SKIP 1 LIMIT 2" ); - - assert containsRows( res, true, true, - Row.of( TestLiteral.from( "Max" ) ), - Row.of( TestLiteral.from( "Kira" ) ) ); - } - - - @Test - public void orderByNotReturnedPropertyTest() { - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_ANIMAL ); - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_1 ); - - GraphResult res = execute( "MATCH (n) RETURN n ORDER BY n.name DESC" ); - - assert containsRows( res, true, true, - Row.of( MAX ), - Row.of( MAX ), - Row.of( KIRA ), - Row.of( KIRA ), - Row.of( KIRA ) ); - } - -} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/UnwindTest.java b/dbms/src/test/java/org/polypheny/db/cypher/UnwindTest.java deleted file mode 100644 index 8790c7d29f..0000000000 --- a/dbms/src/test/java/org/polypheny/db/cypher/UnwindTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2019-2024 The Polypheny Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.polypheny.db.cypher; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.polypheny.db.cypher.helper.TestLiteral; -import org.polypheny.db.webui.models.results.GraphResult; - -public class UnwindTest extends CypherTestTemplate { - - @BeforeEach - public void reset() { - tearDown(); - createGraph(); - } - - - @Test - public void simpleUnwindTest() { - GraphResult res = execute( "UNWIND [1, 3, null] AS x RETURN x, 'val' AS y" ); - - assert containsRows( res, true, true, - Row.of( TestLiteral.from( 1 ), TestLiteral.from( "val" ) ), - Row.of( TestLiteral.from( 3 ), TestLiteral.from( "val" ) ), - Row.of( TestLiteral.from( null ), TestLiteral.from( "val" ) ) ); - - } - - - @Test - public void emptyUnwind() { - GraphResult res = execute( "UNWIND [] AS x RETURN x, 'val' AS y" ); - - assertEmpty( res ); - } - - - @Test - public void nullUnwind() { - GraphResult res = execute( "UNWIND null AS x RETURN x, 'val' AS y" ); - - assertEmpty( res ); - } - - - @Test - public void listOfListUnwind() { - GraphResult res = execute( "WITH [[1], [2, 4], 3] AS nested UNWIND nested AS x UNWIND x AS y RETURN y" ); - - containsRows( res, true, true, - Row.of( TestLiteral.from( 1 ) ), - Row.of( TestLiteral.from( 2 ) ), - Row.of( TestLiteral.from( 4 ) ), - Row.of( TestLiteral.from( 3 ) ) ); - } - - - @Test - public void nodePropertyUnwind() { - execute( "CREATE (n {key: [3,1]})" ); - GraphResult res = execute( "MATCH (n) UNWIND n.key AS x RETURN x" ); - - containsRows( res, true, false, - Row.of( TestLiteral.from( 3 ) ), - Row.of( TestLiteral.from( 1 ) ) ); - } - -} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/WithTest.java b/dbms/src/test/java/org/polypheny/db/cypher/WithTest.java deleted file mode 100644 index 068f3e9dd4..0000000000 --- a/dbms/src/test/java/org/polypheny/db/cypher/WithTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2019-2024 The Polypheny Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.polypheny.db.cypher; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.polypheny.db.cypher.helper.TestLiteral; -import org.polypheny.db.webui.models.results.GraphResult; - - -public class WithTest extends CypherTestTemplate { - - @BeforeEach - public void reset() { - tearDown(); - createGraph(); - } - - - @Test - public void addVariableTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - - GraphResult res = execute( "MATCH (n:Person) WITH n.name, n WHERE n.name STARTS WITH 'H' RETURN n" ); - assertNode( res, 0 ); - - assert containsRows( res, true, true, - Row.of( HANS ) ); - - } - - - @Test - public void renameVariableTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - - GraphResult res = execute( "MATCH (n:Person) WITH n.name AS name, n WHERE name ENDS WITH 'x' RETURN name, n" ); - assertNode( res, 1 ); - - assert containsRows( res, true, true, Row.of( TestLiteral.from( "Max" ), MAX ) ); - - } - - - @Test - public void starTest() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_PERSON_2 ); - execute( SINGLE_EDGE_1 ); - execute( SINGLE_EDGE_2 ); - - GraphResult res = execute( "MATCH (n:Person)-[]->(p:Animal) WITH *, n.name AS username WHERE username CONTAINS 'a' RETURN username, p" ); - assertNode( res, 1 ); - - assert containsRows( res, true, true, Row.of( TestLiteral.from( "Max" ), KIRA ) ); - - } - - // aggregate - -} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/general/CallTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/CallTest.java new file mode 100644 index 0000000000..851e14696a --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/CallTest.java @@ -0,0 +1,180 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.general; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class CallTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleCallProcedureTest() { + GraphResult res = execute( "CALL db.labels()" ); + assertEmpty( res ); + + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + + res = execute( "CALL db.labels()" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Person" ) ), + Row.of( TestLiteral.from( "Animal" ) ) ); + + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + + res = execute( "CALL db.labels()" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Person" ) ), + Row.of( TestLiteral.from( "Person" ) ), + Row.of( TestLiteral.from( "Animal" ) ), + Row.of( TestLiteral.from( "Animal" ) ) ); + } + + + @Test + public void callLabelsYieldTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult res = execute( "CALL db.labels() YIELD label" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Person" ) ), + Row.of( TestLiteral.from( "Animal" ) ) ); + } + + + @Test + public void renameCallLabelsYieldTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult res = execute( "CALL db.labels() YIELD label As Label" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Person" ) ), + Row.of( TestLiteral.from( "Animal" ) ) ); + } + + + @Test + public void callLabelsYieldCountTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult res = execute( "CALL db.labels() YIELD *" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 2 ) ) ); + } + + + @Test + public void returnCallLabelsYieldTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult res = execute( "CALL db.labels() YIELD label\n" + + "RETURN label" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 2 ) ) ); + } + + + @Test + public void returnFilterCallLabelsYieldTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult res = execute( "CALL db.labels() YIELD label WHERE label = 'Person' RETURN count(label) AS numLabels" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 1 ) ) ); + } + + + @Test + public void callLabelsYieldWithOrderingTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult res = execute( "CALL db.labels() YIELD label RETURN label ORDER BY label" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Animal" ) ), + Row.of( TestLiteral.from( "Person" ) ), + Row.of( TestLiteral.from( "Person" ) ) ); + } + + + @Test + public void callLabelsYieldWithAggregationTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( "CALL db.labels() YIELD label RETURN label, count(*) AS labelCount" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Person" ), TestLiteral.from( 2 ) ), + Row.of( TestLiteral.from( "Animal" ), TestLiteral.from( 1 ) ) ); + } + + + @Test + public void simpleCallPropertyKeysYieldTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( "CALL db.propertyKeys() YIELD propertyKey " ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "name" ) ), + Row.of( TestLiteral.from( "age" ) ), + Row.of( TestLiteral.from( "type" ) ) ); + } + + + @Test + public void renameCallPropertyKeysYieldTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult res = execute( "CALL db.propertyKeys() YIELD propertyKey AS prop" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Person" ) ), + Row.of( TestLiteral.from( "Animal" ) ) ); + } + + + @Test + public void callPropertyKeysYieldWithMatchTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( """ + CALL db.propertyKeys() YIELD propertyKey AS prop + MATCH (n) + WHERE n[prop] IS NOT NULL + RETURN prop, count(n) AS numNodes""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "name" ), TestLiteral.from( 3 ) ), + Row.of( TestLiteral.from( "age" ), TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( "type" ), TestLiteral.from( 1 ) ) ); + } + + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/general/CaseTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/CaseTest.java new file mode 100644 index 0000000000..075c2a8137 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/CaseTest.java @@ -0,0 +1,120 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.general; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class CaseTest extends CypherTestTemplate { + + protected static final String PERSON_NODE_ALICE = "CREATE (:Person {name:'Alice', age: 38, eyes: 'brown'})"; + protected static final String PERSON_NODE_BOB = "CREATE (:Person {name: 'Bob', age: 25, eyes: 'blue'})"; + protected static final String PERSON_NODE_CHARLIE = "CREATE (:Person {name: 'Charlie', age: 53, eyes: 'green'})"; + + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleCaseTest() { + execute( PERSON_NODE_ALICE ); + execute( PERSON_NODE_BOB ); + execute( PERSON_NODE_CHARLIE ); + GraphResult res = execute( """ + MATCH (n:Person) + RETURN + CASE n.eyes + WHEN 'blue' THEN 1 + WHEN 'brown' THEN 2 + ELSE 3 + END AS result, n.eyes""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Alice" ), TestLiteral.from( 2 ) ), + Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( "Charlie" ), TestLiteral.from( 3 ) ) ); + } + + + @Test + public void genericCaseTest() { + execute( PERSON_NODE_ALICE ); + execute( PERSON_NODE_BOB ); + execute( PERSON_NODE_CHARLIE ); + + GraphResult res = execute( """ + MATCH (n:Person) + RETURN + CASE + WHEN n.eyes = 'blue' THEN 1 + WHEN n.age < 40 THEN 2 + ELSE 3 + END AS result, n.eyes, n.age""" ); + + containsRows( res, true, false, Row.of( TestLiteral.from( "Alice" ), TestLiteral.from( 2 ) ), Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( 1 ) ), Row.of( TestLiteral.from( "Charlie" ), TestLiteral.from( 3 ) ) ); + } + + + @Test + + public void nullWithCaseTest() { + execute( PERSON_NODE_ALICE ); + execute( PERSON_NODE_BOB ); + execute( PERSON_NODE_CHARLIE ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( """ + MATCH (n:Person) + RETURN n.name, + CASE n.age + WHEN null THEN -1 + ELSE n.age - 10 + END AS age_10_years_ago""" ); + + containsRows( res, true, false, Row.of( TestLiteral.from( "Alice" ), TestLiteral.from( 28 ) ), Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( 15 ) ), Row.of( TestLiteral.from( "Charlie" ), TestLiteral.from( 43 ) ), Row.of( TestLiteral.from( "MAX" ), TestLiteral.from( null ) ) ); + } + + + @Test + public void expressionsAndSucceedingClauses() { + execute( PERSON_NODE_ALICE ); + execute( PERSON_NODE_BOB ); + execute( PERSON_NODE_CHARLIE ); + + GraphResult res = execute( """ + MATCH (n:Person) + WITH n, + CASE n.eyes + WHEN 'blue' THEN 1 + WHEN 'brown' THEN 2 + ELSE 3 + END AS colorCode + SET n.colorCode = colorCode + RETURN n.name, n.colorCode""" ); + + containsRows( res, true, false, Row.of( TestLiteral.from( "Alice" ), TestLiteral.from( 2 ) ), Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( 1 ) ), Row.of( TestLiteral.from( "Charlie" ), TestLiteral.from( 3 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/general/FilterTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/FilterTest.java new file mode 100644 index 0000000000..9146c9aa08 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/FilterTest.java @@ -0,0 +1,324 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.general; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class FilterTest extends CypherTestTemplate { + + @BeforeEach + public void setUp() { + tearDown(); + createGraph(); + } + + /////////////////////////////////////////////// + /////////// FILTER + /////////////////////////////////////////////// + + @Test + public void nodeLabelFilterTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult res = execute( """ + MATCH (n) + WHERE n:Person + RETURN n.name, n.age""" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ) ), + Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( 31 ) ) ); + } + + + @Test + public void nodePropertyFilterTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult result = execute( "MATCH (p) WHERE p.age > 3 RETURN p" ); + + assertNode( result, 0 ); + + containsRows( result, true, false ); + + result = execute( "MATCH (p) WHERE p.age >= 3 RETURN p" ); + assertNode( result, 0 ); + + containsRows( result, true, false, Row.of( KIRA ) ); + } + + + @Test + public void relationPropertyFilterTest() { + execute( SINGLE_EDGE_2 ); + GraphResult res = execute( """ + MATCH (n:Person)-[k:KNOWS]->(f) + WHERE k.since < 1995 + RETURN f.name""" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Hans" ) ) ); + } + + + @Test + public void nodePatternFilterTest() { + execute( SINGLE_EDGE_2 ); + GraphResult res = execute( "MATCH (a:Person WHERE a.name = 'Max')-[:KNOWS]->(b:Person WHERE b.name = 'Hans')\n" + + "RETURN b.name" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Hans" ) ) ); + } + + + @Test + public void relationshipPatternFilterTest() { + execute( SINGLE_EDGE_2 ); + GraphResult res = execute( "MATCH (a:Person)-[r:KNOWS WHERE r.since < 2000 ]->(b:Person)\n" + + "RETURN r.since" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 1994 ) ) ); + } + + + @Test + public void propertyExistenceCheckFilterTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( """ + MATCH (n:Person) + WHERE n.age IS NOT NULL + RETURN n.name, n.age""" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ) ) ); + } + + + @Test + public void propertyNonExistenceCheckFilterTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + GraphResult res = execute( """ + MATCH (n:Person) + WHERE n.age IS NULL + RETURN n.name""" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void rangeFilterTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + GraphResult res = execute( "MATCH (n) WHERE 21 < n.age <= 32 RETURN n.name" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Bob" ) ) ); + } + + + @Test + public void booleanConditionFilterTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + GraphResult result = execute( "MATCH (p) WHERE p.age >= 45 AND p.depno = 13 RETURN p.name" ); + + containsRows( result, true, false, Row.of( TestLiteral.from( "Ann" ) ) ); + + result = execute( "MATCH (p) WHERE p.age <= 32 OR p.depno = 13 RETURN p.name " ); + + containsRows( result, true, false, Row.of( TestLiteral.from( "Ann" ) ), + Row.of( TestLiteral.from( "Bob" ) ), + Row.of( TestLiteral.from( "Alex" ) ) ); + } + + + @Test + public void multiBooleanOperatorsFilterTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + GraphResult res = execute( """ + MATCH (n:Person) + WHERE (n.name = 'Alex' XOR (n.age < 30 AND n.name = 'Bob')) OR NOT (n.name = 'Bob' OR n.name = 'Alex') + RETURN + n.name AS name, + n.age AS age + """ ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Alex" ), TestLiteral.from( 32 ) ), + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ) ) ); + } + + + @Test + public void withFilterTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + GraphResult res = execute( """ + MATCH (n:Person) + WITH n.name as name + WHERE n.age = 45 + RETURN name""" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Ann" ) ) ); + } + + + @Test + public void existFilterTest() { + execute( SINGLE_NODE_PERSON_1 ); + GraphResult result = execute( "MATCH (p) WHERE exists(p.age) RETURN p" ); + + assertEmpty( result ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + + result = execute( "MATCH (p) WHERE exists(p.name) RETURN p" ); + assertNode( result, 0 ); + + containsRows( result, true, false, Row.of( MAX ) ); + } + + + /////////////////////////////////// + ///// STRING matching + ///////////////////////////////////// + + + @Test + public void startWithFilterTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult res = execute( "MATCH (p) WHERE p.name STARTS WITH 'M' RETURN p.name" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void endWithFilterTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( """ + MATCH (n) + WHERE n.name ENDS WITH 's' + RETURN n.name""" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Hans" ) ) ); + } + + + @Test + public void notEndWithFilterTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( """ + MATCH (n:Person) + WHERE NOT n.name ENDS WITH 's' + RETURN n.name, n.age""" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void containsFilterTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_NODE_ANIMAL ); + GraphResult result = execute( "MATCH (p) WHERE p.name CONTAINS 'H' RETURN p.name" ); + containsRows( result, true, false, Row.of( TestLiteral.from( "Hans" ) ) ); + } + + + @Test + public void patternFilterTest() { + execute( SINGLE_EDGE_2 ); + GraphResult res = execute( """ + MATCH + (p:Person {name: 'Max'}), + (other:Person) + WHERE (p)-->(other) + RETURN other.name""" ); + + containsRows( res, true, false, Row.of( TestLiteral.from( "Hans" ) ) ); + + res = execute( """ + MATCH + (p:Person {name: 'Max'}), + (other:Person) + WHERE (p)--(other) + RETURN other.name""" ); + + containsRows( res, true, false, Row.of( TestLiteral.from( "Hans" ) ) ); + + res = execute( """ + MATCH + (p:Person {name: 'Max'}), + (other:Person) + WHERE (p)<--(other) + RETURN other.name""" ); + assertEmpty( res ); + } + + + @Test + public void patternWithPropertiesFilterTest() { + execute( SINGLE_EDGE_2 ); + GraphResult res = execute( """ + MATCH (other:Person) + WHERE (other)-[:KNOWS]-({name: 'Hans'}) + RETURN other.name""" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void listComprehensionFilterTest() { + execute( SINGLE_EDGE_2 ); + GraphResult res = execute( "MATCH (a:Person {name: 'Max'})\n" + + "RETURN [(a)-->(b WHERE b:Person) | b.name] AS friends" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( List.of( "Hans" ) ) ) ); + } + + + @Test + public void useInOperatorWithFilterTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( """ + MATCH (a:Person) + WHERE a.name IN ['Peter', 'Max'] + RETURN a.name""" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void missingPropertyFilterTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + GraphResult res = execute( """ + MATCH (n:Person) + WHERE n.age >= 40 + RETURN n.name, n.age""" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/general/LimitTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/LimitTest.java new file mode 100644 index 0000000000..4c3753acf2 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/LimitTest.java @@ -0,0 +1,156 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.general; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.cypher.helper.TestNode; +import org.polypheny.db.util.Pair; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class LimitTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleLimitTest() { + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name, n.age LIMIT 3" ); + + assertEquals( 3, res.getData().length ); + } + + + @Test + public void insertNodeLimitTest() { + GraphResult res = execute( """ + CREATE (n:person) + RETURN n + LIMIT 0""" ); + + assertEquals( 0, res.getData().length ); + res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void updateNodeLimitTest() { + execute( SINGLE_NODE_PERSON_1 ); + GraphResult res = execute( """ + MATCH (n {name: 'Max'}) + SET n.age = 60 + RETURN n + LIMIT 0""" ); + assertEquals( 0, res.getData().length ); + res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), + Pair.of( "name", "Max" ) ) ); + + res = execute( """ + MATCH (n {name: 'Max'}) + SET n.age = 60 + RETURN n + LIMIT 1""" ); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), + Pair.of( "name", "Max" ), + Pair.of( "age", 60 ) ) ); + } + + + @Test + public void orderByLimitTest() { + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name ORDER BY n.name DESC LIMIT 3" ); + + assertEquals( 3, res.getData().length ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Kira" ) ) ); + } + + + @Test + public void withLimitTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (n) WITH n LIMIT 2 RETURN n.name, n.age;" ); + assertEquals( 2, res.getData().length ); + } + + + @Test + public void withAndOrderByLimitTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( """ + MATCH (n) + WITH n ORDER BY n.name LIMIT 1 + RETURN n""" ); + + assertEquals( 1, res.getData().length ); + + containsNodes( res, true, TestNode.from( Pair.of( "name", "Ann" ), + Pair.of( "age", 45 ), + Pair.of( "depno", 13 ) ) ); + } + + + @Test + public void numberOfUpdatesLimitTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( """ + MATCH (n) + WITH n ORDER BY n.name LIMIT 1 + SET n.locked = true + RETURN n.name""" ); + + containsRows( res, true, false, Row.of( TestLiteral.from( "Max" ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/general/OrderByTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/OrderByTest.java new file mode 100644 index 0000000000..5e4e450089 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/OrderByTest.java @@ -0,0 +1,238 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.general; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class OrderByTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void singleOrderByTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name ORDER BY n.name ASC" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( "Hans" ) ), + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Max" ) ) ); + + res = execute( "MATCH (n) RETURN n.name ORDER BY n.name DESC" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Hans" ) ) ); + } + + + @Test + public void doubleOrderByTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name, n.age ORDER BY n.age ASC, n.name ASC" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( 31 ) ), + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ) ) ); + + res = execute( "MATCH (n) RETURN n.depno, n.name ORDER BY n.depno ASC, n.name DESC" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( 13 ), TestLiteral.from( "Bob" ) ), + Row.of( TestLiteral.from( 13 ), TestLiteral.from( "Ann" ) ) ); + } + + + @Test + public void nullOrderByTest() { + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name, n.age ORDER BY n.age ASC" ); + + assertTrue( containsRows( res, true, true, + Row.of( TestLiteral.from( "Kira" ), TestLiteral.from( 3 ) ), + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( null ) ) ) ); + + res = execute( "MATCH (n) RETURN n.name, n.age ORDER BY n.age DESC" ); + + assertTrue( containsRows( res, true, true, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( null ) ), + Row.of( TestLiteral.from( "Kira" ), TestLiteral.from( 3 ) ) ) ); + } + + + @Test + public void skipAndLimitWithSortTest() { + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name ORDER BY n.name DESC SKIP 1 LIMIT 2" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Kira" ) ) ); + } + + + @Test + public void orderByNotReturnedPropertyTest() { + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (n) RETURN n ORDER BY n.name DESC" ); + + containsRows( res, true, true, + Row.of( MAX ), + Row.of( MAX ), + Row.of( KIRA ), + Row.of( KIRA ), + Row.of( KIRA ) ); + } + + + @Test + public void renameWithClauseSortTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + GraphResult res = execute( "MATCH (p:Person) WITH p.age AS personAge RETURN personAge ORDER BY personAge" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 31 ) ), + Row.of( TestLiteral.from( 45 ) ) ); + } + + + @Test + public void renameWithClauseOrderByWithLimitTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + GraphResult res = execute( "MATCH (p:Person) WITH p.age AS age ORDER BY age DESC LIMIT 2 RETURN age" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( 45 ) ), + Row.of( TestLiteral.from( 32 ) ) ); + } + + + @Test + public void renameWithClauseDoubleOrderByTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (p:Person) WITH p.depno AS department , p.name AS name ORDER BY department ASC, name ASC RETURN department , name" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( 13 ), TestLiteral.from( "Ann" ) ), + Row.of( TestLiteral.from( 13 ), TestLiteral.from( "Bob" ) ), + Row.of( TestLiteral.from( 14 ), TestLiteral.from( "Alex" ) ) ); + } + + + @Test + public void renameWithClauseDoubleOrderByWithLimitTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (p:Person) WITH p.depno AS department , p.name AS name ORDER BY department ASC, name ASC LIMIT 3 RETURN department , name " ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( 13 ), TestLiteral.from( "Ann" ) ), + Row.of( TestLiteral.from( 13 ), TestLiteral.from( "Bob" ) ), + Row.of( TestLiteral.from( 14 ), TestLiteral.from( "Alex" ) ) ); + } + + + @Test + public void unwindSortTest() { + GraphResult res = execute( "UNWIND [1, true,3.14] AS i RETURN i ORDER BY i" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( true ) ), + Row.of( TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( 3.14 ) ) ); + } + + + @Test + public void renameWithClauseWithUnwindSortTest() { + GraphResult res = execute( "WITH [1 ,2 ,3] AS number UNWIND number AS n RETURN n ORDER BY n" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( 2 ) ), + Row.of( TestLiteral.from( 3 ) ) ); + } + + + @Test + public void renameWithMixedTypesWithUnwindSortTest() { + GraphResult res = execute( "WITH [1 ,2 ,'4'] AS number UNWIND number AS n RETURN n ORDER BY n" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( '4' ) ), + Row.of( TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( 2 ) ) ); + } + + + @Test + public void avgAggregateFieldSortTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + GraphResult res = execute( "MATCH (p:Person)RETURN p.depno As Department ,avg(p.age) AS averageAge ORDER BY averageAge" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 14 ), TestLiteral.from( 32 ) ), + Row.of( TestLiteral.from( 13 ), TestLiteral.from( 38 ) ) ); + } + + + @Test + public void renameWithClauseOrderByWithSkipLimitTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + GraphResult res = execute( "MATCH (p:Person) WITH p.age AS age ORDER BY age DESC SKIP 1 Limit 1 RETURN age" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 32 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/general/SkipTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/SkipTest.java new file mode 100644 index 0000000000..6ea39bca3f --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/SkipTest.java @@ -0,0 +1,113 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.general; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.cypher.helper.TestNode; +import org.polypheny.db.util.Pair; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class SkipTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleSkipTest() { + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name, n.age SKIP 3" ); + + assertEquals( 2, res.getData().length ); + } + + + @Test + public void orderBySkipTest() { + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name ORDER BY n.name DESC SKIP 3" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( "Kira" ) ), + Row.of( TestLiteral.from( "Kira" ) ) ); + } + + + @Test + public void withAndSkipTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (n) WITH n SKIP 2 RETURN n.name, n.age" ); + + assertEquals( 1, res.getData().length ); + } + + + @Test + public void returnSubsetSkipTest() { + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_ANIMAL ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name SKIP 3 LIMIT 1" ); + + assertEquals( 1, res.getData().length ); + } + + + @Test + public void withAndOrderBySkipTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( """ + MATCH (n) + WITH n ORDER BY n.name SKIP 1 + RETURN n""" ); + + assertEquals( 1, res.getData().length ); + + containsNodes( res, true, + TestNode.from( Pair.of( "name", "Bob" ), + Pair.of( "age", 31 ), + Pair.of( "depno", 13 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/general/UnionTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/UnionTest.java new file mode 100644 index 0000000000..08026a124d --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/UnionTest.java @@ -0,0 +1,223 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.general; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + +public class UnionTest extends CypherTestTemplate { + + protected static final String SINGLE_NODE_MOVIE = "CREATE (wallStreet:Movie {title: 'Wall Street' , released : 2002})"; + + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleUnionTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( """ + MATCH (n:Person) + RETURN n.name + UNION + MATCH (n:Person) + RETURN n.name""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Hans" ) ) ); + } + + + @Test + public void differentStructureUnionTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_MOVIE ); + + GraphResult res = execute( """ + MATCH (p:Person) + RETURN p.name AS name + UNION + MATCH (m :Movie) + RETURN p.Title AS name + """ ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Wall Street" ) ) ); + } + + + @Test + public void nullPropertiesUnionTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_MOVIE ); + + GraphResult res = execute( """ + MATCH (p:Person) + RETURN p.name AS name, p.age AS age, NULL AS title, NULL AS released + UNION + MATCH (m:Movie) + RETURN NULL AS name, NULL AS age, m.title AS title, m.released AS released + """ ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ), TestLiteral.from( null ), TestLiteral.from( null ) ), + Row.of( TestLiteral.from( null ), TestLiteral.from( null ), TestLiteral.from( "Wall Street" ), TestLiteral.from( 2002 ) ) ); + } + + + @Test + public void allUnionTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( """ + MATCH (n:Person) + RETURN n.name + UNION ALL + MATCH (n:Person) + RETURN n.name""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Hans" ) ), + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Hans" ) ) ); + } + + + @Test + public void differentStructureAllUnionTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_MOVIE ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_MOVIE ); + + GraphResult res = execute( """ + MATCH (p:Person) + RETURN p.name AS name + UNION ALL + MATCH (m :Movie) + RETURN p.Title AS name + """ ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Wall Street" ) ), + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Wall Street" ) ) ); + } + + + @Test + public void nullPropertiesAllUnionTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_MOVIE ); + execute( SINGLE_NODE_MOVIE ); + + GraphResult res = execute( """ + MATCH (p:Person) + RETURN p.name AS name, p.age AS age, NULL AS title, NULL AS released + UNION ALL + MATCH (m:Movie) + RETURN NULL AS name, NULL AS age, m.title AS title, m.released AS released + """ ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ), TestLiteral.from( null ), TestLiteral.from( null ) ), + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ), TestLiteral.from( null ), TestLiteral.from( null ) ), + Row.of( TestLiteral.from( null ), TestLiteral.from( null ), TestLiteral.from( "Wall Street" ), TestLiteral.from( 2002 ) ), + Row.of( TestLiteral.from( null ), TestLiteral.from( null ), TestLiteral.from( "Wall Street" ), TestLiteral.from( 2002 ) ) ); + } + + + @Test + public void distinctUnionTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( """ + MATCH (n:Person) + RETURN n.name + UNION DISTINCT + MATCH (n:Person) + RETURN n.name""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Hans" ) ) ); + } + + + @Test + public void differentStructureDistinctUnionTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_MOVIE ); + + GraphResult res = execute( """ + MATCH (p:Person) + RETURN p.name AS name + UNION DISTINCT + MATCH (m :Movie) + RETURN p.Title AS name + """ ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Wall Street" ) ) ); + } + + + @Test + public void nullPropertiesDistinctUnionTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_MOVIE ); + + GraphResult res = execute( """ + MATCH (p:Person) + RETURN p.name AS name, p.age AS age, NULL AS title, NULL AS released + UNION DISTINCT + MATCH (m:Movie) + RETURN NULL AS name, NULL AS age, m.title AS title, m.released AS released + """ ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ), TestLiteral.from( null ), TestLiteral.from( null ) ), + Row.of( TestLiteral.from( null ), TestLiteral.from( null ), TestLiteral.from( "Wall Street" ), TestLiteral.from( 2002 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/general/UnwindTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/UnwindTest.java new file mode 100644 index 0000000000..8a04f4a22f --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/UnwindTest.java @@ -0,0 +1,177 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.general; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class UnwindTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleUnwindTest() { + GraphResult res = execute( "UNWIND [1, 3, null] AS x RETURN x, 'val' AS y" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 1 ), TestLiteral.from( "val" ) ), + Row.of( TestLiteral.from( 3 ), TestLiteral.from( "val" ) ), + Row.of( TestLiteral.from( null ), TestLiteral.from( "val" ) ) ); + } + + + @Test + public void emptyUnwind() { + GraphResult res = execute( "UNWIND [] AS x RETURN x, 'val' AS y" ); + assertEmpty( res ); + } + + + @Test + public void nullUnwind() { + GraphResult res = execute( "UNWIND null AS x RETURN x, 'val' AS y" ); + assertEmpty( res ); + } + + + @Test + public void listOfListUnwind() { + GraphResult res = execute( "WITH [[1], [2, 4], 3] AS nested UNWIND nested AS x UNWIND x AS y RETURN y" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( 2 ) ), + Row.of( TestLiteral.from( 4 ) ), + Row.of( TestLiteral.from( 3 ) ) ); + } + + + @Test + public void nodePropertyUnwind() { + execute( "CREATE (n {key: [3,1]})" ); + GraphResult res = execute( "MATCH (n) UNWIND n.key AS x RETURN x" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 3 ) ), + Row.of( TestLiteral.from( 1 ) ) ); + } + + + @Test + public void minMaxAggregateSimpleUnwind() { + GraphResult res = execute( "UNWIND [1, 'a', NULL, 0.2, 'b', '1', '99'] AS val RETURN min(val)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( '1' ) ) ); + + res = execute( "UNWIND [1, 'a', NULL, 0.2, 'b', '1', '99'] As val RETURN max(val)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 1 ) ) ); + } + + + @Test + public void minMaxAggregateListOfListUnwind() { + GraphResult res = execute( "UNWIND ['d', [1, 2], ['a', 'c', 23]] AS val RETURN min(val)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( List.of( 'a', 'c', 23 ) ) ) ); + + res = execute( "UNWIND ['d', [1, 2], ['a', 'c', 23]] AS val RETURN max(val)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 'd' ) ) ); + } + + + @Test + public void maxMinAggregateNodePropertyUnwind() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (n) UNWIND n.age AS age RETURN max(age)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 45 ) ) ); + + res = execute( "MATCH (n) UNWIND n.age AS age RETURN min(age)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 31 ) ) ); + } + + + @Test + public void sumAggregateNodePropertyUnwind() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + GraphResult res = execute( "MATCH (n) UNWIND n.age AS age RETURN sum(age)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 76 ) ) ); + } + + + @Test + public void avgAggregateNodePropertyUnwind() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + GraphResult res = execute( "MATCH (n) UNWIND n.age AS age RETURN avg(age)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 38 ) ) ); + } + + + @Test + public void collectAggregateUnwind() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + GraphResult res = execute( "MATCH (n) UNWIND n.age AS age RETURN Collect(age)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( List.of( 45, 31 ) ) ) ); + } + + + @Test + public void countUnwind() { + GraphResult res = execute( "UNWIND [2, 1 , 1] AS i RETURN count( i)" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 3 ) ) ); + } + + + @Test + public void distinctUnwind() { + GraphResult res = execute( "UNWIND [3, 3 ,2 ,1 ] AS i RETURN DISTINCT i" ); + assertEquals( 3, res.getData().length ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 3 ) ), + Row.of( TestLiteral.from( 2 ) ), Row.of( TestLiteral.from( 1 ) ) ); + } + + + @Test + public void conditionalLogicUnwind() { + GraphResult res = execute( "UNWIND [1, 2, 3] AS number RETURN number, CASE WHEN number % 2 = 0 THEN 'even' ELSE 'odd' END AS type" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 1 ), TestLiteral.from( "odd" ) ), + Row.of( TestLiteral.from( 2 ), TestLiteral.from( "even" ) ), + Row.of( TestLiteral.from( 3 ), TestLiteral.from( "odd" ) ) ); + } + + + @Test + public void mapStructureUnwind() { + GraphResult res = execute( "UNWIND [{name: 'Alice', age: 30}] AS person RETURN person.name , person.age" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Alice" ) ), Row.of( TestLiteral.from( 30 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/general/WithTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/WithTest.java new file mode 100644 index 0000000000..d3b498d801 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/general/WithTest.java @@ -0,0 +1,358 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.general; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class WithTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void singleVariableWithTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (n:Person) WITH n RETURN n.name" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ), + Row.of( TestLiteral.from( "Hans" ) ) ); + } + + + @Test + public void multipleRenameVariablesWithTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) WITH p.name AS person_name , p.age AS person_age , p RETURN person_name, person_age , p.depno;" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ), TestLiteral.from( 13 ) ), + Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( 31 ), TestLiteral.from( 13 ) ) ); + } + + + @Test + public void renameWithTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (n:Person) WITH n.name AS name, n RETURN name, n" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), MAX ), + Row.of( TestLiteral.from( "Hans" ), HANS ) ); + } + + + @Test + public void startWithTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (n:Person) WITH n.name, n WHERE n.name STARTS WITH 'H' RETURN n" ); + containsRows( res, true, true, Row.of( HANS ) ); + } + + + @Test + public void startRenameWithTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (n:Person) WITH n.name as name , n WHERE name STARTS WITH 'H' RETURN n" ); + assertNode( res, 0 ); + containsRows( res, true, true, Row.of( HANS ) ); + } + + + @Test + public void endWithTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (n:Person) " + + "WITH n.name, n " + + "WHERE n.name ENDS WITH 'x' " + + "RETURN n.name, n" ); + assertNode( res, 1 ); + containsRows( res, true, true, Row.of( TestLiteral.from( "Max" ), MAX ) ); + } + + + @Test + public void endRenameWithTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (n:Person) " + + "WITH n.name AS name, n " + + "WHERE name ENDS WITH 'x' " + + "RETURN name, n" ); + assertNode( res, 1 ); + containsRows( res, true, true, Row.of( TestLiteral.from( "Max" ), MAX ) ); + } + + + @Test + public void containsWithTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_EDGE_1 ); + execute( SINGLE_EDGE_2 ); + + GraphResult res = execute( "MATCH (n:Person)-[]->(p:Animal) WITH *, n.name AS username WHERE username CONTAINS 'a' RETURN username, p" ); + assertNode( res, 1 ); + containsRows( res, true, true, Row.of( TestLiteral.from( "Max" ), KIRA ) ); + } + + + // aggregate + @Test + public void avgAggregationRenameWithTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) WITH avg(p.age) as ageAvg RETURN ageAvg " ); + containsRows( res, true, true, Row.of( TestLiteral.from( 38 ) ) ); + } + + + @Test + public void maxMinAggregationRenameWithTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) WITH MAX(p.age) as ageMax RETURN ageMax " ); + containsRows( res, true, true, Row.of( TestLiteral.from( 45 ) ) ); + + res = execute( "MATCH (p:Person) WITH MIN(p.age) as ageMin RETURN ageMin " ); + containsRows( res, true, true, Row.of( TestLiteral.from( 31 ) ) ); + } + + + @Test + public void countAggregationWithTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) WITH COUNT(*) as count RETURN count " ); + containsRows( res, true, true, Row.of( TestLiteral.from( 2 ) ) ); + + } + + + @Test + public void stdevAggregationWithTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) WITH STDEV(p.age) as ageStdev RETURN ageStdev " ); + containsRows( res, true, true, Row.of( TestLiteral.from( 9.8994949 ) ) ); + } + + + @Test + public void collectAggregationWithTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) WITH COLLECT(p.age) as ageList RETURN ageList " ); + containsRows( res, true, true, Row.of( TestLiteral.from( 45 ), TestLiteral.from( 31 ) ) ); + } + + + @Test + public void mapStructureRenameWithTest() { + GraphResult res = execute( "WITH {person: {name: 'Anne', age: 25}} AS p RETURN p" ); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void filterWithTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (p:Person) WITH p WHERE p.age > 31 RETURN p.name, p.age" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ) ), + Row.of( TestLiteral.from( "Alex" ), TestLiteral.from( 32 ) ) ); + } + + + @Test + public void mathematicalOperationWithTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (p:Person) WITH p, p.age * 2 AS double_age RETURN p.name, double_age" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 90.0 ) ), + Row.of( TestLiteral.from( "Alex" ), TestLiteral.from( 62.0 ) ), + Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( 64.0 ) ) ); + } + + + @Test + public void listWithTest() { + GraphResult res = execute( "WITH [1, 2, 3, 4, 5] AS numbers RETURN numbers" ); + containsRows( res, true, false, Row.of( TestLiteral.from( List.of( 1, 2, 3, 4, 5 ) ) ) ); + } + + + @Test + public void unwindListWithTest() { + GraphResult res = execute( "WITH [1, 2, 3, 4, 5] AS numbers UNWIND numbers AS number RETURN number" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( 2 ) ), + Row.of( TestLiteral.from( 3 ) ), + Row.of( TestLiteral.from( 4 ) ), + Row.of( TestLiteral.from( 5 ) ) ); + } + + + @Test + public void unwindAndFilterListWithTest() { + GraphResult res = execute( "WITH [1, 2, 3, 4, 5] AS numbers " + + "UNWIND numbers AS number " + + "WITH number " + + "WHERE number > 3 " + + "RETURN number" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 4 ) ), + Row.of( TestLiteral.from( 5 ) ) ); + } + + + @Test + public void unwindAndStartListWithTest() { + GraphResult res = execute( "WITH ['John', 'Mark', 'Jonathan', 'Bill'] AS somenames " + + "UNWIND somenames AS names " + + "WITH names AS candidate " + + "WHERE candidate STARTS WITH 'Jo' " + + "RETURN candidate" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "John" ) ), Row.of( TestLiteral.from( "Jonathan" ) ) ); + } + + + @Test + public void unwindAndLogicalOperatorsListWithTest() { + GraphResult res = execute( "WITH [2, 4, 7, 9, 12] AS numberlist " + + "UNWIND numberlist AS number " + + "WITH number " + + "WHERE number = 4 OR (number > 6 AND number < 10) " + + "RETURN number" ); + assertTrue( containsRows( res, false, false, + Row.of( TestLiteral.from( 4 ) ), + Row.of( TestLiteral.from( 7 ) ), + Row.of( TestLiteral.from( 9 ) ) ) ); + } + + + @Test + public void unwindAndWhereInListWithTest() { + GraphResult res = execute( "WITH [2, 3, 4, 5] AS numberlist " + + "UNWIND numberlist AS number " + + "WITH number " + + "WHERE number IN [2, 3, 8] " + + "RETURN number" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 2 ) ), Row.of( TestLiteral.from( 3 ) ) ); + } + + + @Test + public void createNodeWithTest() { + execute( "WITH [1, 1.0] AS list CREATE ({l: list})" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void distinctWithTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (p:Person) WITH Distinct(p) Return p " ); + assertEquals( 2, res.getData().length ); + } + + + @Test + public void existsWithTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (n) WITH n as person WHERE EXISTS(person.age) RETURN person.name, person.age" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ) ) ); + } + + + @Test + public void conditionalLogicWithTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (p:Person) " + + "WITH p, " + + " CASE " + + " WHEN p.age < 30 THEN 'Young'" + + " WHEN p.age >= 30 AND p.age < 60 THEN 'Middle-aged' " + + " ELSE 'Elderly' " + + " END AS ageGroup " + + "RETURN p.name, ageGroup" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( "Ana" ), TestLiteral.from( "Middle-aged" ) ), + Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( "Middle-aged" ) ), + Row.of( TestLiteral.from( "Alex" ), TestLiteral.from( "Middle-aged" ) ) ); + } + + + @Test + public void orderByWithTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (p:Person) " + + "WITH p " + + "ORDER BY p.name ASC " + + "RETURN p.name" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( "Hans" ) ), + Row.of( TestLiteral.from( "Max" ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/MatchTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/read/MatchTest.java similarity index 67% rename from dbms/src/test/java/org/polypheny/db/cypher/MatchTest.java rename to dbms/src/test/java/org/polypheny/db/cypher/clause/read/MatchTest.java index fd1aa3fe43..e45fb797f7 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/MatchTest.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/read/MatchTest.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package org.polypheny.db.cypher; +package org.polypheny.db.cypher.clause.read; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; import org.polypheny.db.cypher.helper.TestEdge; import org.polypheny.db.cypher.helper.TestLiteral; import org.polypheny.db.cypher.helper.TestNode; @@ -27,10 +28,10 @@ import org.polypheny.db.util.Pair; import org.polypheny.db.webui.models.results.GraphResult; + @Slf4j public class MatchTest extends CypherTestTemplate { - @BeforeEach public void reset() { tearDown(); @@ -41,6 +42,8 @@ public void reset() { /////////////////////////////////////////////// ///////// MATCH /////////////////////////////////////////////// + + @Test public void simpleMatchTest() { GraphResult res = execute( "MATCH (n) RETURN n" ); @@ -57,7 +60,6 @@ public void simpleMatchNoneTest() { GraphResult res = execute( "MATCH (n:Villain) RETURN n" ); assertNode( res, 0 ); assertEmpty( res ); - } @@ -68,8 +70,7 @@ public void simpleMatchLabelTest() { GraphResult res = execute( "MATCH (n:Person) RETURN n" ); assertNode( res, 0 ); - assert containsNodes( res, true, MAX ); - + containsNodes( res, true, MAX ); } @@ -105,21 +106,29 @@ public void simpleMatchMultiplePropertyTest() { res = execute( "MATCH (n {name: 'Kira', age: 3}) RETURN n" ); assertNode( res, 0 ); - assert containsNodes( res, true, TestNode.from( List.of( "Animal" ), Pair.of( "name", "Kira" ), Pair.of( "age", 3 ), Pair.of( "type", "dog" ) ) ); - + containsNodes( res, true, TestNode.from( List.of( "Animal" ), Pair.of( "name", "Kira" ), Pair.of( "age", 3 ), Pair.of( "type", "dog" ) ) ); } /////////////////////////////////////////////// ///////// PROJECT /////////////////////////////////////////////// + @Test + public void noLabelPropertyTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + GraphResult res = execute( "MATCH (n) RETURN n.age" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 45 ) ), + Row.of( TestLiteral.from( null ) ) ); + } + @Test public void simplePropertyTest() { execute( SINGLE_NODE_PERSON_1 ); GraphResult res = execute( "MATCH (n:Person) RETURN n.name" ); - assert is( res, Type.ANY, 0 ); - assert containsIn( res, true, 0, "n.name", TestLiteral.from( "Max" ) ); + is( res, Type.ANY, 0 ); + containsIn( res, true, 0, "n.name", TestLiteral.from( "Max" ) ); } @@ -127,10 +136,10 @@ public void simplePropertyTest() { public void simpleMultiplePropertyTest() { execute( SINGLE_NODE_ANIMAL ); GraphResult res = execute( "MATCH (n:Animal) RETURN n.name, n.age" ); - assert is( res, Type.ANY, 0 ); // we never have guaranties for the types - assert is( res, Type.ANY, 1 ); - assert containsIn( res, true, 0, "n.name", TestLiteral.from( "Kira" ) ); - assert containsIn( res, true, 1, "n.age", TestLiteral.from( "3" ) ); + is( res, Type.ANY, 0 ); // we never have guaranties for the types + is( res, Type.ANY, 1 ); + containsIn( res, true, 0, "n.name", TestLiteral.from( "Kira" ) ); + containsIn( res, true, 1, "n.age", TestLiteral.from( "3" ) ); } @@ -139,20 +148,19 @@ public void mixedNodeAndPropertyProjectTest() { execute( SINGLE_NODE_ANIMAL ); GraphResult res = execute( "MATCH (n:Person) RETURN n, n.age" ); assertNode( res, 0 ); - assert is( res, Type.ANY, 1 ); + is( res, Type.ANY, 1 ); } /////////////////////////////////////////////// ///////// EDGE /////////////////////////////////////////////// - @Test public void simpleEdgeTest() { execute( SINGLE_EDGE_1 ); GraphResult res = execute( "MATCH ()-[r]->() RETURN r" ); assertEdge( res, 0 ); - assert containsEdges( res, true, TestEdge.from( List.of( "OWNER_OF" ) ) ); + containsEdges( res, true, TestEdge.from( List.of( "OWNER_OF" ) ) ); } @@ -174,7 +182,7 @@ public void singleEdgeFilterTest() { assertEmpty( res ); res = execute( "MATCH ()-[r:KNOWS {since: 1994}]->() RETURN r" ); - assert containsEdges( res, true, TestEdge.from( List.of( "KNOWS" ), Pair.of( "since", 1994 ) ) ); + containsEdges( res, true, TestEdge.from( List.of( "KNOWS" ), Pair.of( "since", 1994 ) ) ); } @@ -183,7 +191,7 @@ public void singleEdgeFilterMatchNodeTest() { execute( SINGLE_EDGE_1 ); execute( SINGLE_EDGE_2 ); GraphResult res = execute( "MATCH (n:Person)-[r:KNOWS {since: 1994}]->() RETURN n" ); - assert containsNodes( res, true, TestEdge.from( List.of( "Person" ), Pair.of( "name", "Max" ) ) ); + containsNodes( res, true, TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ) ); } @@ -202,7 +210,7 @@ public void multipleHopTest() { res = execute( "MATCH ()-[r:FRIEND_OF {since:1995}]->()-[]-() RETURN r" ); assertEdge( res, 0 ); - assert containsEdges( res, true, TestEdge.from( List.of( "FRIEND_OF" ), Pair.of( "since", 1995 ) ) ); + containsEdges( res, true, TestEdge.from( List.of( "FRIEND_OF" ), Pair.of( "since", 1995 ) ) ); } @@ -214,7 +222,7 @@ public void repeatingHopTest() { GraphResult res = execute( "MATCH ()-[*2]->(n) RETURN n" ); assertNode( res, 0 ); - assert containsRows( res, true, true, Row.of( TestNode.from( List.of( "Animal" ), Pair.of( "name", "Kira" ) ) ) ); + containsRows( res, true, true, Row.of( TestNode.from( List.of( "Animal" ), Pair.of( "name", "Kira" ) ) ) ); } @@ -226,7 +234,7 @@ public void variableHopTest() { GraphResult res = execute( "MATCH ()-[*1..2]->(n) RETURN n" ); assertNode( res, 0 ); - assert containsRows( res, true, false, + containsRows( res, true, false, Row.of( KIRA ), Row.of( HANS_AGE ), Row.of( MAX ), @@ -240,7 +248,6 @@ assert containsRows( res, true, false, ///////// MIXED /////////////////////////////////////////////// - @Test public void simpleMixedRelationshipTest() { execute( SINGLE_EDGE_1 ); @@ -248,28 +255,25 @@ public void simpleMixedRelationshipTest() { GraphResult res = execute( "MATCH (h:Person)-[r:OWNER_OF]-(p:Animal) RETURN h,p" ); assertNode( res, 0 ); assertNode( res, 1 ); - assert containsIn( res, true, 0, MAX ); - assert containsIn( res, true, 1, TestNode.from( + containsIn( res, true, 0, MAX ); + containsIn( res, true, 1, TestNode.from( List.of( "Animal" ), Pair.of( "name", "Kira" ), Pair.of( "age", 3 ), Pair.of( "type", "dog" ) ) ); - } /////////////////////////////////////////////// ///////// "CROSS PRODUCT" MATCH /////////////////////////////////////////////// - @Test public void simpleCrossProductMatchTest() { execute( SINGLE_NODE_PERSON_1 ); GraphResult res = execute( "MATCH (p:Person),(n) RETURN p,n" ); assertNode( res, 0 ); assertNode( res, 1 ); - - assert containsRows( res, true, true, Row.of( MAX, MAX ) ); + containsRows( res, true, true, Row.of( MAX, MAX ) ); } @@ -280,8 +284,7 @@ public void simpleCrossProductBiMatchTest() { GraphResult res = execute( "MATCH (p:Person),(n) RETURN p,n" ); assertNode( res, 0 ); assertNode( res, 1 ); - - assert containsRows( res, true, false, + containsRows( res, true, false, Row.of( MAX, MAX ), Row.of( HANS, MAX ), Row.of( MAX, HANS ), @@ -296,9 +299,7 @@ public void simpleTriCrossProductMatchTest() { assertNode( res, 0 ); assertNode( res, 1 ); assertNode( res, 2 ); - - assert containsRows( res, true, true, Row.of( MAX, MAX, MAX ) ); - + containsRows( res, true, true, Row.of( MAX, MAX, MAX ) ); } @@ -308,13 +309,17 @@ public void simpleNeighborMatchTest() { execute( SINGLE_EDGE_2 ); execute( SINGLE_NODE_PERSON_COMPLEX_1 ); execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH (p:Person {name:'Max'})-[]-(t) RETURN t" ); assertNode( res, 0 ); - - assert containsRows( res, true, false, + containsRows( res, true, false, Row.of( KIRA ), Row.of( TestNode.from( List.of( "Person" ), Pair.of( "name", "Hans" ), Pair.of( "age", 31 ) ) ) ); + res = execute( "MATCH (p:Person {name:'Max'})-->(t) RETURN t " ); + containsRows( res, true, false, + Row.of( KIRA ), + Row.of( TestNode.from( List.of( "Person" ), Pair.of( "name", "Hans" ), Pair.of( "age", 31 ) ) ) ); } @@ -327,7 +332,7 @@ public void triCrossProductMatchTest() { assertNode( res, 1 ); assertNode( res, 2 ); - assert containsRows( res, true, false, + containsRows( res, true, false, Row.of( MAX, MAX, MAX ), Row.of( HANS, MAX, MAX ), Row.of( MAX, HANS, MAX ), @@ -342,13 +347,20 @@ assert containsRows( res, true, false, ///////// PATH /////////////////////////////////////////////// - @Test public void matchPathTest() { execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH (m {name:'Max'}), (k {name:'Kira'}), p = (m)-[]-(k)\n" + "RETURN p" ); - assert containsRows( res, true, true, + containsRows( res, true, true, + Row.of( TestPath.of( + TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ), + TestEdge.from( List.of( "OWNER_OF" ) ), + KIRA ) ) ); + + res = execute( "MATCH Path = (p:Person {name: 'Max'})-[rel:OWNER_OF]->(a:Animal {name:'Kira', age:3, type:'dog'}) RETURN Path " ); + containsRows( res, true, true, Row.of( TestPath.of( TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ), TestEdge.from( List.of( "OWNER_OF" ) ), @@ -356,4 +368,66 @@ assert containsRows( res, true, true, } + @Test + public void returnPathsByAsteriskMatchTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH Path = () -[]->() RETURN *" ); + containsRows( res, true, false, + Row.of( TestPath.of( MAX, TestEdge.from( List.of( "OWNER_OF" ) ), KIRA ) ) ); + } + + + @Test + public void returnNodesByAsteriskMatchTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH (p) RETURN *" ); + containsRows( res, true, false, + Row.of( MAX ), + Row.of( KIRA ) ); + } + + + @Test + public void returnBothSideByAsteriskMatchTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH (p) -[]->(m) RETURN *" ); + containsRows( res, true, false, + Row.of( MAX ), + Row.of( KIRA ) ); + } + + + @Test + public void returnAllElementsByAsteriskMatchTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH Path = (p) -[r] ->(m) RETURN *" ); + containsRows( res, true, false, + Row.of( + TestPath.of( MAX, TestEdge.from( List.of( "OWNER_OF" ) ), KIRA ), + MAX, + TestEdge.from( List.of( "OWNER_OF" ) ), KIRA ) ); + } + + + @Test + public void returnEdgesByAsteriskMatchTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH ()-[r]-() RETURN *" ); + containsEdges( res, true, TestEdge.from( List.of( "OWNER_OF" ) ) ); + } + + + @Test + public void shortestPathMatchTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH (b:Person {name: 'Max'}), (a:Animal {name: 'Kira'}) " + + "MATCH p = shortestPath((b)-[*]-(a)) " + + "RETURN p" ); + containsRows( res, true, true, + Row.of( TestPath.of( + TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ), + TestEdge.from( List.of( "OWNER_OF" ) ), + KIRA ) ) ); + } + } diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/write/DmlDeleteTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/DmlDeleteTest.java new file mode 100644 index 0000000000..32afe474fd --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/DmlDeleteTest.java @@ -0,0 +1,151 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.write; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestNode; +import org.polypheny.db.util.Pair; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class DmlDeleteTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleEmptyNodeDeleteTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( "MATCH (p:Person) DELETE p" ); + GraphResult res = matchAndReturnAllNodes(); + assertEmpty( res ); + } + + + @Test + public void simpleNodeDeleteTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( "MATCH (p:Person {name: 'Max'}) DELETE p" ); + GraphResult res = matchAndReturnAllNodes(); + assertEmpty( res ); + } + + + @Test + public void simpleFilteredNodeDeleteTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( "MATCH (p:Person {name: 'Max'}) DELETE p" ); + GraphResult res = matchAndReturnAllNodes(); + containsRows( res, true, false, + Row.of( TestNode.from( List.of( "Person" ), Pair.of( "name", "Hans" ) ) ) ); + } + + + @Test + public void twoNodeDeleteTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( "MATCH (p:Person {name: 'Max'}), (h:Person {name: 'Hans'})\n" + + "DELETE p, h" ); + assertEmpty( res ); + } + + + @Test + public void simpleRelationshipDeleteTest() { + execute( SINGLE_EDGE_1 ); + execute( "MATCH (:Person {name: 'Max'})-[rel:OWNER_OF]->(:Animal {name: 'Kira'}) \n" + + "DELETE rel" ); + + GraphResult res = execute( "MATCH (n)-[rel:OWNER_OF]->(a) RETURN rel" ); + assertEmpty( res ); + + res = matchAndReturnAllNodes(); + containsRows( res, true, false, + Row.of( TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ) ), + Row.of( TestNode.from( + List.of( "Animal" ), + Pair.of( "name", "Kira" ), + Pair.of( "age", 3 ), + Pair.of( "type", "dog" ) ) ) ); + } + + + @Test + public void relationshipWithPropertiesDeleteTest() { + execute( SINGLE_EDGE_2 ); + execute( "MATCH (:Person {name: 'Max'})-[rel:KNOWS {since: 1994}]->(:Person {name:'Hans'})\n" + + "DELETE rel" ); + + GraphResult res = execute( "MATCH (p1)-[rel:KNOWS ]->(p2) RETURN rel " ); + assertEmpty( res ); + + res = matchAndReturnAllNodes(); + containsRows( res, true, false, + Row.of( TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ) ), + Row.of( TestNode.from( + List.of( "Person" ), + Pair.of( "name", "Hans" ), + Pair.of( "age", 31 ) ) ) ); + } + + + @Test + public void pathDeleteTest() { + execute( SINGLE_EDGE_1 ); + execute( "MATCH p = (person:Person {name: 'Max'})-[rel:OWNER_OF]->( animal :Animal {name: 'Kira'}) DELETE p" ); + + GraphResult res = matchAndReturnAllNodes(); + GraphResult relations = execute( "MATCH (p:Person) -[rel:OWNER_OF]->(A:Animal) RETURN rel " ); + assertTrue( res.getData().length == 0 && relations.getData().length == 0 ); + } + + + @Test + public void nodeWithAllRelationshipsDeleteTest() { + execute( SINGLE_EDGE_2 ); + execute( "MATCH (n:Person {name: 'MAX'}) DETACH DELETE n" ); + + GraphResult res = matchAndReturnAllNodes(); + GraphResult relations = execute( "MATCH (p:Person) -[rel:OWNER_OF]->(A:Animal) Return rel" ); + assertTrue( res.getData().length == 0 && relations.getData().length == 0 ); + } + + + @Test + public void allNodesAndRelationshipsDeleteTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_EDGE_1 ); + execute( "MATCH (n) DETACH DELETE n" ); + + GraphResult res = matchAndReturnAllNodes(); + GraphResult relations = execute( "MATCH (p:Person) -[rel:OWNER_OF]->(A:Animal) Return rel" ); + assertTrue( res.getData().length == 0 && relations.getData().length == 0 ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/write/DmlInsertTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/DmlInsertTest.java new file mode 100644 index 0000000000..bb89632d33 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/DmlInsertTest.java @@ -0,0 +1,503 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.write; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestEdge; +import org.polypheny.db.cypher.helper.TestNode; +import org.polypheny.db.util.Pair; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class DmlInsertTest extends CypherTestTemplate { + + public static final String CREATE_PERSON_MAX = "CREATE (p:Person {name: 'Max Muster'})"; + + public static final String CREATE_COMPLEX_GRAPH_1 = + """ + CREATE + (adam:User {name: 'Adam'}), + (pernilla:User {name: 'Pernilla'}), + (david:User {name: 'David'}), + (adam)-[:FRIEND]->(pernilla), + (pernilla)-[:FRIEND]->(david)"""; + + public static final String CREATE_COMPLEX_GRAPH_2 = + "CREATE (adam:User {name: 'Adam'}), (pernilla:User {name: 'Pernilla'}), (david:User {name: 'David'}), (adam)-[:FRIEND]->(pernilla), (pernilla)-[:FRIEND]->(david), (david)-[:FRIEND]->(adam)"; + + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void insertEmptyNodeWithNoVariableNoLabelTest() { + execute( "CREATE ()" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of() ) ); + } + + + @Test + public void insertEmptyNodeWithNoVariableTest() { + execute( "CREATE (:Person)" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of( "Person" ) ) ); + } + + + @Test + public void insertEmptyNoLabelNodeTest() { + execute( "CREATE (p)" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of() ) ); + } + + + @Test + public void insertEmptyNodeTest() { + execute( "CREATE (p:Person)" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of( "Person" ) ) ); + } + + + @Test + public void insertNodeTest() { + execute( "CREATE (p:Person {name: 'Max Muster'})" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( Pair.of( "name", "Max Muster" ) ) ); + } + + + @Test + public void insertNodeWithManyLabelsTest() { + execute( "CREATE (p:Person :Employee )" ); + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, TestNode.from( List.of( "Person", "Employee" ) ) ); + } + + + @Test + public void insertNodeWithManyLabelsAndAsPropertyTest() { + execute( "CREATE (n:Person:Employee {name :'Max'})" ); + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, TestNode.from( List.of( "Person", "Employee" ), Pair.of( "name", "Max" ) ) ); + } + + + @Test + public void insertBigIntegerExceeded32BitAsPropertyValeTest() { + execute( "CREATE (n : node { id : 4294967296 })" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, TestNode.from( List.of( "node" ), Pair.of( "id", 4294967296L ) ) ); + } + + + @Test + public void insertMaxLongAsPropertyValueTest() { + execute( "CREATE (n : node { id : 9223372036854775807})" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, TestNode.from( List.of( "node" ), Pair.of( "id", 9223372036854775807L ) ) ); + } + + + @Test + public void insertResultOfMathematicalOperationsTest() { + execute( "CREATE (n : node { id : 1*2+6/3 })" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, TestNode.from( List.of( "node" ), Pair.of( "id", 9223372036854775807L ) ) ); + } + + + @Test + public void insertENotationAsPropertyValueTest() { + execute( "CREATE (n : node { id : 1e2})" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, TestNode.from( List.of( "node" ), Pair.of( "id", 100 ) ) ); + } + + + @Test + public void insertMaxLongAsResultOfMathematicalOperationsTest() { + execute( "CREATE (n : node { id : 2^63-1})" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, TestNode.from( List.of( "node" ), Pair.of( "id", 4 ) ) ); + } + + + @Test + public void insertMinLongAsPropertyValueTest() { + execute( "CREATE (n : node { id : -9223372036854775808L})" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, TestNode.from( List.of( "node" ), Pair.of( "id", -9223372036854775808L ) ) ); + } + + + @Test + public void insertMaxDoubleAsPropertyValueTest() { + execute( "CREATE (n : node { id : 1.7976931348623157e+308})" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, TestNode.from( List.of( "node" ), Pair.of( "id", 1.7976931348623157e+308 ) ) ); + } + + + @Test + public void insertMinDoubleAsPropertyValueTest() { + execute( "CREATE (n : node { id : 4.9e-324 })" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( res.getData().length, 1 ); + containsNodes( res, true, TestNode.from( List.of( "node" ), Pair.of( "id", 4.9e-324 ) ) ); + } + + + @Test + public void insertHexadecimalIntegerAsPropertyValueTest() { + execute( "CREATE (n : node { id : 0x13af})" ); + execute( "CREATE (n : node { id : -0x66eff})" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, + TestNode.from( List.of( "node" ), Pair.of( "id", 5039 ) ), + TestNode.from( List.of( "node" ), Pair.of( "id", -421631 ) ) ); + } + + + @Test + public void insertOctalIntegerAsPropertyValueTest() { + execute( "CREATE (n : node { id : 0o1372})" ); + execute( "CREATE (n : node { id :-0o5671 })" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, + TestNode.from( List.of( "node" ), Pair.of( "id", 762 ) ), + TestNode.from( List.of( "node" ), Pair.of( "id", -3001 ) ) ); + } + + + @Test + public void insertTabCharWithPropertyValueTest() { + execute( "CREATE (n : node { title : \"Hello\\tWorld\" })" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, + TestNode.from( List.of( "node" ), Pair.of( "title", "Hello\tWorld" ) ) ); + } + + + @Test + public void insertBackspaceCharWithPropertyValueTest() { + execute( "CREATE (n : node { title : \"Hello\\bWorld\" })" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, + TestNode.from( List.of( "node" ), Pair.of( "title", "Hello\bWorld" ) ) ); + } + + + @Test + public void insertNewlineWithPropertyValueTest() { + execute( "CREATE (n : node { title : \"Hello\\nWorld\" })" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, + TestNode.from( List.of( "node" ), Pair.of( "title", "Hello\nWorld" ) ) ); + } + + + @Test + public void insertSingleQuoteCharWithPropertyValueTest() { + execute( "CREATE (n : node { title : 'Hello\\'World' })" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, + TestNode.from( List.of( "node" ), Pair.of( "title", "Hello'World" ) ) ); + } + + + @Test + public void insertMultiplePropertiesWithSingleQuoteValuesTest() { + execute( " CREATE (n:node { name: \"Xi'an\", url: \"http://dbpedia.org/resource/Xi'an\"})" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, + TestNode.from( List.of( "node" ), Pair.of( "name", "Xi'an" ), + Pair.of( "url", "http://dbpedia.org/resource/Xi'an" ) ) ); + } + + + @Test + public void insertDoubleQuoteCharWithPropertyValueTest() { + execute( "CREATE (n : node { title : \"Hello\\\"World\" })" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + containsNodes( res, true, + TestNode.from( List.of( "node" ), Pair.of( "title", "Hello\"World" ) ) ); + } + + + @Test + public void insertNodeWithPropertyContainsListTest() { + execute( "CREATE ({l: [1 ,2,3]})" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void insertTwoNodeTest() { + execute( CREATE_PERSON_MAX ); + execute( CREATE_PERSON_MAX ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, + TestNode.from( Pair.of( "name", "Max Muster" ) ), + TestNode.from( Pair.of( "name", "Max Muster" ) ) ); + } + + + @Test + public void insertMultipleNodesTest() { + execute( "CREATE (p),(n),(m)" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from(), TestNode.from(), TestNode.from() ); + } + + + @Test + public void insertPropertyTypeTest() { + execute( "CREATE (p:Person {name: 'Max Muster', age: 13, height: 185.3, nicknames: [\"Maxi\",\"Musti\"]})" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, + TestNode.from( + Pair.of( "name", "Max Muster" ), + Pair.of( "age", 13 ), + Pair.of( "height", 185.3 ), + Pair.of( "nicknames", List.of( "Maxi", "Musti" ) ) + ) ); + } + + + @Test + @Disabled + public void insertReturnNodeTest() { + GraphResult res = execute( + "CREATE (p:Person {name: 'Max Muster'})\n" + + "RETURN p" ); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of() ) ); + } + + + @Test + public void insertSingleHopPathNoVariableTest() { + execute( "CREATE (p:Person {name :'Max'}) -[ : OWNER_OF] ->(a: Animal {name :'Kira' , age: 3 , type :'dog'})" ); + GraphResult res = matchAndReturnAllNodes(); + // only select all nodes + containsNodes( res, true, + TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ), + TestNode.from( + List.of( "Animal" ), + Pair.of( "name", "Kira" ), + Pair.of( "age", 3 ), + Pair.of( "type", "dog" ) ) ); + + // only select all edges + res = execute( "MATCH ()-[r]->() RETURN r" ); + containsEdges( res, true, TestEdge.from( List.of( "OWNER_OF" ) ) ); + } + + + @Test + public void insertSingleHopPathTest() { + execute( "CREATE (p:Person {name: 'Max'})-[rel:OWNER_OF]->(a:Animal {name:'Kira', age:3, type:'dog'})" ); + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ), + TestNode.from( + List.of( "Animal" ), + Pair.of( "name", "Kira" ), + Pair.of( "age", 3 ), + Pair.of( "type", "dog" ) ) ); + } + + + @Test + public void insertSingleHopPathEdgesTest() { + execute( "CREATE (p:Person {name: 'Max'})-[rel:OWNER_OF]->(a:Animal {name:'Kira', age:3, type:'dog'})" ); + GraphResult res = execute( "MATCH ()-[r]->() RETURN r" ); + containsEdges( res, true, TestEdge.from( List.of( "OWNER_OF" ) ) ); + } + + + @Test + public void insertSingleHopPathWithMultiplePropertiesTest() { + execute( "CREATE (p:Person {name: 'Max'})-[rel:KNOWS {since: 1994 , relation : 'friend'}]->(a:Person {name:'Hans', age:31})" ); + + // only select all nodes + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ), + TestNode.from( + List.of( "Person" ), + Pair.of( "name", "Hans" ), + Pair.of( "age", 31 ) ) ); + + // only select all edges + res = execute( "MATCH ()-[r]->() RETURN r" ); + containsEdges( res, true, TestEdge.from( List.of( "KNOWS" ), + Pair.of( "since", 1994 ), Pair.of( "relation", "friend" ) ) ); + } + + + @Test + public void insertSingleHopPathWithListPropertyTest() { + execute( "Create (p:Person:Employee {name: 'Max'})-[role:ACTED_IN {roles:['Neo', 42, 'Thomas Anderson']}]->(matrix:Movie {title: 'The Matrix'})" ); + // only select all nodes + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person", "Employee" ), Pair.of( "name", "Max" ) ), + TestNode.from( + List.of( "Movie" ), + Pair.of( "title", "The Matrix" ) ) ); + + // only select all edges + res = execute( "MATCH ()-[r]->() RETURN r" ); + containsEdges( res, true, TestEdge.from( List.of( "ACTED_IN" ), + Pair.of( "roles", List.of( "Neo", 42, "Thomas Anderson" ) ) ) ); + } + + + @Test + public void insertMultipleHopPathTest() { + execute( "CREATE (n:Person)-[f:FRIEND_OF]->(p:Person {name: 'Max'})-[rel:OWNER_OF]->(a:Animal {name:'Kira'})" ); + + // only select all nodes + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person" ) ), + TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ) ), + TestNode.from( List.of( "Animal" ), Pair.of( "name", "Kira" ) ) ); + + // only select all edges + res = execute( "MATCH ()-[r]->() RETURN r" ); + containsRows( res, true, false, + Row.of( TestEdge.from( List.of( "OWNER_OF" ) ) ), + Row.of( TestEdge.from( List.of( "FRIEND_OF" ) ) ) ); + } + + + @Test + public void insertAllInOneTest() { + execute( CREATE_COMPLEX_GRAPH_1 ); + + GraphResult res = execute( "MATCH (n) RETURN n" ); + assertEquals( 3, res.getData().length ); + assertNode( res, 0 ); + + res = execute( "MATCH ()-[r]-() RETURN r" ); + assertEdge( res, 0 ); + // double matches of same path is correct, as no direction is specified + assertEquals( 4, res.getData().length ); + + res = execute( "MATCH ()-[r]->() RETURN r" ); + assertEdge( res, 0 ); + // double matches of same path is correct, as no direction is specified + assertEquals( 2, res.getData().length ); + } + + + @Test + public void insertAllInOneCircleTest() { + execute( CREATE_COMPLEX_GRAPH_2 ); + GraphResult res = execute( "MATCH (n) RETURN n" ); + assertEquals( 3, res.getData().length ); + assertNode( res, 0 ); + + res = execute( "MATCH ()-[r]-() RETURN r" ); + assertEdge( res, 0 ); + assertEquals( 6, res.getData().length ); + } + + + @Test + public void insertAdditionalEdgeTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + execute( "MATCH (max:Person {name: 'Max'}), (hans:Person {name: 'Hans'})\n" + + "CREATE (max)-[:KNOWS]->(hans)" ); + + GraphResult res = execute( "MATCH ()-[r]->() RETURN r" ); + + containsRows( res, true, true, + Row.of( TestEdge.from( List.of( "KNOWS" ) ) ) ); + } + + + @Test + public void insertAdditionalEdgeOneSideTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + execute( "MATCH (max:Person {name: 'Max'})\n" + + "CREATE (max)-[:KNOWS]->(hans:Person {name: 'Hans'})" ); + + GraphResult res = execute( "MATCH ()-[r]->() RETURN r" ); + + containsRows( res, true, true, + Row.of( TestEdge.from( List.of( "KNOWS" ) ) ) ); + } + + + @Test + public void insertAdditionalEdgeOneSideBothSideTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + execute( "MATCH (max:Person {name: 'Max'})\n" + + "CREATE (max)-[:KNOWS]->(hans:Person {name: 'Hans'})-[:KNOWS]->(peter:Person {name: 'Peter'})" ); + GraphResult res = execute( "MATCH ()-[r]->() RETURN r" ); + containsRows( res, true, true, + Row.of( TestEdge.from( List.of( "KNOWS" ) ) ), + Row.of( TestEdge.from( List.of( "KNOWS" ) ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/DmlUpdateTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/DmlUpdateTest.java similarity index 50% rename from dbms/src/test/java/org/polypheny/db/cypher/DmlUpdateTest.java rename to dbms/src/test/java/org/polypheny/db/cypher/clause/write/DmlUpdateTest.java index cf72fa3761..f99b982d38 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/DmlUpdateTest.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/DmlUpdateTest.java @@ -14,16 +14,18 @@ * limitations under the License. */ -package org.polypheny.db.cypher; +package org.polypheny.db.cypher.clause.write; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; import org.polypheny.db.cypher.helper.TestNode; import org.polypheny.db.util.Pair; import org.polypheny.db.webui.models.results.GraphResult; + public class DmlUpdateTest extends CypherTestTemplate { @BeforeEach @@ -40,7 +42,7 @@ public void updatePropertyTest() { + "SET a.age = 25" ); GraphResult res = matchAndReturnAllNodes(); - assert containsRows( res, true, true, + containsRows( res, true, true, Row.of( TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ), Pair.of( "age", 25 ) ) ) ); } @@ -52,7 +54,7 @@ public void updateLabelTest() { + "SET a:Swiss" ); GraphResult res = matchAndReturnAllNodes(); - assert containsRows( res, true, true, + containsRows( res, true, true, Row.of( TestNode.from( List.of( "Person", "Swiss" ), Pair.of( "name", "Max" ) ) ) ); } @@ -64,7 +66,7 @@ public void updateLabelsTest() { + "SET a:Swiss:German" ); GraphResult res = matchAndReturnAllNodes(); - assert containsRows( res, true, true, + containsRows( res, true, true, Row.of( TestNode.from( List.of( "Person", "Swiss", "German" ), Pair.of( "name", "Max" ) ) ) ); } @@ -76,7 +78,7 @@ public void updateVariablesReplaceTest() { + "SET a = {} " ); GraphResult res = matchAndReturnAllNodes(); - assert containsRows( res, true, true, + containsRows( res, true, true, Row.of( TestNode.from( List.of( "Person" ) ) ) ); } @@ -85,23 +87,55 @@ assert containsRows( res, true, true, public void updateVariablesIncrementTest() { execute( SINGLE_NODE_PERSON_1 ); execute( "MATCH (a:Person {name: 'Max'})\n" - + "SET a = { age: 13, job: 'Developer'} " ); + + "SET a = { name : 'Max' , age: 13, job: 'Developer'} " ); GraphResult res = matchAndReturnAllNodes(); - assert containsRows( res, true, true, + containsRows( res, true, true, Row.of( TestNode.from( List.of( "Person" ), + Pair.of( "name", "Max" ), Pair.of( "age", 13 ), Pair.of( "job", "Developer" ) ) ) ); } + @Test + public void updateVariablesDecrementTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( "MATCH (p {name: 'Ann'})\n" + + "SET p = {name: 'Ann Smith'}" ); + + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), + Pair.of( "name", "'Ann Smith" ) ) ); + } + + + @Test + public void updateVariablesIncrementAndDecrementTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( """ + MATCH (p {name: 'Ann'}) + SET p = {name: 'Peter Smith', position: 'Entrepreneur'} + RETURN p.name, p.age, p.position""" ); + + GraphResult res = matchAndReturnAllNodes(); + containsRows( res, true, true, + Row.of( TestNode.from( + List.of( "Person" ), + Pair.of( "name", "Peter Smith" ), + Pair.of( "position", "Entrepreneur" ) ) ) ); + } + + @Test @Disabled // Extension of Cypher implementation required public void updatePropertyReturnTest() { - execute( "MATCH (a:Animal {name: 'Kira'})\n" - + "SET a.age = 4\n" - + "RETURN a" ); + execute( """ + MATCH (a:Animal {name: 'Kira'}) + SET a.age = 4 + RETURN a""" ); } @@ -120,4 +154,56 @@ public void updateRelationshipNewPropertyTest() { + "SET rel.status = 'fresh'" ); } + + @Test + public void updateCaseWhenTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( "MATCH (n {name: 'Ann'})\n" + + "SET (CASE WHEN n.age = 45 THEN n END).worksIn = 'Malmo" ); + + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), + Pair.of( "name", "Ann" ), + Pair.of( "worksIn", "Malmo" ) ) ); + + execute( "MATCH (n {name: 'Ann'})\n" + + "SET (CASE WHEN n.age = 45 THEN n END).name = 'Max" + + "RETURN n.name, n.age" ); + + containsNodes( res, true, + TestNode.from( List.of( "Person" ), + Pair.of( "name", "Max" ), + Pair.of( "age", 45 ) ) ); + } + + + @Test + public void updatePropertyWithNullTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( """ + MATCH (n {name: 'Ann'}) + SET n.name = null + """ ); + + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), + Pair.of( "age", 54 ) ) ); + } + + + @Test + public void updateMultiplePropertiesWithOneSetTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( "MATCH (n {name: 'Max'})\n" + + "SET n.position = 'Developer', n.surname = 'Taylor'" ); + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), + Pair.of( "name", "Max" ), + Pair.of( "position", "Developer" ), + Pair.of( "surname", "Taylor " ) ) ); + } + } diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/write/ForeachTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/ForeachTest.java new file mode 100644 index 0000000000..11e95a38e1 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/ForeachTest.java @@ -0,0 +1,144 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.write; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.cypher.helper.TestNode; +import org.polypheny.db.util.Pair; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class ForeachTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void createWithForeachTest() { + execute( """ + WITH ['Alice', 'Bob', 'Charlie'] AS names + FOREACH (name IN names | + CREATE (p:Person {name: name}) + )""" ); + + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 3, res.getData().length ); + containsNodes( res, true, + TestNode.from( Pair.of( "name", "Alice" ) ), + TestNode.from( Pair.of( "name", "Bob" ) ), + TestNode.from( Pair.of( "name", "Charlie" ) ) ); + } + + + @Test + public void mergeWithForeachTest() { + execute( """ + WITH ['Alice', 'Bob', 'Charlie'] AS names + FOREACH (name IN names | + MERGE (p:Person {name: name}) + )""" ); + + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 3, res.getData().length ); + containsNodes( res, true, + TestNode.from( Pair.of( "name", "Alice" ) ), + TestNode.from( Pair.of( "name", "Bob" ) ), + TestNode.from( Pair.of( "name", "Charlie" ) ) ); + } + + + @Test + public void deleteWithForeachTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + execute( """ + MATCH (p:Person) + WITH collect(p) AS people + FOREACH (p IN people | + DELETE p + )""" ); + GraphResult res = matchAndReturnAllNodes(); + assertEmpty( res ); + } + + + @Test + public void removeWithForeachTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + execute( """ + MATCH (p:Person) + WITH collect(p) AS people + FOREACH (p IN people | + REMOVE p.name\s + )""" ); + + GraphResult res = matchAndReturnAllNodes(); + containsRows( res, true, false, Row.of( TestLiteral.from( null ) ), + Row.of( TestLiteral.from( null ) ) ); + } + + + @Test + public void updateWithForeachTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( """ + MATCH (p:Person) + WITH collect(p) AS people + FOREACH (p IN people | + SET p.status = 'active' + )""" ); + + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), Pair.of( "name", "Max" ), Pair.of( "status", "active" ) ), + TestNode.from( List.of( "Person" ), Pair.of( "name", "Hans" ), Pair.of( "status", "active" ) ) ); + } + + + @Test + public void nestedForeachTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( """ + MATCH (p:Person) + WITH collect(p) AS people + FOREACH (p1 IN people | + FOREACH (p2 IN people | + CREATE (p1)-[:KNOWS]->(p2) + ) + )""" ); + GraphResult res = execute( "MATCH (p1)-[r:KNOWS]->(p2) RETURN r" ); + assertEquals( 4, res.getData().length ); + res = execute( "MATCH (p1)-[r:KNOWS]-(p2) RETURN r" ); + assertEquals( 6, res.getData().length ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/write/MergeTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/MergeTest.java new file mode 100644 index 0000000000..1261e4462a --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/MergeTest.java @@ -0,0 +1,399 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.write; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestEdge; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.cypher.helper.TestNode; +import org.polypheny.db.util.Pair; +import org.polypheny.db.webui.models.results.GraphResult; + + +@Slf4j +public class MergeTest extends CypherTestTemplate { + + protected static final String SINGLE_NODE_PERSON_COMPLEX_4 = "CREATE (charlie:Person {name: 'Charlie Sheen', bornIn: 'New York', chauffeurName: 'John Brown'})"; + protected static final String SINGLE_NODE_PERSON_COMPLEX_5 = "CREATE (martin:Person {name: 'Martin Sheen', bornIn: 'Ohio', chauffeurName: 'Bob Brown'})"; + protected static final String SINGLE_NODE_PERSON_COMPLEX_6 = "CREATE (michael:Person {name: 'Michael Douglas', bornIn: 'New Jersey', chauffeurName: 'John Brown'})"; + + protected static final String SINGLE_NODE_MOVIE = "CREATE (wallStreet:Movie {title: 'Wall Street'})"; + + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void singleNodeMergeTest() { + execute( "MERGE (P)" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of() ) ); + } + + + @Test + public void returnSingleNodeMergeTest() { + GraphResult res = execute( "MERGE (P) Return P " ); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of() ) ); + } + + + @Test + public void singleNodeWithLabelMergeTest() { + // new node + execute( "MERGE (p:Person)" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of( "Person" ) ) ); + // System.out.print( res.getData().length ); return zero + + // exist node + execute( "MERGE (n:Person)" ); + res = matchAndReturnAllNodes(); + + // num of nodes should be one + assertEquals( 1, res.getData().length ); + } + + + @Test + public void singleNodeWithMultipleLabelsMergeTest() { + // new node + execute( "MERGE (robert:Critic:Viewer)" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of( "Critic", "Viewer" ) ) ); + + // exist node + execute( "MERGE (robert:Critic:Viewer)" ); + res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void singleNodeWithSinglePropertyMergeTest() { + execute( "MERGE (charlie {name: 'Charlie Sheen'})" ); + + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of(), + Pair.of( "name", "Charlie Sheen" ) ) ); + + execute( "MERGE (charlie {name: 'Charlie Sheen'})" ); + res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void singleNodeWithMultiplePropertiesMergeTest() { + execute( "MERGE (charlie {name: 'Charlie Sheen', age: 10})" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of(), + Pair.of( "name", "Charlie Sheen" ), + Pair.of( "age", 10 ) ) ); + + execute( "MERGE (charlie {name: 'Charlie Sheen', age: 10})" ); + res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void singleNodeWithPropertiesAndLabelMergeTest() { + execute( "MERGE (michael:Person {name: 'Michael Douglas' , age : 10})" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( List.of( "Person" ), + Pair.of( "name", "Michael Douglas" ), + Pair.of( "age", 10 ) ) ); + + execute( "MERGE (michael:Person {name: 'Michael Douglas' , age : 10})" ); + res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void singleNodeDerivedFromExistingNodeMergeTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_4 ); + execute( SINGLE_NODE_PERSON_COMPLEX_5 ); + execute( SINGLE_NODE_PERSON_COMPLEX_6 ); + execute( """ + MATCH (person:Person) + MERGE (location:Location {name: person.bornIn}) + """ ); + + GraphResult res = execute( "MATCH (location:Location) RETURN location.name" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( "New York" ) ), + Row.of( TestLiteral.from( "Ohio" ) ), + Row.of( TestLiteral.from( "New Jersey" ) ) ); + + execute( """ + MATCH (person:Person) + MERGE (location:Location {name: person.bornIn}) + """ ); + + res = execute( "MATCH (location:Location) RETURN location" ); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void createWithMergeTest() { + execute( "MERGE (keanu:Person {name: 'Keanu Reeves', bornIn: 'Beirut', chauffeurName: 'Eric Brown'})\n" + + "ON CREATE SET keanu.age = 20" + ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, + (TestNode.from( List.of( "Person" ), Pair.of( "name", "Keanu Reeves" ), Pair.of( "age", 20 ) )) ); + execute( "MERGE (keanu:Person {name: 'Keanu Reeves', bornIn: 'Beirut', chauffeurName: 'Eric Brown'})\n" + + "ON CREATE SET keanu.age = 20" + ); + res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void matchWithMergeTest() { + execute( """ + MERGE (person:Person{ found : false}) + ON MATCH SET person.found = true + """ ); + + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( + List.of( "Person" ), + Pair.of( "found", false ) ) ); + + execute( """ + MERGE (person:Person{ found : false}) + ON MATCH SET person.found = true + """ ); + + res = matchAndReturnAllNodes(); + containsNodes( res, true, TestNode.from( + List.of( "Person" ), + Pair.of( "found", true ) ) ); + + assertEquals( 1, res.getData().length ); + } + + + @Test + public void createOrMatchNodeWithMergeTest() { + // Create new node + execute( "MERGE (n:Person {name: 'Alice'}) ON CREATE SET n.age = 30 ON MATCH SET n.age = 35" ); + GraphResult res = matchAndReturnAllNodes(); + assertNode( res, 0 ); + containsNodes( res, true, TestNode.from( + List.of( "Person" ), + Pair.of( "age", 30 ), + Pair.of( "name", "Alice" ) ) ); + // Updated the Matched node + execute( "MERGE (n:Person {name: 'Alice'}) ON CREATE SET n.age = 30 ON MATCH SET n.age = 35 " ); + res = matchAndReturnAllNodes(); + + containsNodes( res, true, TestNode.from( + List.of( "Person" ), + Pair.of( "age", 35 ), + Pair.of( "name", "Alice" ) ) ); + + assertEquals( 1, res.getData().length ); + } + + + @Test + public void singleRelationshipMergeTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_4 ); + execute( SINGLE_NODE_MOVIE ); + + execute( """ + MATCH + (charlie:Person {name: 'Charlie Sheen'}), + (wallStreet:Movie {title: 'Wall Street'}) + MERGE (charlie)-[r:ACTED_IN]->(wallStreet) + """ ); + + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), Pair.of( "name", "Charlie Sheen" ) ), + TestNode.from( List.of( "Person" ), Pair.of( "name", "Wall Street" ) ) ); + + res = execute( "MATCH ()-[r]->() RETURN r" ); + containsEdges( res, true, TestEdge.from( List.of( "ACTED_IN" ) ) ); + execute( """ + MATCH + (charlie:Person {name: 'Charlie Sheen'}), + (wallStreet:Movie {title: 'Wall Street'}) + MERGE (charlie)-[r:ACTED_IN]->(wallStreet) + """ ); + + res = execute( "MATCH ()-[r]->() RETURN r" ); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void multipleRelationshipsMergeTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_4 ); + execute( SINGLE_NODE_PERSON_COMPLEX_5 ); + + execute( """ + MATCH + (charlie:Person {name: 'Charlie Sheen'}), + (martin:Person {name: 'Martin Sheen'}) + MERGE (oliver)-[:DIRECTED]->(movie:Movie)<-[:DIRECTED]-(reiner) + """ ); + + GraphResult res = execute( """ + MATCH (p1:Person)-[:DIRECTED]->(movie:Movie)<-[:DIRECTED]-(p2:Person) + RETURN p1, p2, movie + """ ); + + containsNodes( res, true, + TestNode.from( Pair.of( "name", "Charlie Sheen" ) ), + TestNode.from( Pair.of( "name", "Martin Sheen" ) ), + TestNode.from( List.of( "Movie" ) ) ); + + execute( """ + MATCH + (charlie:Person {name: 'Charlie Sheen'}), + (martin:Person {name: 'Martin Sheen'}) + MERGE (oliver)-[:DIRECTED]->(movie:Movie)<-[:DIRECTED]-(reiner) + """ ); + + res = matchAndReturnAllNodes(); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void undirectedRelationshipMergeTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_4 ); + execute( SINGLE_NODE_PERSON_COMPLEX_5 ); + + execute( """ + MATCH + (charlie:Person {name: 'Charlie Sheen'}), + (martin:Person {name: 'Martin Sheen'}) + MERGE (charlie)-[r:KNOWS]-(oliver) + """ ); + + GraphResult res = execute( "MATCH (p1:Person)-[r:KNOWS]-(p2:Person)\n" + + "RETURN KNOWS" ); + + containsEdges( res, true, TestEdge.from( List.of( "KNOWS" ) ) ); + + execute( """ + MATCH + (charlie:Person {name: 'Charlie Sheen'}), + (martin:Person {name: 'Martin Sheen'}) + MERGE (charlie)-[r:KNOWS]-(oliver) + """ ); + + res = execute( "MATCH (p1:Person)-[r:KNOWS]-(p2:Person)\n" + + "RETURN KNOWS" ); + + assertEquals( 1, res.getData().length ); + } + + + @Test + public void relationshipOnTwoExistingNodeMergeTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_4 ); + execute( SINGLE_NODE_PERSON_COMPLEX_5 ); + execute( SINGLE_NODE_PERSON_COMPLEX_6 ); + + execute( """ + MATCH (person:Person) + MERGE (location:Location {name: person.bornIn}) + MERGE (person)-[r:BORN_IN]->(location) + """ ); + + GraphResult res = execute( "MATCH (location:Location) RETURN location.name" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( "New York" ) ), + Row.of( TestLiteral.from( "Ohio" ) ), + Row.of( TestLiteral.from( "New Jersey" ) ) ); + + res = execute( "MATCH ()-[BORN_IN]->() RETURN BORN_IN" ); + assertEquals( 3, res.getData().length ); + containsEdges( res, true, TestEdge.from( List.of( "BORN_IN" ) ) ); + + execute( """ + MATCH (person:Person) + MERGE (location:Location {name: person.bornIn}) + MERGE (person)-[r:BORN_IN]->(location) + """ ); + + GraphResult edges = execute( "MATCH ()-[BORN_IN]->() RETURN BORN_IN" ); + GraphResult nodes = execute( "MATCH (location:Location) RETURN Location " ); + + assertTrue( edges.getData().length == 3 && nodes.getData().length == 3 ); + } + + + @Test + public void relationshipOnExistingNodeAndMergeNodeDerivedFromAnodeProperty() { + execute( SINGLE_NODE_PERSON_COMPLEX_4 ); + execute( SINGLE_NODE_PERSON_COMPLEX_5 ); + execute( SINGLE_NODE_PERSON_COMPLEX_6 ); + + execute( """ + MATCH (person:Person) + MERGE (person)-[r:HAS_CHAUFFEUR]->(chauffeur:Chauffeur {name: person.chauffeurName}) + """ ); + + GraphResult res = execute( "MATCH ( chauffeur :Chauffeur) Return Chauffeur.name" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( "John Brown" ) ), + Row.of( TestLiteral.from( "Bob Brown" ) ), + Row.of( TestLiteral.from( "John Brown" ) ) ); + + res = execute( "MATCH ()-[HAS_CHAUFFEUR]->() RETURN HAS_CHAUFFEUR" ); + assertEquals( 3, res.getData().length ); + containsEdges( res, true, TestEdge.from( List.of( "HAS_CHAUFFEUR" ) ) ); + execute( """ + MATCH (person:Person) + MERGE (person)-[r:HAS_CHAUFFEUR]->(chauffeur:Chauffeur {name: person.chauffeurName}) + """ ); + GraphResult edges = execute( "MATCH ()-[HAS_CHAUFFEUR]->() RETURN HAS_CHAUFFEUR" ); + GraphResult nodes = execute( "MATCH (n:Chauffeur) RETURN Chauffeur" ); + assertTrue( edges.getData().length == 3 && nodes.getData().length == 3 ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/clause/write/RemoveTest.java b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/RemoveTest.java new file mode 100644 index 0000000000..257c9c64d8 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/clause/write/RemoveTest.java @@ -0,0 +1,136 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.clause.write; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.cypher.helper.TestNode; +import org.polypheny.db.webui.models.results.GraphResult; + + +@Slf4j +public class RemoveTest extends CypherTestTemplate { + + protected static final String SINGLE_NODE_PERSON_EMPLOYEE = "CREATE (n:Person:Employee) "; + + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void labelRemoveTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( "MATCH (n:Person {name: 'Max'})\n" + + "REMOVE n:Person " ); + GraphResult res = execute( "MATCH (n :Person) RETURN n" ); + assertEquals( 0, res.getData().length ); + } + + + @Test + public void returnWithRemoveTest() { + execute( SINGLE_NODE_PERSON_1 ); + GraphResult res = execute( "MATCH (n:Person {name: 'Max'})\n" + + "REMOVE n:Person RETURN n " ); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void multipleLabelsRemoveTest() { + execute( SINGLE_NODE_PERSON_EMPLOYEE ); + + GraphResult res = matchAndReturnAllNodes(); + containsNodes( res, true, TestNode.from( List.of( "Person", "Employee" ) ) ); + + execute( "MATCH (n) REMOVE n:Person:Employee " ); + res = execute( "MATCH (n :Person:Employee) RETURN n" ); + assertEquals( 0, res.getData().length ); + } + + + @Test + public void singlePropertyNodeRemoveTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( "MATCH (n : Person {name: 'Ann'}) REMOVE a.age " ); + GraphResult res = execute( "MATCH (n : Person) RETURN n.age , n.name" ); + containsRows( res, true, true, Row.of( TestLiteral.from( null ), TestLiteral.from( "Ann" ) ) ); + } + + + @Test + public void multiplePropertiesRemoveTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( """ + MATCH (n:Person {name: 'Ann'}) + REMOVE n.age, n.depno + """ ); + GraphResult res = execute( "MATCH (n : Person) RETURN n.age , n.depno , n.name " ); + containsRows( res, true, true, Row.of( TestLiteral.from( null ), TestLiteral.from( null ), TestLiteral.from( "Ann" ) ) ); + } + + + @Test + public void allPropertiesNodeRemoveTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( "MATCH (n:Person) SET n = {}" ); + GraphResult res = matchAndReturnAllNodes(); + assertEquals( 0, res.getData().length ); + } + + + @Test + public void singlePropertyRelationshipRemoveTest() { + execute( SINGLE_EDGE_2 ); + execute( "MATCH(p1:Person)-[rel:KNOWS]->(p2:Person)\n" + + "REMOVE rel.since" ); + GraphResult res = execute( "MATCH ()-[r:KNOWS]->() RETURN r.since" ); + containsRows( res, true, true, Row.of( TestLiteral.from( null ) ) ); + } + + + @Test + public void multiplePropertiesRelationshipRemoveTest() { + execute( "Create (p:Person {name: 'Max'})-[rel:KNOWS {since: 1994 , relation : 'friend'}]->(a:Person {name:'Hans', age:31})" ); + execute( "MATCH(p1:Person)-[rel:KNOWS]->(p2:Person)\n" + + "REMOVE rel.since , rel.relation" ); + GraphResult res = execute( "MATCH ()-[r:KNOWS]->() RETURN r.since , r.relation" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( null ) ), + Row.of( TestLiteral.from( null ) ) ); + } + + + @Test + public void allPropertiesRelationshipRemoveTest() { + execute( SINGLE_EDGE_2 ); + execute( "MATCH ()-[r:KNOWS]->() RETURN SET r = {}" ); + GraphResult res = execute( "MATCH ()-[r:KNOWS]->() RETURN r.since" ); + containsRows( res, true, true, Row.of( TestLiteral.from( null ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/functions/AggregateTest.java b/dbms/src/test/java/org/polypheny/db/cypher/functions/AggregateTest.java new file mode 100644 index 0000000000..659b76e56a --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/functions/AggregateTest.java @@ -0,0 +1,395 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.functions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class AggregateTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @AfterEach + public void tearGraphDown() { + tearDown(); + } + + + @Test + public void singleCountAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_EDGE_1 ); + execute( SINGLE_EDGE_2 ); + + GraphResult res = execute( "MATCH (n:Person) RETURN count(*)" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( 5 ) ) ); + + execute( SINGLE_EDGE_2 ); + res = execute( "MATCH (n:Person) RETURN count(*)" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( 7 ) ) ); + } + + + @Test + public void countFieldAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_EDGE_1 ); + execute( SINGLE_EDGE_2 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name, count(*)" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( 3 ) ), + Row.of( TestLiteral.from( "Kira" ), TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( 2 ) ) ); + } + + + @Test + public void countNullAggregateTest() { + GraphResult res = execute( "MATCH (n) RETURN count(*)" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 0 ) ) ); + } + + + @Test + public void countRenameFieldAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_EDGE_1 ); + execute( SINGLE_EDGE_2 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name, count(*) AS c" ); + assertEquals( "c", res.getHeader()[1].name ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( 3 ) ), + Row.of( TestLiteral.from( "Kira" ), TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( 2 ) ) ); + } + + + @Test + public void doubleCountRenameAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_EDGE_1 ); + execute( SINGLE_EDGE_2 ); + + GraphResult res = execute( "MATCH (n) RETURN n.name, n.age, count(*) AS c" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( null ), TestLiteral.from( 3 ) ), + Row.of( TestLiteral.from( "Kira" ), TestLiteral.from( 3 ), TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( null ), TestLiteral.from( 1 ) ), + Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( 31 ), TestLiteral.from( 1 ) ) ); + } + + + @Test + public void countPropertyAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( "MATCH (n) RETURN count(n.age)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 0 ) ) ); + } + + + @Test + public void countDistinctFunctionTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( "MATCH (n) RETURN COUNT(DISTINCT n.name)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 1 ) ) ); + } + + + @Test + public void singleAvgAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_EDGE_1 ); + execute( SINGLE_EDGE_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + + GraphResult res = execute( "MATCH (n) RETURN AVG(n.age)" ); + // Printing the data using Arrays.deepToString + //String[][] data = res.getData(); + //System.out.println( Arrays.deepToString( data ) ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 26.33333333333333 ) ) ); + } + + + @Test + public void avgNullAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN AVG(p.age)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( null ) ) ); + } + + + @Test + public void avgRenameAggregationTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN AVG(p.age) AS ages" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 38 ) ) ); + } + + + @Test + public void avgRenameFieldAggregationTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN p.depno As depNumber , AVG(p.age) As avgAge" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 13 ), TestLiteral.from( 38 ) ), + Row.of( TestLiteral.from( 14 ), TestLiteral.from( 32 ) ) ); + } + + + @Test + public void singleCollectAggregationTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN COLLECT(p.age) " ); + containsRows( res, true, false, Row.of( TestLiteral.from( List.of( 45, 31 ) ) ) ); + } + + + @Test + public void collectRenameAggregationTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN COLLECT(p.age) AS ages" ); + containsRows( res, true, false, Row.of( TestLiteral.from( List.of( 45, 31 ) ) ) ); + } + + + @Test + public void collectRenameFieldAggregationTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN COLLECT(p.age) AS ages , p.depno AS depNumber" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( List.of( 45, 31 ) ), TestLiteral.from( 13 ) ), + Row.of( TestLiteral.from( List.of( 32 ) ), TestLiteral.from( 14 ) ) ); + } + + + @Test + public void collectNullAggregationTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( "MATCH (p:Person) RETURN COLLECT(p.age)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( List.of() ) ) ); + } + + + @Test + public void singleMinMaxAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_EDGE_1 ); + execute( SINGLE_EDGE_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + + GraphResult res = execute( "MATCH (n) RETURN min(n.age)" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 3 ) ) ); + + res = execute( "MATCH (n) RETURN max(n.age)" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 45 ) ) ); + } + + + @Test + void minMaxNullAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (n) RETURN min(n.age) as ageMin" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( null ) ) ); + + res = execute( "MATCH (n) RETURN max(n.age) as ageMax" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( null ) ) ); + } + + + @Test + public void minMaxRenameAggregateTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (n) RETURN min(n.age) as ageMin" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 31 ) ) ); + + res = execute( "MATCH (n) RETURN max(n.age) as ageMax" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 45 ) ) ); + } + + + @Test + public void minMaxRenameFieldAggregateTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (n) RETURN n.depno as depNumber , min(n.age) as ageMin" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 13 ), TestLiteral.from( 31 ) ), + Row.of( TestLiteral.from( 14 ), TestLiteral.from( 32 ) ) ); + + res = execute( "MATCH (n) RETURN n.depno as depNumber , max(n.age) as ageMax" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 13 ), TestLiteral.from( 45 ) ), + Row.of( TestLiteral.from( 14 ), TestLiteral.from( 32 ) ) ); + } + + + @Test + public void singleSumAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_EDGE_1 ); + execute( SINGLE_EDGE_2 ); + + GraphResult res = execute( "MATCH (n) RETURN sum(n.age)" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 34 ) ) ); + } + + + @Test + public void sumNullAggregationTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (n) RETURN sum(n.age)" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 0 ) ) ); + } + + + @Test + public void sumRenameAggregationTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN sum(p.age) As totalAge " ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 76 ) ) ); + } + + + @Test + public void sumRenameFieldAggregationTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN sum(p.age) AS totalAge, p.depno AS depNumber," ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 13 ), TestLiteral.from( 76 ) ), + Row.of( TestLiteral.from( 14 ), TestLiteral.from( 32 ) ) ); + } + + + @Test + public void singleStdevAggregateTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN stdev(p.age) " ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 9.8994949 ) ) ); + } + + + @Test + public void stdevNullAggregateTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN stdev(p.age) " ); + containsRows( res, true, false, + Row.of( TestLiteral.from( null ) ) ); + } + + + @Test + public void stdevRenameAggregateTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN stdev(p.age) AS Stdev " ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 9.8994949 ) ) ); + } + + + @Test + public void stdevRenameFieldAggregateTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( "MATCH (p:Person) RETURN stdev(p.age) AS Stdev , n.depno As department" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( 9.8994949 ), TestLiteral.from( 13 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/functions/ListFunTest.java b/dbms/src/test/java/org/polypheny/db/cypher/functions/ListFunTest.java new file mode 100644 index 0000000000..93a94620c3 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/functions/ListFunTest.java @@ -0,0 +1,120 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.functions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class ListFunTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleSizeFunTest() { + GraphResult res = execute( "RETURN size([1, 2, 3])" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 3 ) ) ); + } + + + @Test + public void nullSizeFunTest() { + GraphResult res = execute( "RETURN size(null)" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( null ) ) ); + } + + + @Test + public void patternExpressionSizeFunTest() { + execute( SINGLE_EDGE_1 ); + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( """ + MATCH (a) + WHERE a.name = 'Max' + RETURN size((a)-[]->())) AS fof""" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 2 ) ) ); + } + + + @Test + public void stringSizeFunTest() { + execute( SINGLE_NODE_PERSON_1 ); + GraphResult res = execute( "MATCH (a) RETURN size(a.name)" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 3 ) ) ); + } + + + @Test + public void simpleRangeFunTest() { + GraphResult res = execute( "RETURN RANGE(1, 3)" ); + containsRows( res, true, true, + Row.of( TestLiteral.from( 1 ), TestLiteral.from( 2 ), TestLiteral.from( 3 ) ) ); + } + + + @Test + public void returnLabelsFunTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH (a)" + + "RETURN labels(a)" ); + containsRows( res, true, false, Row.of( TestLiteral.from( List.of( "Person" ) ) ) ); + } + + + @Test + public void returnNodesFunTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH p = (a)-->(b)-->(c)\n" + + "RETURN nodes(p)" ); + assertEquals( 1, res.getData().length ); + containsRows( res, true, false, Row.of( TestLiteral.from( List.of( MAX, KIRA ) ) ) ); + } + + + @Test + public void returnRelationsFunTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH p = (a)-->(b)-->(c)\n" + + "RETURN relationships(p)" ); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void returnRelationAndNodesFunTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( "MATCH p = (a)-->(b)-->(c)\n" + + "RETURN relationships(p) , nodes(p)" ); + assertEquals( 1, res.getData().length ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/functions/NumericFunTest.java b/dbms/src/test/java/org/polypheny/db/cypher/functions/NumericFunTest.java new file mode 100644 index 0000000000..c7c133420e --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/functions/NumericFunTest.java @@ -0,0 +1,126 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.functions; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class NumericFunTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void absFunTest() { + GraphResult res = execute( "RETURN ABS(-5) " ); + containsRows( res, true, true, Row.of( TestLiteral.from( 5 ) ) ); + + res = execute( "RETURN ABS(5)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 5 ) ) ); + + res = execute( "RETURN ABS(0)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 0 ) ) ); + } + + + @Test + public void roundFunTest() { + GraphResult res = execute( "RETURN ROUND(3)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 3 ) ) ); + + res = execute( "RETURN ROUND(-3)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( -3 ) ) ); + + res = execute( "RETURN ROUND(3.4)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 3 ) ) ); + + res = execute( "RETURN ROUND(3.5)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 4 ) ) ); + + res = execute( "RETURN ROUND(-3.5)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( -4 ) ) ); + } + + + @Test + public void floorFunTest() { + GraphResult res = execute( "RETURN FLOOR(3)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 3 ) ) ); + + res = execute( "RETURN FLOOR(-3)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( -3 ) ) ); + + res = execute( "RETURN FLOOR(3.16)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 3 ) ) ); + + res = execute( "RETURN FLOOR(3.9)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 3 ) ) ); + + res = execute( "RETURN FLOOR(-3.16)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( -4 ) ) ); + } + + + @Test + public void ceilFunTest() { + GraphResult res = execute( "RETURN CEIL(3)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 4 ) ) ); + + res = execute( "RETURN CEIL(-3)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 4 ) ) ); + + res = execute( "RETURN CEIL(3.16)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 4 ) ) ); + + res = execute( "RETURN CEIL(3.5)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 4 ) ) ); + } + + + @Test + public void sqrtFunTest() { + GraphResult res = execute( "RETURN SQRT(9)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 3 ) ) ); + + res = execute( "RETURN SQRT(0)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 0 ) ) ); + } + + + @Test + public void nonPerfectSquareSqrtFunTest() { + GraphResult res = execute( "RETURN SQRT(8)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( Math.sqrt( 8 ) ) ) ); + } + + + // Todo missing + @Test + public void sqrtFunTestNegative() { + execute( "RETURN SQRT(-9)" ); + // containsRows(res, true, true, Row.of(TestLiteral.from(null))); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/functions/OtherFunTest.java b/dbms/src/test/java/org/polypheny/db/cypher/functions/OtherFunTest.java new file mode 100644 index 0000000000..218176a141 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/functions/OtherFunTest.java @@ -0,0 +1,113 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.functions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class OtherFunTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + ////////////////////////// + /// Scalar Functions + /////////////////////////// + @Test + public void typeFunTest() { + execute( SINGLE_EDGE_1 ); + GraphResult res = execute( """ + MATCH (a)-[r]->(b) + RETURN TYPE(r) + """ ); + containsRows( res, true, true, Row.of( TestLiteral.from( "OWNER_OF" ) ) ); + } + + + @Test + public void idFunTest() { + execute( SINGLE_NODE_PERSON_1 ); + GraphResult res = execute( """ + MATCH (p:Person { name: 'Max' }) + RETURN ID(p) + """ ); + assertEquals( 1, res.getData().length ); + } + + + @Test + public void testCoalesceFunTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + GraphResult result = execute( "MATCH (p) RETURN p.name, coalesce(p.age, 0) AS age" ); + containsRows( result, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( 0 ) ), + Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( 0 ) ), + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ) ) ); + } + + + @Test + public void defaultValuesWithCoalesceFunTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + + GraphResult result = execute( "MATCH (p) RETURN p.name, coalesce(p.age, 'unknown') AS age" ); + assertNode( result, 0 ); + containsRows( result, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( "unknown" ) ), + Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( "unknown" ) ), + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( "unknown" ) ) ); + } + + + /////////////////////////////// + // Predicate Functions + ///////////////////////////// + + + @Test + public void existFunTest() { + execute( SINGLE_NODE_PERSON_1 ); + + GraphResult res = execute( """ + MATCH (p:Person { name: 'Max' }) + RETURN EXISTS(p.age) + """ ); + containsRows( res, true, true, Row.of( TestLiteral.from( false ) ) ); + + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( """ + MATCH (p:Person { name: 'Ann' }) + RETURN EXISTS(p.age) + """ ); + containsRows( res, true, true, Row.of( TestLiteral.from( true ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/functions/SpatialFunTest.java b/dbms/src/test/java/org/polypheny/db/cypher/functions/SpatialFunTest.java new file mode 100644 index 0000000000..42abee4e79 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/functions/SpatialFunTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.functions; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class SpatialFunTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void cartesian2DPointFunTest() { + GraphResult res = execute( """ + WITH point({x: 3, y: 4}) AS p + RETURN + p.x AS x, + p.y AS y, + p.crs AS crs, + p.srid AS srid""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( 3.0 ), TestLiteral.from( 4.0 ), + TestLiteral.from( "cartesian" ), TestLiteral.from( 7203 ) ) ); + } + + + @Test + public void WGS_843DPointFunTest() { + GraphResult res = execute( """ + WITH point({latitude: 3, longitude: 4, height: 4321}) AS p + RETURN + p.latitude AS latitude, + p.longitude AS longitude, + p.height AS height, + p.x AS x, + p.y AS y, + p.z AS z, + p.crs AS crs, + p.srid AS srid""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( 3.0 ), + TestLiteral.from( 4.0 ), + TestLiteral.from( 4321.0 ), + TestLiteral.from( 4.0 ), + TestLiteral.from( 3.0 ), + TestLiteral.from( 4321.0 ), + TestLiteral.from( "wgs-84-3d" ), + TestLiteral.from( 4979 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/functions/StringFunTest.java b/dbms/src/test/java/org/polypheny/db/cypher/functions/StringFunTest.java new file mode 100644 index 0000000000..3d559e4599 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/functions/StringFunTest.java @@ -0,0 +1,156 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.functions; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class StringFunTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void upperFunTest() { + GraphResult res = execute( "RETURN UPPER('hello')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "HELLO" ) ) ); + res = execute( "RETURN UPPER('hElLo')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "HELLO" ) ) ); + } + + + @Test + public void emptyUpperFunTest() { + GraphResult res = execute( "RETURN UPPER('')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "" ) ) ); + } + + + @Test + public void nullUpperFunTest() { + GraphResult res = execute( "RETURN UPPER(null)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( null ) ) ); + } + + + @Test + public void lowerFunTest() { + GraphResult res = execute( "RETURN LOWER('WORLD')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "world" ) ) ); + + res = execute( "RETURN LOWER('WOrLd')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "world" ) ) ); + } + + + @Test + public void emptyLowerFunTest() { + GraphResult res = execute( "RETURN LOWER('')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "" ) ) ); + } + + + @Test + public void nullLowerFunTest() { + GraphResult res = execute( "RETURN LOWER(null)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( null ) ) ); + } + + + @Test + public void normalSubstringFunTest() { + GraphResult res = execute( "RETURN SUBSTRING('Hello, world!', 0, 5)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "Hello" ) ) ); + + res = execute( "RETURN SUBSTRING('Hello, world!', 7, 5)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "world" ) ) ); + + res = execute( "RETURN SUBSTRING('Hello, world!', 7, 0)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "" ) ) ); + } + + + @Test + public void exceedLengthSubstringFunTest() { + GraphResult res = execute( "RETURN SUBSTRING('Hello', 0, 10)" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "Hello" ) ) ); + } + + + @Test + public void trimFunTest() { + GraphResult res = execute( "RETURN TRIM(' hello ')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "hello" ) ) ); + + res = execute( "RETURN TRIM('hello')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "hello" ) ) ); + + res = execute( "RETURN TRIM(' ')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "" ) ) ); + } + + + @Test + public void emptyTrimFunTest() { + GraphResult res = execute( "RETURN TRIM('')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "" ) ) ); + } + + + @Test + public void normalReplaceFunTest() { + GraphResult res = execute( "RETURN REPLACE('Hello, world!', 'world', 'Cypher') " ); + containsRows( res, true, true, Row.of( TestLiteral.from( "Hello, Cypher!" ) ) ); + } + + + @Test + public void caseSensitiveReplaceFunTest() { + GraphResult res = execute( "RETURN REPLACE('Hello, world!', 'WORLD', 'Cypher')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "Hello, world!" ) ) ); + } + + + @Test + public void removeSpacesReplaceFunTest() { + GraphResult res = execute( "RETURN REPLACE('Hello, world!', ' ', '')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "Hello,world!" ) ) ); + } + + + @Test + public void removeSubstringReplaceFunTest() { + GraphResult res = execute( "RETURN REPLACE('Hello, world!', 'world', '')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "Hello, !" ) ) ); + } + + + @Test + public void stringLengthFunTest() { + GraphResult res = execute( "RETURN LENGTH('Hello, world!')" ); + containsRows( res, true, true, Row.of( TestLiteral.from( 13 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/functions/TemporalFunTest.java b/dbms/src/test/java/org/polypheny/db/cypher/functions/TemporalFunTest.java new file mode 100644 index 0000000000..d763bd9672 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/functions/TemporalFunTest.java @@ -0,0 +1,212 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.functions; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class TemporalFunTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void stringIntoDateFunTest() { + GraphResult res = execute( "RETURN date('2015-07-21')\n" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "2015-07-21" ) ) ); + + res = execute( "RETURN date('2015-07')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "2015-07-01" ) ) ); + + res = execute( "RETURN date('201507')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "2015-07-01" ) ) ); + + res = execute( "RETURN date('2015-W30-2')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "2015-07-21" ) ) ); + + res = execute( "RETURN date('2015')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "2015-01-01" ) ) ); + } + + + @Test + public void yearMonthDayDateFunTest() { + GraphResult res = execute( "RETURN date({year: 1984, month: 10, day: 11})" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "1984-10-11" ) ) ); + + res = execute( "RETURN date({year: 1984, month: 10})" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "1984-10-01" ) ) ); + + res = execute( "RETURN date({year: 1984})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "1984-01-01" ) ) ); + } + + + @Test + public void yearWeekDayDateFunTest() { + GraphResult res = execute( "RETURN date({year: 1984, week: 10, dayOfWeek: 3})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "1984-03-07" ) ) ); + } + + + @Test + public void yearQuarterDayDateFunTest() { + GraphResult res = execute( "RETURN date({year: 1984, quarter: 3, dayOfQuarter: 45})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "1984-08-14" ) ) ); + } + + + @Test + public void yearMonthDayZonedTimeDateFunTest() { + GraphResult res = execute( "RETURN datetime({year: 1984, month: 10, day: 11, hour: 12, minute: 31, second: 14, millisecond: 123, microsecond: 456, nanosecond: 789}) AS theDate" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "1984-10-11T12:31:14.123456789Z" ) ) ); + + res = execute( "datetime({year: 1984, month: 10, day: 11, hour: 12, minute: 31, second: 14, millisecond: 645, timezone: '+01:00'})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "1984-10-11T12:31:14.645+01:00" ) ) ); + + res = execute( "RETURN datetime({year: 1984, month: 10, day: 11, hour: 12, minute: 31, second: 14})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "1984-10-11T12:31:14Z" ) ) ); + } + + + @Test + public void yearWeekDayTimeDateFunTest() { + GraphResult res = execute( "RETURN datetime({year: 1984, week: 10, dayOfWeek: 3, hour: 12, minute: 31, second: 14, millisecond: 645})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "1984-03-07T12:31:14.645Z" ) ) ); + } + + + @Test + public void yearQuarterDayTimeDateFunTest() { + GraphResult res = execute( "RETURN datetime({year: 1984, quarter: 3, dayOfQuarter: 45, hour: 12, minute: 31, second: 14, microsecond: 645876})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "1984-08-14T12:31:14.645876Z" ) ) ); + } + + + @Test + public void stringIntoTimeDateFunTest() { + GraphResult res = execute( "RETURN datetime('2015-07-21T21:40:32.142+0100')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "2015-07-21T21:40:32.142+01:00" ) ) ); + + res = execute( "RETURN datetime('2015-W30-2T214032.142Z')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "2015-07-21T21:40:32.142Z" ) ) ); + + res = execute( "RETURN datetime('2015T214032-0100')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "2015-01-01T21:40:32-01:00" ) ) ); + + res = execute( "RETURN datetime('20150721T21:40-01:30')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "2015-07-21T21:40-01:30" ) ) ); + + res = execute( "RETURN datetime('2015-07-21T21:40:32.142[Europe/London]')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "datetime('2015-07-21T21:40:32.142[Europe/London]')" ) ) ); + } + + + @Test + public void timeFunTest() { + GraphResult res = execute( "RETURN time({hour: 12, minute: 31, second: 14, millisecond: 123, microsecond: 456, nanosecond: 789})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "12:31:14.123456789Z" ) ) ); + + res = execute( "RETURN time({hour: 12, minute: 31, second: 14, nanosecond: 645876123})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "12:31:14.645876123Z" ) ) ); + + res = execute( "RETURN time({hour: 12, minute: 31, second: 14, microsecond: 645876, timezone: '+01:00'})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "12:31:14.645876000+01:00" ) ) ); + + res = execute( "time({hour: 12, minute: 31, timezone: '+01:00'})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "12:31:00+01:00" ) ) ); + + res = execute( "RETURN time({hour: 12, timezone: '+01:00'})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "12:00:00+01:00" ) ) ); + } + + + @Test + public void stringIntoTimeFunTest() { + GraphResult res = execute( "RETURN time('21:40:32.142+0100')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "21:40:32.142000000+01:00" ) ) ); + + res = execute( "RETURN time('214032.142Z')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "21:40:32.142000000Z" ) ) ); + + res = execute( "RETURN time('21:40:32+01:00')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "21:40:32+01:00" ) ) ); + + res = execute( "RETURN time('214032-0100')" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "21:40:32-01:00" ) ) ); + + } + + + @Test + public void durationFunTest() { + GraphResult res = execute( "RETURN duration({days: 14, hours:16, minutes: 12})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "P14DT16H12M" ) ) ); + + res = execute( "RETURN duration({months: 5, days: 1.5})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "P5M1DT12H" ) ) ); + + res = execute( "RETURN duration({months: 0.75})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "P22DT19H51M49.5S" ) ) ); + + res = execute( "RETURN duration({weeks: 2.5})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "P17DT12H" ) ) ); + + res = execute( "RETURN duration({minutes: 1.5, seconds: 1, milliseconds: 123, microseconds: 456, nanoseconds: 789})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "PT1M31.123456789S" ) ) ); + + res = execute( "RETURN duration({minutes: 1.5, seconds: 1, nanoseconds: 123456789})" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "PT1M31.123456789S" ) ) ); + } + + + @Test + public void stringIntoDurationFunTest() { + GraphResult res = execute( "RETURN duration(\"P14DT16H12M\") " ); + containsRows( res, true, false, Row.of( TestLiteral.from( "P14DT16H12M" ) ) ); + + res = execute( "RETURN duration(\"P5M1.5D\")" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "P5M1DT12H" ) ) ); + + res = execute( "RETURN duration(\"P0.75M\") " ); + containsRows( res, true, false, Row.of( TestLiteral.from( "P22DT19H51M49.5S" ) ) ); + + res = execute( "RETURN duration(\"PT0.75M\")" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "PT45S" ) ) ); + + res = execute( "RETURN duration(\"P2012-02-02T14:37:21.545\")" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "P2012Y2M2DT14H37M21.545S" ) ) ); + } + + + @Test + public void durationBetweenFunTest() { + GraphResult res = execute( "RETURN duration.between(date('1984-10-11'), date('2015-06-24')) AS theDuration" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "P30Y8M13D" ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestEdge.java b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestEdge.java index 34e40f55b9..f9492714dd 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestEdge.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestEdge.java @@ -23,6 +23,7 @@ import org.polypheny.db.type.entity.PolyValue; import org.polypheny.db.util.Pair; + public class TestEdge extends TestGraphObject { @Nullable diff --git a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestGraphObject.java b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestGraphObject.java index 9dae0e0508..6c8d956fce 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestGraphObject.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestGraphObject.java @@ -33,6 +33,7 @@ import org.polypheny.db.type.entity.graph.GraphPropertyHolder; import org.polypheny.db.util.Pair; + public class TestGraphObject implements TestObject { public static double EPSILON = 0.2; @@ -98,15 +99,12 @@ public boolean matches( GraphPropertyHolder other, boolean exclusive, boolean ig if ( entry.getValue().isList() ) { int i = 0; PolyList list = entry.getValue().asList(); - for ( PolyValue o : list ) { matches &= o.equals( ((List) properties.get( entry.getKey() )).get( i ) ); i++; } } else if ( entry.getValue().isNumber() || other.properties.get( entry.getKey() ).isNumber() ) { - matches &= - toBigDecimal( other.properties.get( entry.getKey() ).toString() ).doubleValue() - - toBigDecimal( entry.getValue().toString() ).doubleValue() < EPSILON; + matches &= toBigDecimal( other.properties.get( entry.getKey() ).toString() ).doubleValue() - toBigDecimal( entry.getValue().toString() ).doubleValue() < EPSILON; } else { matches &= other.properties.get( entry.getKey() ).equals( entry.getValue() ); } diff --git a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestLiteral.java b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestLiteral.java index fdfe8a9dce..d18040e3fa 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestLiteral.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestLiteral.java @@ -19,6 +19,7 @@ import javax.annotation.Nullable; import org.polypheny.db.type.entity.PolyValue; + public class TestLiteral implements TestObject { public final String value; diff --git a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestNode.java b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestNode.java index 1da891528f..092dc878f8 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestNode.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestNode.java @@ -24,6 +24,7 @@ import org.polypheny.db.type.entity.PolyValue; import org.polypheny.db.util.Pair; + public class TestNode extends TestGraphObject { public TestNode( @Nullable String id, @Nullable Map properties, @Nullable List labels ) { @@ -42,5 +43,4 @@ public static TestNode from( @NotNull List labels, Pair. return new TestNode( null, getProps( properties ), getLabels( labels ) ); } - } diff --git a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestObject.java b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestObject.java index 5273056301..4e07da1123 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestObject.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestObject.java @@ -18,6 +18,7 @@ import org.polypheny.db.type.entity.PolyValue; + public interface TestObject { boolean matches( PolyValue other, boolean exclusive ); diff --git a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestPath.java b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestPath.java index 6f4a5ffcf8..85830ecc00 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/helper/TestPath.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/helper/TestPath.java @@ -16,7 +16,6 @@ package org.polypheny.db.cypher.helper; - import java.util.List; import lombok.SneakyThrows; import org.polypheny.db.cypher.CypherTestTemplate; @@ -25,6 +24,7 @@ import org.polypheny.db.type.entity.graph.GraphPropertyHolder; import org.polypheny.db.type.entity.graph.PolyPath; + public class TestPath implements TestObject { private final List objects; diff --git a/dbms/src/test/java/org/polypheny/db/cypher/operators/BooleanOperatorsTest.java b/dbms/src/test/java/org/polypheny/db/cypher/operators/BooleanOperatorsTest.java new file mode 100644 index 0000000000..95aa2d70db --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/operators/BooleanOperatorsTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.operators; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class BooleanOperatorsTest extends CypherTestTemplate { + + @BeforeEach + public void setUp() { + tearDown(); + createGraph(); + } + + + @Test + public void conjunctionOperatorTest() { + GraphResult res = execute( "WITH true as a , false as b RETURN a AND b " ); + containsRows( res, true, false, Row.of( TestLiteral.from( false ) ) ); + + res = execute( "WITH true as a , true as b RETURN a AND b " ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + } + + + @Test + public void disjunctionOperatorTest() { + GraphResult res = execute( "WITH true as a , false as b RETURN a OR b " ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + + res = execute( "WITH false as a , false as b RETURN a OR b " ); + containsRows( res, true, false, Row.of( TestLiteral.from( false ) ) ); + } + + + @Test + public void exclusiveDisjunctionOperatorTest() { + GraphResult res = execute( "WITH true as a , false as b RETURN a XOR b " ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + + res = execute( "WITH true as a , true as b RETURN a XOR b " ); + containsRows( res, true, false, Row.of( TestLiteral.from( false ) ) ); + + res = execute( "WITH false as a , false as b RETURN a XOR b " ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + } + + + @Test + public void negationOperatorTest() { + GraphResult res = execute( "WITH true as a RETURN NOT a " ); + containsRows( res, true, false, Row.of( TestLiteral.from( false ) ) ); + + res = execute( "WITH false as a RETURN NOT a " ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/operators/ComparisonOperationsTest.java b/dbms/src/test/java/org/polypheny/db/cypher/operators/ComparisonOperationsTest.java new file mode 100644 index 0000000000..d68c070391 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/operators/ComparisonOperationsTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.operators; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class ComparisonOperationsTest extends CypherTestTemplate { + + @BeforeEach + public void setUp() { + tearDown(); + createGraph(); + } + + + @Test + public void IsNullOperatorTest() { + GraphResult res = execute( "Return null is not null as Result" ); + containsRows( res, true, false, Row.of( TestLiteral.from( false ) ) ); + } + + + @Test + public void IsNotNullFunction() { + GraphResult res = execute( "Return null is null as Result" ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + } + + + @Test + public void greaterThanOperatorTest() { + GraphResult res = execute( "Return 1 > 2 as result " ); + containsRows( res, true, false, Row.of( TestLiteral.from( false ) ) ); + } + + + @Test + public void smallerThanOperatorTest() { + GraphResult res = execute( "Return 1 < 2 as result " ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + } + + + @Test + public void greaterThanOrEqualOperatorTest() { + GraphResult res = execute( "Return 1 >= 2 as result " ); + containsRows( res, true, false, Row.of( TestLiteral.from( false ) ) ); + } + + + @Test + public void smallerThanOrEqualOperatorTest() { + GraphResult res = execute( "Return 1 <= 2 as result " ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + } + + + @Test + public void equalityOperatorTest() { + GraphResult res = execute( "Return 2 = 2 as result " ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + } + + + @Test + public void inequalityOperatorTest() { + GraphResult res = execute( "Return 1 <> 2 as result " ); + containsRows( res, true, false, Row.of( TestLiteral.from( false ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/operators/ListOperatorsTest.java b/dbms/src/test/java/org/polypheny/db/cypher/operators/ListOperatorsTest.java new file mode 100644 index 0000000000..baa8c08495 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/operators/ListOperatorsTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.operators; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class ListOperatorsTest extends CypherTestTemplate { + + @BeforeEach + public void setUp() { + tearDown(); + createGraph(); + } + + + @Test + public void checkIfNumberInListOperatorTest() { + GraphResult res = execute( "RETURN 1 IN [ 1 ,2 ]" ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + + res = execute( "RETURN 3 IN [ 1 ,2 ]" ); + containsRows( res, true, false, Row.of( TestLiteral.from( false ) ) ); + } + + + @Test + public void checkIfListInListOperatorTest() { + GraphResult res = execute( "RETURN [2, 1] IN [1, [2, 1], 3] " ); + containsRows( res, true, false, Row.of( TestLiteral.from( true ) ) ); + + res = execute( "RETURN [1, 2] IN [1, 2] " ); + containsRows( res, true, false, Row.of( TestLiteral.from( false ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/operators/MathematicalOperatorsTest.java b/dbms/src/test/java/org/polypheny/db/cypher/operators/MathematicalOperatorsTest.java new file mode 100644 index 0000000000..0f735a8f3a --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/operators/MathematicalOperatorsTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.operators; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class MathematicalOperatorsTest extends CypherTestTemplate { + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void additionOperator() { + GraphResult res = execute( "RETURN 2 + 3" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 5 ) ) ); + } + + + @Test + public void minisOperatorTest() { + GraphResult res = execute( "RETURN 3 - 2" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 1 ) ) ); + } + + + @Test + public void multiplicationOperatorTest() { + GraphResult res = execute( "RETURN 2 * 3" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 6 ) ) ); + } + + + @Test + public void divisionOperatorTest() { + GraphResult res = execute( "RETURN 6 / 3 " ); + containsRows( res, true, false, Row.of( TestLiteral.from( 2 ) ) ); + } + + + @Test + public void moduleOperatorTest() { + GraphResult res = execute( "RETURN 3 % 2 " ); + containsRows( res, true, false, Row.of( TestLiteral.from( 1 ) ) ); + } + + + @Test + public void exponentiationOperator() { + GraphResult res = execute( "RETURN 2 ^ 3" ); + containsRows( res, true, false, Row.of( TestLiteral.from( 8.0 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/FilterTest.java b/dbms/src/test/java/org/polypheny/db/cypher/operators/StringOperatorsTest.java similarity index 55% rename from dbms/src/test/java/org/polypheny/db/cypher/FilterTest.java rename to dbms/src/test/java/org/polypheny/db/cypher/operators/StringOperatorsTest.java index 1b5a90b207..f76bf6ca9b 100644 --- a/dbms/src/test/java/org/polypheny/db/cypher/FilterTest.java +++ b/dbms/src/test/java/org/polypheny/db/cypher/operators/StringOperatorsTest.java @@ -14,38 +14,29 @@ * limitations under the License. */ -package org.polypheny.db.cypher; +package org.polypheny.db.cypher.operators; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; import org.polypheny.db.webui.models.results.GraphResult; -public class FilterTest extends CypherTestTemplate { + +public class StringOperatorsTest extends CypherTestTemplate { + @BeforeEach - public void reset() { + public void setUp() { tearDown(); createGraph(); } - /////////////////////////////////////////////// - ///////// FILTER - /////////////////////////////////////////////// - @Test - public void simplePropertyFilter() { - execute( SINGLE_NODE_PERSON_1 ); - execute( SINGLE_NODE_ANIMAL ); - GraphResult res = execute( "MATCH (p) WHERE p.age > 3 RETURN p" ); - assertNode( res, 0 ); - - assert containsRows( res, true, false ); - - res = execute( "MATCH (p) WHERE p.age >= 3 RETURN p" ); - assertNode( res, 0 ); - - assert containsRows( res, true, false, Row.of( KIRA ) ); + public void concatenateWithPlusOperatorTest() { + GraphResult res = execute( "RETURN 'neo' + '4j' AS result" ); + containsRows( res, true, true, Row.of( TestLiteral.from( "neo4j" ) ) ); } } diff --git a/dbms/src/test/java/org/polypheny/db/cypher/subqueries/CallSubqueriesTest.java b/dbms/src/test/java/org/polypheny/db/cypher/subqueries/CallSubqueriesTest.java new file mode 100644 index 0000000000..9a0131fb60 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/subqueries/CallSubqueriesTest.java @@ -0,0 +1,194 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.subqueries; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.cypher.helper.TestNode; +import org.polypheny.db.util.Pair; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class CallSubqueriesTest extends CypherTestTemplate { + + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleCallTest() { + GraphResult res = execute( " CALL { RETURN 'hello' AS innerReturn} RETURN innerReturn" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "hello" ) ) ); + } + + + @Test + public void repeatCallTest() { + GraphResult res = execute( """ + UNWIND [0, 1, 2] AS x + CALL { RETURN 'hello' AS innerReturn } + RETURN innerReturn""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "hello" ) ), + Row.of( TestLiteral.from( "hello" ) ), + Row.of( TestLiteral.from( "hello" ) ) ); + } + + + @Test + public void unwindVariablesAsInputsIntoCallTest() { + GraphResult res = execute( """ + UNWIND [0, 1, 2] AS x + CALL { WITH x RETURN x * 10 AS y } + RETURN x, y""" ); + + containsRows( res, true, true, + Row.of( TestLiteral.from( 0 ), TestLiteral.from( 0 ) ), + Row.of( TestLiteral.from( 1 ), TestLiteral.from( 10 ) ), + Row.of( TestLiteral.from( 2 ), TestLiteral.from( 20 ) ) ); + } + + + @Test + public void returnMatchNodesCallTest() { + execute( SINGLE_NODE_PERSON_1 ); + GraphResult res = execute( "CALL { MATCH (p:Person) RETURN p} RETURN p " ); + containsRows( res, true, true, Row.of( MAX ) ); + } + + + @Test + public void countNodesCallTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( """ + CALL { + MATCH (p) + RETURN count(p) AS totalPeople} + RETURN totalPeople + """ ); + + containsRows( res, true, false, Row.of( TestLiteral.from( 2 ) ) ); + } + + + @Test + public void countRelationshipsCallTest() { + execute( SINGLE_EDGE_1 ); + execute( SINGLE_EDGE_2 ); + + GraphResult res = execute( """ + CALL { + MATCH ()-[r]->() + RETURN count(r) AS totalRelationships } + RETURN totalRelationships + """ ); + + containsRows( res, true, false, Row.of( TestLiteral.from( 2 ) ) ); + } + + + @Test + public void useMatchedNodesAsInputsIntoCallTest() { + execute( SINGLE_EDGE_2 ); + + GraphResult res = execute( """ + MATCH (p:Person) + CALL { + WITH p + MATCH (p)-[:KNOWS]-(c:Person) + RETURN c.name AS friend + } + RETURN p.name, friend""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( "Hans" ) ) ); + } + + + @Test + public void filterMatchedNodesByOutputOfCallTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + execute( SINGLE_NODE_PERSON_COMPLEX_3 ); + + GraphResult res = execute( """ + CALL { + MATCH (p:Person { name: 'Bob' }) + RETURN p.age AS age} + MATCH (p:Person) + WHERE p.age > age + RETURN p + """ ); + + assertEquals( 2, res.getData().length ); + containsNodes( res, true, + TestNode.from( List.of( "Person" ), Pair.of( "name", "Ann" ) ), + TestNode.from( List.of( "Person" ), Pair.of( "name", "Alex" ) ) ); + } + + + @Test + public void unionCallTest() { + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_1 ); + execute( SINGLE_NODE_PERSON_COMPLEX_2 ); + + GraphResult res = execute( """ + CALL { MATCH (p:Person) + RETURN p + UNION + MATCH (p:Person) + RETURN p + } + RETURN p.name, p.age""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Ann" ), TestLiteral.from( 45 ) ), + Row.of( TestLiteral.from( "Bob" ), TestLiteral.from( 31 ) ) ); + } + + + @Test + public void unitSubQueryCallTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( """ + MATCH (p:Person) CALL { + WITH p + CREATE (:Person {name: p.name}) + } RETURN count(*)""" ); + + // The number of rows present after the subquery is the same as was going into the subquery + containsRows( res, true, false, Row.of( TestLiteral.from( 2 ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/subqueries/CollectSubQueriesTest.java b/dbms/src/test/java/org/polypheny/db/cypher/subqueries/CollectSubQueriesTest.java new file mode 100644 index 0000000000..640739c5c0 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/subqueries/CollectSubQueriesTest.java @@ -0,0 +1,159 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.subqueries; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class CollectSubQueriesTest extends CypherTestTemplate { + + public static final String EDGE_3 = "CREATE (p:Person {name :'Max'}),(p)-[rel:OWNER_OF{ since : 2002}] -> (c:Cat : Animal {name :'Mittens' , age : 3}), (p)-[rel2:OWNER_OF { since : 1999}] -> (d:Dog :Animal { name : 'Andy' , age :10})"; + + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleCollectSubQueryTest() { + execute( SINGLE_EDGE_1 ); + + GraphResult res = execute( """ + MATCH (person:Person) + WHERE 'Kira' IN COLLECT { MATCH (person)-[:OWNER_OF]->(a:Animal) RETURN a.name } + RETURN person.name AS name""" ); + + containsRows( res, true, true, Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void useCollectSubQueryInReturnTest() { + execute( SINGLE_NODE_PERSON_1 ); + execute( EDGE_3 ); + + GraphResult res = execute( """ + MATCH (person:Person) + RETURN person.name, + COLLECT { + MATCH (person)-[:OWNER_OF]->(d:Dog) + RETURN d.name + } as DogNames""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( "Andy" ) ) ); + } + + + @Test + public void whereWithCollectSubQueryTest() { + execute( EDGE_3 ); + + GraphResult res = execute( """ + MATCH (person:Person) + RETURN person.name as name, COLLECT { + MATCH (person)-[r:OWNER_OF]->(a:Dog) + WHERE a.age <= 3 + RETURN a.name + } as youngDog """ ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( "Andy" ) ) ); + } + + + @Test + public void unionWithCollectSubQueryTest() { + execute( EDGE_3 ); + + GraphResult res = execute( """ + MATCH (person:Person) + RETURN + person.name AS name, + COLLECT { + MATCH (person)-[:HAS_DOG]->(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:HAS_CAT]->(cat:Cat) + RETURN cat.name AS petName + } AS petNames""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( List.of( "Andy", "Mittens" ) ) ) ); + } + + + @Test + public void withClauseWithCollectSubQueryTest() { + execute( EDGE_3 ); + + GraphResult res = execute( """ + MATCH (person:Person) + RETURN person.name AS name, COLLECT { + WITH 1999 AS yearOfTheDog + MATCH (person)-[r:OWNER_OF]->(d:Dog) + WHERE r.since = yearOfTheDog + RETURN d.name + } as dogsOfTheYear""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( "Andy" ) ) ); + } + + + @Test + public void caseWithCollectSubQueryTest() { + execute( EDGE_3 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( """ + MATCH (person:Person) + RETURN + CASE + WHEN COLLECT { MATCH (person)-[:OWNER_OF]->(d:Dog) RETURN d.name } = [] THEN " No Dogs " + person.name + ELSE person.name + END AS result""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "No Dogs" ) ), + Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void updateWithCollectSubQueryTest() { + execute( SINGLE_NODE_PERSON_2 ); + execute( EDGE_3 ); + + GraphResult res = execute( """ + MATCH (person:Person) WHERE person.name = "Hans" + SET person.dogNames = COLLECT { MATCH (person)-[:OWNER_OF]->(d:Dog) RETURN d.name } + RETURN person.dogNames as dogNames""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( List.of( "Andy" ) ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/subqueries/CountSubQueriesTest.java b/dbms/src/test/java/org/polypheny/db/cypher/subqueries/CountSubQueriesTest.java new file mode 100644 index 0000000000..8902b55f8d --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/subqueries/CountSubQueriesTest.java @@ -0,0 +1,152 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.subqueries; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class CountSubQueriesTest extends CypherTestTemplate { + + public static final String EDGE_3 = "CREATE (p:Person {name :'Max'}),(p)-[rel:OWNER_OF{ since : 2002}] -> (c:Cat : Animal {name :'Mittens' , age : 3}), (p)-[rel2:OWNER_OF { since : 1999}] -> (d:Dog :Animal { name : 'Andy' , age :10})"; + + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleCountSubQueryTest() { + execute( SINGLE_NODE_PERSON_2 ); + execute( EDGE_3 ); + + GraphResult res = execute( """ + MATCH (person:Person) + WHERE COUNT { (person)-[r:OWNER_OF]->(:Animal) } > 1 + RETURN person.name AS name""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void useCountSubQueryInReturnTest() { + execute( EDGE_3 ); + + GraphResult res = execute( "MATCH (person:Person) RETURN person.name, COUNT { (person)-[:OWNER_OF]->(:Dog) } as howManyDogs" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( 1 ) ) ); + } + + + @Test + public void whereWithCountSubQueryTest() { + execute( SINGLE_NODE_PERSON_2 ); + execute( EDGE_3 ); + + GraphResult res = execute( """ + MATCH (person:Person) + WHERE COUNT { + (person)-[r:OWNER_OF]->(dog:Dog) + WHERE person.name = dog.name } = 1 + RETURN person.name AS name""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void unionWithCountSubQueryTest() { + execute( EDGE_3 ); + + GraphResult res = execute( """ + MATCH (person:Person) + RETURN + person.name AS name, + COUNT { + MATCH (person)-[:OWNER_OF]->(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:OWNER_OF]->(cat:Cat) + RETURN cat.name AS petName + } AS numPets""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( 2 ) ) ); + } + + + @Test + public void withClauseWithCountSubQueryTest() { + execute( EDGE_3 ); + + GraphResult res = execute( """ + MATCH (person:Person) + WHERE COUNT { + WITH "Andy" AS dogName + MATCH (person)-[:OWNER_OF]->(d:Dog) + WHERE d.name = dogName + } = 1 + RETURN person.name AS name""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void updateWithCountSubQueryTest() { + execute( EDGE_3 ); + + GraphResult res = execute( """ + MATCH (person:Person) WHERE person.name ="Max" + SET person.howManyDogs = COUNT { (person)-[:OWNER_OF]->(:Dog) } + RETURN person.howManyDogs as howManyDogs""" ); + + containsRows( res, true, false, Row.of( + TestLiteral.from( 1 ) ) ); + } + + + @Test + public void caseWithCountSubQueryTest() { + execute( EDGE_3 ); + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( """ + MATCH (person:Person) + RETURN + CASE + WHEN COUNT { (person)-[:OWNER_OF]->(:Dog) } >= 1 THEN "DogLover " + person.name + ELSE person.name + END AS result""" ); + + containsRows( res, true, false, + Row.of( TestLiteral.from( "DogLover" ) ), + Row.of( TestLiteral.from( "Hans" ) ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/cypher/subqueries/ExistsSubQueriesTest.java b/dbms/src/test/java/org/polypheny/db/cypher/subqueries/ExistsSubQueriesTest.java new file mode 100644 index 0000000000..d4db4e050a --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/cypher/subqueries/ExistsSubQueriesTest.java @@ -0,0 +1,140 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.cypher.subqueries; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.polypheny.db.cypher.CypherTestTemplate; +import org.polypheny.db.cypher.helper.TestLiteral; +import org.polypheny.db.webui.models.results.GraphResult; + + +public class ExistsSubQueriesTest extends CypherTestTemplate { + + public static final String EDGE_3 = "CREATE (p:Person {name :'Max'}),(p)-[rel:OWNER_OF{ since : 2002}] -> (c:Cat : Animal {name :'Mittens' , age : 3}), (p)-[rel2:OWNER_OF { since : 1999}] -> (d:Dog :Animal { name : 'Andy' , age :10}),(d)-[:HAS_TOY]->(:Toy{name:'Banana'})"; + + + @BeforeEach + public void reset() { + tearDown(); + createGraph(); + } + + + @Test + public void simpleExistsSubQueryTest() { + execute( SINGLE_NODE_PERSON_2 ); + + GraphResult res = execute( """ + MATCH (person:Person) + WHERE EXISTS { + (person)-[:OWNER_OF]->(:Dog) + } + RETURN person.name AS name""" ); + containsRows( res, true, false, Row.of( TestLiteral.from( null ) ) ); + + execute( EDGE_3 ); + execute( """ + MATCH (person:Person) + WHERE EXISTS { + (person)-[:OWNER_OF]->(:Dog) + } + RETURN person.name AS name""" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void whereWithExistsSubQueryTest() { + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( """ + MATCH (person:Person) + WHERE EXISTS { + MATCH (person)-[:OWNER_OF]->(dog:Dog) + WHERE person.name = "Max"\s + } + RETURN dog.name AS name""" ); + containsRows( res, true, false, Row.of( TestLiteral.from( "Andy" ) ) ); + } + + + @Test + public void nestedExistsSubQueryTest() { + execute( EDGE_3 ); + GraphResult res = execute( """ + MATCH (person:Person) + WHERE EXISTS { + MATCH (person)-[:OWNER_OF]->(dog:Dog) + WHERE EXISTS { + MATCH (dog)-[:HAS_TOY]->(toy:Toy) + WHERE toy.name = 'Banana' + } + } + RETURN person.name AS name""" ); + + containsRows( res, true, false, Row.of( TestLiteral.from( "Max" ) ) ); + } + + + @Test + public void returnExistsSubQueryTest() { + execute( EDGE_3 ); + GraphResult res = execute( """ + MATCH (person:Person) + RETURN person.name AS name, EXISTS { + MATCH (person)-[:OWNER_OF]->(:Dog) + } AS hasDog""" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( true ) ) ); + } + + + @Test + public void unionWithExistsSubQueryTest() { + execute( EDGE_3 ); + execute( SINGLE_NODE_PERSON_2 ); + GraphResult res = execute( """ + MATCH (person:Person) + RETURN + person.name AS name, + EXISTS { + MATCH (person)-[:HAS_DOG]->(:Dog) + UNION + MATCH (person)-[:HAS_CAT]->(:Cat) + } AS hasPet""" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ), TestLiteral.from( true ) ), + Row.of( TestLiteral.from( "Hans" ), TestLiteral.from( false ) ) ); + } + + + @Test + public void withClauseWithExistsSubQueryTest() { + execute( EDGE_3 ); + GraphResult res = execute( """ + MATCH (person:Person {name: name}) + WHERE EXISTS { + WITH "Andy" AS name + MATCH (person)-[:OWNER_OF]->(d:Dog) + WHERE d.name = name + } + RETURN person.name AS name""" ); + containsRows( res, true, false, + Row.of( TestLiteral.from( "Max" ) ) ); + } + +} diff --git a/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/cypher2alg/CypherToAlgConverter.java b/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/cypher2alg/CypherToAlgConverter.java index 9998a9f7b3..59fe2a38d3 100644 --- a/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/cypher2alg/CypherToAlgConverter.java +++ b/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/cypher2alg/CypherToAlgConverter.java @@ -159,15 +159,13 @@ private void convertQuery( CypherNode node, CypherContext context ) { switch ( node.getCypherKind() ) { case SINGLE: CypherSingleQuery query = (CypherSingleQuery) node; - for ( CypherClause clause : query.getClauses() ) { convertClauses( clause, context ); } - break; case PERIODIC_COMMIT: case UNION: - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( "Unsupported cypher operation: " + node.getCypherKind() ); } } @@ -201,9 +199,8 @@ private void convertClauses( CypherClause clause, CypherContext context ) { convertRemove( (CypherRemove) clause, context ); break; default: - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( "Unsupported cypher clause: " + clause.getCypherKind() ); } - } diff --git a/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherExpression.java b/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherExpression.java index 63223cc2b8..bbe52323f2 100644 --- a/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherExpression.java +++ b/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherExpression.java @@ -83,7 +83,7 @@ public Pair getRex( CypherContext context, RexType type ) { case ANY -> OperatorName.CYPHER_ANY_MATCH; case NONE -> OperatorName.CYPHER_NONE_MATCH; case SINGLE -> OperatorName.CYPHER_SINGLE_MATCH; - default -> throw new UnsupportedOperationException(); + default -> throw new UnsupportedOperationException( "Unsupported cypher expression: " + this.type ); }; // ANY ( Variable IN Expression(list) Where? ) diff --git a/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherFunctionInvocation.java b/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherFunctionInvocation.java index 0b2d227292..124deceb7e 100644 --- a/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherFunctionInvocation.java +++ b/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherFunctionInvocation.java @@ -43,7 +43,7 @@ public CypherFunctionInvocation( ParserPos beforePos, ParserPos namePos, List getRex( CypherContext context, RexType type ) { operatorName = OperatorName.AND; break; case XOR: - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( "Unsupported cypher operator: " + gate ); case NOT: return handleSingular( context, type, OperatorName.NOT ); } diff --git a/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherVariable.java b/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherVariable.java index 3dbb9ec047..7aba2e9154 100644 --- a/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherVariable.java +++ b/plugins/cypher-language/src/main/java/org/polypheny/db/cypher/expression/CypherVariable.java @@ -81,7 +81,7 @@ public Pair getRex( CypherContext context, RexType type ) { } } - throw new GenericRuntimeException( "The used variable is not known." ); + throw new GenericRuntimeException( "Unknown variable: " + name ); }