From 5233e2a052becd74fe70166da18a19bcd48abcc9 Mon Sep 17 00:00:00 2001 From: Giuseppe Villani Date: Fri, 26 Aug 2022 12:40:02 +0200 Subject: [PATCH] Fixes #2395: The apoc.cypher.runSchemaFile doesn't support full text indexes (#3084) --- .../main/java/apoc/cypher/CypherExtended.java | 7 +- .../java/apoc/cypher/CypherExtendedTest.java | 66 ++++++++++++++----- .../src/test/resources/constraints.cypher | 3 +- extended/src/test/resources/schema.cypher | 5 +- full/src/test/resources/constraints.cypher | 2 + full/src/test/resources/schema.cypher | 4 ++ 6 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 full/src/test/resources/constraints.cypher create mode 100644 full/src/test/resources/schema.cypher diff --git a/extended/src/main/java/apoc/cypher/CypherExtended.java b/extended/src/main/java/apoc/cypher/CypherExtended.java index cf6572d9fc..aca0866e0f 100644 --- a/extended/src/main/java/apoc/cypher/CypherExtended.java +++ b/extended/src/main/java/apoc/cypher/CypherExtended.java @@ -10,6 +10,7 @@ import apoc.util.collection.Iterators; import org.apache.commons.lang3.StringUtils; import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.QueryExecutionType; import org.neo4j.graphdb.QueryStatistics; import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Transaction; @@ -222,8 +223,10 @@ private String removeShellControlCommands(String stmt) { return stmt; } - private boolean isSchemaOperation(String stmt) { - return stmt.matches("(?is).*(create|drop)\\s+(index|constraint).*"); + private boolean isSchemaOperation(String statement) { + return db.executeTransactionally("EXPLAIN " + statement, Collections.emptyMap(), + res -> QueryExecutionType.QueryType.SCHEMA_WRITE.equals(res.getQueryExecutionType().queryType()) + ); } private boolean isPeriodicOperation(String stmt) { return stmt.matches("(?is).*using\\s+periodic.*") || stmt.matches("(?is).*in\\s+transactions.*"); diff --git a/extended/src/test/java/apoc/cypher/CypherExtendedTest.java b/extended/src/test/java/apoc/cypher/CypherExtendedTest.java index 1bbcce047d..a9c78edc08 100644 --- a/extended/src/test/java/apoc/cypher/CypherExtendedTest.java +++ b/extended/src/test/java/apoc/cypher/CypherExtendedTest.java @@ -11,6 +11,8 @@ import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.schema.ConstraintDefinition; import org.neo4j.graphdb.schema.IndexDefinition; +import org.neo4j.graphdb.schema.IndexType; +import org.neo4j.graphdb.schema.Schema; import org.neo4j.test.rule.DbmsRule; import org.neo4j.test.rule.ImpermanentDbmsRule; @@ -22,15 +24,19 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.concurrent.TimeUnit; +import java.util.stream.StreamSupport; import static apoc.ApocConfig.APOC_IMPORT_FILE_ENABLED; import static apoc.ApocConfig.apocConfig; import static apoc.util.TestUtil.testCall; import static apoc.util.TestUtil.testCallCount; +import static apoc.util.TestUtil.testCallEmpty; import static apoc.util.TestUtil.testResult; import static apoc.util.Util.map; import static org.hamcrest.Matchers.hasEntry; import static org.junit.Assert.*; +import static org.neo4j.driver.internal.util.Iterables.count; /** * @author mh @@ -61,8 +67,8 @@ public static void tearDown() { public void clearDB() { db.executeTransactionally("MATCH (n) DETACH DELETE n"); try (Transaction tx = db.beginTx()) { - tx.schema().getIndexes().forEach(IndexDefinition::drop); tx.schema().getConstraints().forEach(ConstraintDefinition::drop); + tx.schema().getIndexes().forEach(IndexDefinition::drop); tx.commit(); } } @@ -282,32 +288,60 @@ public void testRunFilesMultiple() throws Exception { } @Test - @Ignore - public void testSchemaRunFile() throws Exception { + public void testSchemaRunFile() { + final int expectedBefore; + try (Transaction tx = db.beginTx()) { + expectedBefore = count(tx.schema().getIndexes()); + } + testResult(db, "CALL apoc.cypher.runSchemaFile('schema.cypher')", r -> { Map row = r.next(); Map result = (Map) row.get("result"); assertEquals(1L, toLong(result.get("indexesAdded"))); - }); - } - - @Test - @Ignore - public void testSchemaRunFiles() throws Exception { - testResult(db, "CALL apoc.cypher.runSchemaFiles(['constraints.cypher', 'drop_constraints.cypher', 'index.cypher'])", - r -> { - Map row = r.next(); - Map result = (Map) row.get("result"); - assertEquals(1L, toLong(result.get("constraintsAdded"))); row = r.next(); result = (Map) row.get("result"); - assertEquals(1L, toLong(result.get("constraintsRemoved"))); + assertEquals(1L, toLong(result.get("indexesAdded"))); row = r.next(); result = (Map) row.get("result"); assertEquals(1L, toLong(result.get("indexesAdded"))); - + row = r.next(); + result = (Map) row.get("result"); + assertEquals(1L, toLong(result.get("indexesAdded"))); + assertFalse(r.hasNext()); }); + + try (Transaction tx = db.beginTx()) { + assertEquals(expectedBefore + 4, count(tx.schema().getIndexes())); + } + } + + @Test + public void testSchemaRunFiles() { + schemaAssertions(Collections.emptyList(), Collections.emptyList()); + + testCallEmpty(db, "CALL apoc.cypher.runSchemaFiles($files, {statistics: false})", + map("files", List.of("constraints.cypher", "drop_constraints.cypher", "schema.cypher"))); + + final List another_cons = List.of("CustomerIndex1", "CustomerIndex21", "CustomerIndex231", "another_cons", "node_index_name"); + final List another_cons1 = List.of("another_cons"); + + schemaAssertions(another_cons, another_cons1); + } + + private void schemaAssertions(List expectedIdx, List expectedCons) { + try (Transaction tx = db.beginTx()) { + final Schema schema = tx.schema(); + schema.awaitIndexesOnline(20, TimeUnit.SECONDS); + final List actualIdx = StreamSupport.stream(schema.getIndexes().spliterator(), false) + .filter(idx -> !idx.getIndexType().equals(IndexType.LOOKUP)) + .map(IndexDefinition::getName).sorted().collect(Collectors.toList()); + final List actualCons = StreamSupport.stream(schema.getConstraints().spliterator(), false) + .map(ConstraintDefinition::getName).sorted() + .collect(Collectors.toList()); + assertEquals(expectedIdx, actualIdx); + assertEquals(expectedCons, actualCons); + } } @Test diff --git a/extended/src/test/resources/constraints.cypher b/extended/src/test/resources/constraints.cypher index eb0ad91cd3..d30d8dde00 100644 --- a/extended/src/test/resources/constraints.cypher +++ b/extended/src/test/resources/constraints.cypher @@ -1 +1,2 @@ -CREATE CONSTRAINT uniqueConstraint FOR (n:Person) REQUIRE n.name IS UNIQUE; \ No newline at end of file +CREATE CONSTRAINT uniqueConstraint FOR (n:Person) REQUIRE n.name IS UNIQUE; +CREATE CONSTRAINT another_cons FOR (n:AnotherLabel) REQUIRE n.name IS UNIQUE; \ No newline at end of file diff --git a/extended/src/test/resources/schema.cypher b/extended/src/test/resources/schema.cypher index 34527945c7..444d8518dd 100644 --- a/extended/src/test/resources/schema.cypher +++ b/extended/src/test/resources/schema.cypher @@ -1 +1,4 @@ -CREATE INDEX FOR (n:Node) ON (n.id); \ No newline at end of file +CREATE FULLTEXT INDEX CustomerIndex1 FOR (n:Customer1) ON EACH [n.name1]; +CREATE FULLTEXT INDEX CustomerIndex21 FOR (n:Customer21) ON EACH [n.name12]; +CREATE FULLTEXT INDEX CustomerIndex231 FOR (n:Customer213) ON EACH [n.name123]; +CREATE INDEX node_index_name FOR (n:Person) ON (n.surname); diff --git a/full/src/test/resources/constraints.cypher b/full/src/test/resources/constraints.cypher new file mode 100644 index 0000000000..11cf13e763 --- /dev/null +++ b/full/src/test/resources/constraints.cypher @@ -0,0 +1,2 @@ +CREATE CONSTRAINT ON (n:Person) ASSERT n.name IS UNIQUE; +CREATE CONSTRAINT another_cons ON (n:AnotherLabel) ASSERT n.name IS UNIQUE; \ No newline at end of file diff --git a/full/src/test/resources/schema.cypher b/full/src/test/resources/schema.cypher new file mode 100644 index 0000000000..444d8518dd --- /dev/null +++ b/full/src/test/resources/schema.cypher @@ -0,0 +1,4 @@ +CREATE FULLTEXT INDEX CustomerIndex1 FOR (n:Customer1) ON EACH [n.name1]; +CREATE FULLTEXT INDEX CustomerIndex21 FOR (n:Customer21) ON EACH [n.name12]; +CREATE FULLTEXT INDEX CustomerIndex231 FOR (n:Customer213) ON EACH [n.name123]; +CREATE INDEX node_index_name FOR (n:Person) ON (n.surname);