From 56f91e17619c2ab26812816925ac4bf50d1826f9 Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Sun, 24 Jun 2018 23:07:46 +0200 Subject: [PATCH] fix runFirstColumn for neo4j-graphql-js, fixes #819 fixes #776 --- docs/overview.adoc | 3 ++- .../java/apoc/cypher/CypherFunctions.java | 22 ++++++++++++++++--- src/test/java/apoc/cypher/CypherTest.java | 10 +++++++-- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/docs/overview.adoc b/docs/overview.adoc index 0cc58f9664..afa94e29fe 100644 --- a/docs/overview.adoc +++ b/docs/overview.adoc @@ -554,7 +554,8 @@ CALL apoc.generate.simple([2,2,2,2], null, null) [cols="1m,5"] |=== | CALL apoc.cypher.run(fragment, params) yield value | executes reading fragment with the given parameters -| apoc.cypher.runFirstColumn(statement, params, [expectMultiplevalues]) | function that executes statement with given parameters returning first column only, if expectMultipleValues is true will collect results into a list +| apoc.cypher.runFirstColumnSingle(statement, params) | function that executes statement with given parameters returning first column only, will return first/single row or null +| apoc.cypher.runFirstColumnMany(statement, params) | function that executes statement with given parameters returning first column only, will collect all rows into a list | CALL apoc.cypher.runFile(file or url,{config}) yield row, result | runs each statement in the file, all semicolon separated - currently no schema operations | CALL apoc.cypher.runFiles([files or urls],{config}) yield row, result | runs each statement in the files, all semicolon separated | CALL apoc.cypher.runSchemaFile(file or url,{config}) - allows only schema operations, runs each schema statement in the file, all semicolon separated diff --git a/src/main/java/apoc/cypher/CypherFunctions.java b/src/main/java/apoc/cypher/CypherFunctions.java index 88466fcd58..282398a6be 100644 --- a/src/main/java/apoc/cypher/CypherFunctions.java +++ b/src/main/java/apoc/cypher/CypherFunctions.java @@ -9,7 +9,9 @@ import org.neo4j.procedure.UserFunction; import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static apoc.cypher.Cypher.withParamMapping; @@ -21,16 +23,30 @@ public class CypherFunctions { public GraphDatabaseService db; @UserFunction - @Description("apoc.cypher.runFirstColumn(statement, params, expectMultipleValues) - executes statement with given parameters, returns first column only, if expectMultipleValues is true will collect results into an array") + @Deprecated + @Description("use either apoc.cypher.runFirstColumnMany for a list return or apoc.cypher.runFirstColumnSingle for returning the first row of the first column") public Object runFirstColumn(@Name("cypher") String statement, @Name("params") Map params, @Name(value = "expectMultipleValues",defaultValue = "true") boolean expectMultipleValues) { if (params == null) params = Collections.emptyMap(); - try (Result result = db.execute(withParamMapping(statement, params.keySet()), params)) { + String resolvedStatement = withParamMapping(statement, params.keySet()); + if (!resolvedStatement.contains(" runtime")) resolvedStatement = "cypher runtime=slotted " + resolvedStatement; + try (Result result = db.execute(resolvedStatement, params)) { String firstColumn = result.columns().get(0); try (ResourceIterator iter = result.columnAs(firstColumn)) { - if (expectMultipleValues) return iter.stream().toArray(); + if (expectMultipleValues) return iter.stream().collect(Collectors.toList()); return iter.hasNext() ? iter.next() : null; } } } + + @UserFunction + @Description("apoc.cypher.runFirstColumnMany(statement, params, expectMultipleValues) - executes statement with given parameters, returns first column only collected into a list, params are available as identifiers") + public List runFirstColumnMany(@Name("cypher") String statement, @Name("params") Map params) { + return (List)runFirstColumn(statement, params, true); + } + @UserFunction + @Description("apoc.cypher.runFirstColumnSingle(statement, params, expectMultipleValues) - executes statement with given parameters, returns first element of the first column only, params are available as identifiers") + public Object runFirstColumnSingle(@Name("cypher") String statement, @Name("params") Map params) { + return runFirstColumn(statement, params, false); + } } diff --git a/src/test/java/apoc/cypher/CypherTest.java b/src/test/java/apoc/cypher/CypherTest.java index 56e4fdd1b6..e724381d55 100644 --- a/src/test/java/apoc/cypher/CypherTest.java +++ b/src/test/java/apoc/cypher/CypherTest.java @@ -86,11 +86,17 @@ public void testRunVariable() throws Exception { } @Test - public void testRunFirstColumn() throws Exception { - testCall(db, "RETURN apoc.cypher.runFirstColumn('RETURN a + 7 AS b', {a: 3}, false) AS s", + public void testRunFirstColumnSingle() throws Exception { + testCall(db, "RETURN apoc.cypher.runFirstColumnSingle('RETURN a + 7 AS b', {a: 3}) AS s", r -> assertEquals(10L, (r.get("s")))); } + @Test + public void testRunFirstColumnMany() throws Exception { + testCall(db, "RETURN apoc.cypher.runFirstColumnMany('UNWIND range(1,a) as id RETURN id', {a: 3}) AS s", + r -> assertEquals(Arrays.asList(1L,2L,3L), (r.get("s")))); + } + @Test public void testRunFirstColumnBugCompiled() throws Exception { ResourceIterator it = db.execute("CREATE (m:Movie {title:'MovieA'})<-[:ACTED_IN]-(p:Person {name:'PersonA'})-[:ACTED_IN]->(m2:Movie {title:'MovieB'}) RETURN m").columnAs("m");