Skip to content

Commit

Permalink
Rewrite exists(property) to property IS NOT NULL. No AUTO cherry-pick
Browse files Browse the repository at this point in the history
As the former syntax is deprecated and scheduled for removal in Neo4j.
  • Loading branch information
Lojjs committed Mar 16, 2022
1 parent 3c12d45 commit cf02cca
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 72 deletions.
23 changes: 12 additions & 11 deletions core/src/main/java/apoc/refactor/rename/Rename.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package apoc.refactor.rename;

import apoc.Pools;
import apoc.periodic.Periodic;
import apoc.periodic.BatchAndTotalResult;
import apoc.periodic.Periodic;
import apoc.util.MapUtil;
import apoc.util.Util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
Expand All @@ -21,14 +30,6 @@
import org.neo4j.procedure.Procedure;
import org.neo4j.procedure.TerminationGuard;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* @author AgileLARUS
*
Expand Down Expand Up @@ -116,7 +117,7 @@ public Stream<BatchAndTotalResultWithInfo> nodeProperty(@Name("oldName") String
@Name(value="nodes", defaultValue = "[]") List<Node> nodes,
@Name(value = "config", defaultValue = "{}") Map<String, Object> config) {
nodes = nodes.stream().map(n -> Util.rebind(tx, n)).collect(Collectors.toList());
String cypherIterate = nodes != null && ! nodes.isEmpty() ? "UNWIND $nodes AS n WITH n WHERE exists (n."+oldName+") return n" : "match (n) where exists (n."+oldName+") return n";
String cypherIterate = nodes != null && ! nodes.isEmpty() ? "UNWIND $nodes AS n WITH n WHERE n."+oldName+" IS NOT NULL return n" : "match (n) where n."+oldName+" IS NOT NULL return n";
String cypherAction = "set n."+newName+"= n."+oldName+" remove n."+oldName;
final Map<String, Object> params = MapUtil.map("nodes", nodes);
Map<String, Object> parameters = getPeriodicConfig(config, params);
Expand All @@ -133,7 +134,7 @@ public Stream<BatchAndTotalResultWithInfo> typeProperty(@Name("oldName") String
@Name(value="rels", defaultValue = "[]") List<Relationship> rels,
@Name(value = "config", defaultValue = "{}") Map<String, Object> config) {
rels = rels.stream().map(r -> Util.rebind(tx, r)).collect(Collectors.toList());
String cypherIterate = rels != null && ! rels.isEmpty() ? "UNWIND $rels AS r WITH r WHERE exists (r."+oldName+") return r" : "match ()-[r]->() where exists (r."+oldName+") return r";
String cypherIterate = rels != null && ! rels.isEmpty() ? "UNWIND $rels AS r WITH r WHERE r."+oldName+" IS NOT NULL return r" : "match ()-[r]->() where r."+oldName+" IS NOT NULL return r";
String cypherAction = "set r."+newName+"= r."+oldName+" remove r."+oldName;
final Map<String, Object> params = MapUtil.map("rels", rels);
Map<String, Object> parameters = getPeriodicConfig(config, params);
Expand Down
11 changes: 7 additions & 4 deletions core/src/test/java/apoc/refactor/CloneSubgraphTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.neo4j.test.rule.DbmsRule;
import org.neo4j.test.rule.ImpermanentDbmsRule;

import java.util.Collections;
import java.util.List;

import org.neo4j.test.rule.DbmsRule;
import org.neo4j.test.rule.ImpermanentDbmsRule;

import static apoc.util.MapUtil.map;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class CloneSubgraphTest {
Expand Down Expand Up @@ -290,7 +293,7 @@ public void testCloneSubgraph_With_Standins_For_RootA_With_Skipped_Properties_Sh

TestUtil.testCall(db,
"MATCH ()-[r]->() " +
"WHERE not exists(r.id)" +
"WHERE r.id IS NULL " +
"RETURN count(r) as relsWithNoId",
(row) -> {
assertThat(row.get("relsWithNoId"), is(10L)); // 10 cloned rels with skipped id property
Expand Down
14 changes: 7 additions & 7 deletions core/src/test/java/apoc/refactor/rename/RenameTest.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package apoc.refactor.rename;


import apoc.util.TestUtil;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.test.rule.DbmsRule;
import org.neo4j.test.rule.ImpermanentDbmsRule;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.test.rule.DbmsRule;
import org.neo4j.test.rule.ImpermanentDbmsRule;

import static apoc.util.MapUtil.map;
import static apoc.util.TestUtil.testCall;
import static apoc.util.TestUtil.testResult;
Expand Down Expand Up @@ -145,12 +145,12 @@ public void testRenamePropertyForSomeRelationship() throws Exception {
}

private long resultRelationshipsMatches(String type, String prop){
String query = type != null ? "MATCH ()-[r:"+type+"]->() RETURN count(r) as countResult" : "match ()-[r]->() where exists (r."+prop+") return count(r) as countResult";
String query = type != null ? "MATCH ()-[r:"+type+"]->() RETURN count(r) as countResult" : "match ()-[r]->() where r."+prop+" IS NOT NULL return count(r) as countResult";
return TestUtil.singleResultFirstColumn(db, query);
}

private long resultNodesMatches(String label, String prop) {
String query = label != null ? "MATCH (b:"+label+") RETURN count(b) as countResult" : "match (n) where exists (n."+prop+") return count(n) as countResult";
String query = label != null ? "MATCH (b:"+label+") RETURN count(b) as countResult" : "match (n) where n."+prop+" IS NOT NULL return count(n) as countResult";
return TestUtil.singleResultFirstColumn(db, query);
}

Expand Down
29 changes: 16 additions & 13 deletions core/src/test/java/apoc/schema/SchemasTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.neo4j.configuration.GraphDatabaseSettings;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.QueryExecutionException;
Expand All @@ -18,17 +24,14 @@
import org.neo4j.test.rule.DbmsRule;
import org.neo4j.test.rule.ImpermanentDbmsRule;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import static apoc.util.TestUtil.*;
import static apoc.util.TestUtil.ignoreException;
import static apoc.util.TestUtil.registerProcedure;
import static apoc.util.TestUtil.testCall;
import static apoc.util.TestUtil.testResult;
import static java.util.Arrays.asList;
import static org.junit.Assert.*;
import static org.neo4j.configuration.SettingImpl.newBuilder;
import static org.neo4j.configuration.SettingValueParsers.BOOL;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

/**
* @author mh
Expand Down Expand Up @@ -551,7 +554,7 @@ public void testIndexesMoreLabels() {
@Test
public void testSchemaRelationshipsExclude() {
ignoreException(() -> {
db.executeTransactionally("CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE exists(like.day)");
db.executeTransactionally("CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL");
testResult(db, "CALL apoc.schema.relationships({excludeRelationships:['LIKED']})", (result) -> assertFalse(result.hasNext()));
}, QueryExecutionException.class);
}
Expand Down Expand Up @@ -587,8 +590,8 @@ public void testIndexesLabelsAndExcludeLabelsValuatedShouldFail() {

@Test(expected = QueryExecutionException.class)
public void testConstraintsRelationshipsAndExcludeRelationshipsValuatedShouldFail() {
db.executeTransactionally("CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE exists(like.day)");
db.executeTransactionally("CREATE CONSTRAINT FOR ()-[knows:SINCE]-() REQUIRE exists(since.year)");
db.executeTransactionally("CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL");
db.executeTransactionally("CREATE CONSTRAINT FOR ()-[knows:SINCE]-() REQUIRE since.year IS NOT NULL");
awaitIndexesOnline();
try (Transaction tx = db.beginTx()) {
tx.schema().awaitIndexesOnline(5, TimeUnit.SECONDS);
Expand Down
29 changes: 17 additions & 12 deletions core/src/test/java/apoc/spatial/SpatialTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,26 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;
import org.neo4j.test.rule.DbmsRule;
import org.neo4j.test.rule.ImpermanentDbmsRule;

import java.net.URL;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;
import org.neo4j.test.rule.DbmsRule;
import org.neo4j.test.rule.ImpermanentDbmsRule;

import static apoc.util.MapUtil.map;
import static apoc.util.TestUtil.*;
import static apoc.util.TestUtil.testCallCount;
import static apoc.util.TestUtil.testCallEmpty;
import static apoc.util.TestUtil.testResult;
import static java.util.Collections.emptyMap;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class SpatialTest {

Expand Down Expand Up @@ -103,7 +108,7 @@ private void addEventData(Map<String, Object> event) {
@Test
public void testSimpleGeocode() {
String query = "MATCH (a:Event) \n" +
"WHERE exists(a.address) AND exists(a.name) \n" +
"WHERE a.address IS NOT NULL AND a.name IS NOT NULL \n" +
"CALL apoc.spatial.geocodeOnce(a.address) YIELD location\n" +
"RETURN a.name, location.latitude AS latitude, \n" +
"location.longitude AS longitude, location.description AS description";
Expand All @@ -114,7 +119,7 @@ public void testSimpleGeocode() {
public void testGeocodePointAndDistance() {
String query = "WITH point({latitude: 48.8582532, longitude: 2.294287}) AS eiffel\n" +
"MATCH (a:Event) \n" +
"WHERE exists(a.address)\n" +
"WHERE a.address IS NOT NULL \n" +
"CALL apoc.spatial.geocodeOnce(a.address) YIELD location \n" +
"WITH location, point.distance(point({latitude: location.latitude, longitude:location.longitude}), eiffel) AS distance\n" +
"WHERE distance < 5000\n" +
Expand All @@ -127,15 +132,15 @@ public void testGeocodePointAndDistance() {
@Test
public void testGraphRefactoring() {
String refactorQuery = "MATCH (a:Event) \n" +
"WHERE exists(a.address) AND NOT exists(a.latitude)\n" +
"WHERE a.address IS NOT NULL AND a.latitude IS NULL \n" +
"WITH a LIMIT 1000\n" +
"CALL apoc.spatial.geocodeOnce(a.address) YIELD location \n" +
"SET a.latitude = location.latitude\n" +
"SET a.longitude = location.longitude";
testCallEmpty(db, refactorQuery, emptyMap());
String query = "WITH point({latitude: 48.8582532, longitude: 2.294287}) AS eiffel\n" +
"MATCH (a:Event) \n" +
"WHERE exists(a.latitude) AND exists(a.longitude)\n" +
"WHERE a.latitude IS NOT NULL AND a.longitude IS NOT NULL \n" +
"WITH a, point.distance(point(a), eiffel) AS distance\n" +
"WHERE distance < 5000\n" +
"RETURN a.name AS event, distance\n" +
Expand All @@ -149,7 +154,7 @@ public void testAllTheThings() throws Exception {
String query = "WITH apoc.date.parse('2016-06-01 00:00:00','h') as due_date,\n" +
" point({latitude: 48.8582532, longitude: 2.294287}) as eiffel\n" +
"MATCH (e:Event)\n" +
"WHERE exists(e.address) AND exists(e.datetime)\n" +
"WHERE e.address IS NOT NULL AND e.datetime IS NOT NULL \n" +
"WITH apoc.date.parse(e.datetime,'h') as hours, e, due_date, eiffel\n" +
"CALL apoc.spatial.geocodeOnce(e.address) YIELD location\n" +
"WITH e, location,\n" +
Expand Down Expand Up @@ -179,7 +184,7 @@ public void testAllTheThings() throws Exception {
@Test
public void testSimpleReverseGeocode() {
String query = "MATCH (a:Event) \n" +
"WHERE exists(a.lat) AND exists(a.lon) AND exists(a.name) \n" +
"WHERE a.lat IS NOT NULL AND a.lon IS NOT NULL AND a.name IS NOT NULL \n" +
"CALL apoc.spatial.reverseGeocode(a.lat, a.lon) YIELD latitude, longitude\n" +
"RETURN a.name, latitude, \n" +
"longitude, a.description";
Expand Down
6 changes: 4 additions & 2 deletions docs/asciidoc/modules/ROOT/pages/export/gephi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ You can export your graph as a weighted network, by specifying the property of a
.The following exports `ACTED_IN` paths where the `weightproperty` property exists on the relationship type
[source,cypher]
----
match path = (:Person)-[r:ACTED_IN]->(:Movie) where exists r.weightproperty
MATCH path = (:Person)-[r:ACTED_IN]->(:Movie)
WHERE r.weightproperty IS NOT NULL
WITH path LIMIT 1000
with collect(path) as paths
call apoc.gephi.add(null,'workspace0', paths, 'weightproperty') yield nodes, relationships, time
Expand All @@ -104,7 +105,8 @@ You can also export with your graph other properties of your nodes and/or relati
.The following exports `ACTED_IN` paths, but only includes the `birthYear` and `role` properties
[source,cypher]
----
match path = (:Person)-[r:ACTED_IN]->(:Movie) where exists r.weightproperty
MATCH path = (:Person)-[r:ACTED_IN]->(:Movie)
WHERE r.weightproperty IS NOT NULL
WITH path LIMIT 1000
with collect(path) as paths
call apoc.gephi.add(null,'workspace0', paths, 'weightproperty',['birthYear', 'role']) yield nodes, relationships, time
Expand Down
10 changes: 5 additions & 5 deletions docs/asciidoc/modules/ROOT/pages/misc/spatial.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ A more complex, or useful, example which geocodes addresses found in properties
[source,cypher]
----
MATCH (a:Place)
WHERE exists(a.address)
WHERE a.address IS NOT NULL
CALL apoc.spatial.geocodeOnce(a.address) YIELD location
RETURN location.latitude AS latitude, location.longitude AS longitude, location.description AS description
----
Expand All @@ -135,7 +135,7 @@ latitude and longitude to Cyper Point types, and then use the point.distance() f
----
WITH point({latitude: 48.8582532, longitude: 2.294287}) AS eiffel
MATCH (a:Place)
WHERE exists(a.address)
WHERE a.address IS NOT NULL
CALL apoc.spatial.geocodeOnce(a.address) YIELD location
WITH location, point.distance(point(location), eiffel) AS distance
WHERE distance < 5000
Expand Down Expand Up @@ -186,7 +186,7 @@ Geocode and persist the result
[source,cypher]
----
MATCH (a:Place)
WHERE exists(a.address) AND NOT exists(a.latitude)
WHERE a.address IS NOT NULL AND a.latitude IS NULL
WITH a LIMIT 1000
CALL apoc.spatial.geocodeOnce(a.address) YIELD location
SET a.latitude = location.latitude
Expand All @@ -207,7 +207,7 @@ Now make use of the results in distance queries
----
WITH point({latitude: 48.8582532, longitude: 2.294287}) AS eiffel
MATCH (a:Place)
WHERE exists(a.latitude) AND exists(a.longitude)
WHERE a.latitude IS NOT NULL AND a.longitude IS NOT NULL
WITH a, point.distance(point(a), eiffel) AS distance
WHERE distance < 5000
RETURN a.name, distance
Expand All @@ -223,7 +223,7 @@ Combining spatial and date-time functions can allow for more complex queries:
----
WITH point({latitude: 48.8582532, longitude: 2.294287}) AS eiffel
MATCH (e:Event)
WHERE exists(e.address) AND exists(e.datetime)
WHERE e.address IS NOT NULL AND e.datetime IS NOT NULL
CALL apoc.spatial.geocodeOnce(e.address) YIELD location
WITH e, location,
distance(point(location), eiffel) AS distance,
Expand Down
2 changes: 1 addition & 1 deletion docs/asciidoc/modules/ROOT/partials/periodic-commit.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The query is executed repeatedly in separate transactions until it returns 0.
[source,cypher]
----
call apoc.periodic.commit(
"match (user:User) WHERE exists( user.city )
"match (user:User) WHERE user.city IS NOT NULL
with user limit $limit
MERGE (city:City {name:user.city})
MERGE (user)-[:LIVES_IN]->(city)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ If we want to convert the `city` property to a node, we can do this in batches o
----
CALL apoc.periodic.commit(
"MATCH (person:Person)
WHERE exists(person.city)
WHERE person.city IS NOT NULL
WITH person limit $limit
MERGE (city:City {name:person.city})
MERGE (person)-[:LIVES_IN]->(city)
Expand All @@ -36,12 +36,12 @@ We can check that the refactoring has been done by running the following query:
[source,cypher]
----
MATCH (p:Person)
RETURN exists(p.city), count(*);
RETURN p.city IS NOT NULL as exists, count(*);
----

.Results
[opts="header"]
|===
| exists(p.city) | count(*)
| FALSE | 10000
| exists | count(*)
| FALSE | 10000
|===
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ CALL apoc.schema.relationships();
.Results
[opts="header"]
|===
| name | type | properties | status
| "CONSTRAINT ON ()-[liked:LIKED]-() ASSERT exists(liked.day)" | "RELATIONSHIP_PROPERTY_EXISTENCE" | ["day"] | ""
| name | type | properties | status
| "CONSTRAINT ON ()-[liked:LIKED]-() ASSERT liked.day IS NOT NULL" | "RELATIONSHIP_PROPERTY_EXISTENCE" | ["day"] | ""
|===
Loading

0 comments on commit cf02cca

Please sign in to comment.