txFunction) {
- try (Transaction tx = graphDb().beginTx()) {
- txFunction.accept(tx);
- tx.commit();
- }
- }
}
diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatial.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatial.java
index 2ce72e835..9692c459b 100644
--- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatial.java
+++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatial.java
@@ -25,15 +25,18 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.logging.Logger;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.neo4j.gis.spatial.filter.SearchIntersect;
+import org.neo4j.gis.spatial.functions.SpatialFunctions;
import org.neo4j.gis.spatial.index.IndexManagerImpl;
import org.neo4j.gis.spatial.osm.OSMDataset;
import org.neo4j.gis.spatial.osm.OSMImporter;
import org.neo4j.gis.spatial.osm.OSMLayer;
+import org.neo4j.gis.spatial.procedures.SpatialProcedures;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.internal.kernel.api.security.SecurityContext;
@@ -43,6 +46,7 @@
import org.neo4j.spatial.api.SpatialRecords;
import org.neo4j.spatial.api.index.SpatialIndexReader;
import org.neo4j.spatial.api.layer.Layer;
+import org.neo4j.spatial.testutils.Neo4jTestCase;
/**
*
@@ -88,6 +92,11 @@ public class TestSpatial extends Neo4jTestCase {
private final HashMap> layerTestGeometries = new HashMap<>();
private final HashMap geomStats = new HashMap<>();
+ @Override
+ protected List> loadProceduresAndFunctions() {
+ return List.of(SpatialFunctions.class, SpatialProcedures.class);
+ }
+
@BeforeEach
public void setUp() {
// TODO: Rather load this from a configuration file, properties file or JRuby test code
diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java
index 5b35666bb..ce50fa378 100644
--- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java
+++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java
@@ -23,6 +23,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import java.util.List;
import java.util.logging.Logger;
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.Test;
@@ -30,19 +31,27 @@
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
+import org.neo4j.gis.spatial.functions.SpatialFunctions;
import org.neo4j.gis.spatial.index.IndexManagerImpl;
import org.neo4j.gis.spatial.pipes.GeoPipeline;
+import org.neo4j.gis.spatial.procedures.SpatialProcedures;
import org.neo4j.graphdb.Transaction;
import org.neo4j.internal.kernel.api.security.SecurityContext;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.spatial.api.SpatialRecord;
import org.neo4j.spatial.api.layer.EditableLayer;
import org.neo4j.spatial.api.layer.Layer;
+import org.neo4j.spatial.testutils.Neo4jTestCase;
public class TestSpatialQueries extends Neo4jTestCase {
private static final Logger LOGGER = Logger.getLogger(TestSpatialQueries.class.getName());
+ @Override
+ protected List> loadProceduresAndFunctions() {
+ return List.of(SpatialFunctions.class, SpatialProcedures.class);
+ }
+
/**
* This test case is designed to capture the conditions described in the bug
* report at https://github.com/neo4j/neo4j-spatial/issues/11. There are
diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java
index 261d661bd..beafd3ff1 100644
--- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java
+++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java
@@ -32,21 +32,29 @@
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.neo4j.gis.spatial.SpatialTopologyUtils.PointResult;
+import org.neo4j.gis.spatial.functions.SpatialFunctions;
import org.neo4j.gis.spatial.index.IndexManagerImpl;
import org.neo4j.gis.spatial.osm.OSMDataset;
import org.neo4j.gis.spatial.osm.OSMImporter;
import org.neo4j.gis.spatial.osm.OSMLayer;
+import org.neo4j.gis.spatial.procedures.SpatialProcedures;
import org.neo4j.graphdb.Transaction;
import org.neo4j.internal.kernel.api.security.SecurityContext;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.spatial.api.SpatialRecord;
import org.neo4j.spatial.api.layer.EditableLayer;
import org.neo4j.spatial.api.layer.Layer;
+import org.neo4j.spatial.testutils.Neo4jTestCase;
public class TestSpatialUtils extends Neo4jTestCase {
private static final Logger LOGGER = Logger.getLogger(TestSpatialUtils.class.getName());
+ @Override
+ protected List> loadProceduresAndFunctions() {
+ return List.of(SpatialFunctions.class, SpatialProcedures.class);
+ }
+
@Test
public void testJTSLinearRef() {
SpatialDatabaseService spatial = new SpatialDatabaseService(
diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/functions/SpatialFunctionsTest.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/functions/SpatialFunctionsTest.java
index e35c28bcc..ecf00fb44 100644
--- a/server-plugin/src/test/java/org/neo4j/gis/spatial/functions/SpatialFunctionsTest.java
+++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/functions/SpatialFunctionsTest.java
@@ -32,11 +32,11 @@
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
-import org.neo4j.doc.domain.examples.ExampleCypher;
import org.neo4j.exceptions.KernelException;
-import org.neo4j.gis.spatial.AbstractApiTest;
import org.neo4j.gis.spatial.procedures.SpatialProcedures;
+import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.spatial.Geometry;
+import org.neo4j.spatial.testutils.AbstractApiTest;
public class SpatialFunctionsTest extends AbstractApiTest {
@@ -58,28 +58,22 @@ public void create_point_and_pass_as_param() {
@Test
public void create_point_geometry_return() {
- docExample("spatial.asGeometry", "Creates a point geometry")
- .runCypher(
- "WITH point({latitude: 5.0, longitude: 4.0}) as geom RETURN spatial.asGeometry(geom) AS geometry",
- ExampleCypher::storeResult)
- .assertSingleResult("geometry", geometry -> {
- assertInstanceOf(Geometry.class, geometry, "Should be Geometry type");
- });
+ Object geometry = executeObject(
+ "WITH point({latitude: 5.0, longitude: 4.0}) as geom RETURN spatial.asGeometry(geom) AS geometry",
+ "geometry");
+ assertInstanceOf(Geometry.class, geometry, "Should be Geometry type");
}
@Test
public void create_point_geometry_ad_map() {
- docExample("spatial.asMap", "Creates a point geometry as map")
- .runCypher(
- "WITH point({latitude: 5.0, longitude: 4.0}) as geom RETURN spatial.asMap(geom) AS geometry",
- ExampleCypher::storeResult)
- .assertSingleResult("geometry", geometry -> {
- Assertions.assertThat(geometry)
- .asInstanceOf(InstanceOfAssertFactories.MAP)
- .containsExactly(
- Assertions.entry("type", "Point"),
- Assertions.entry("coordinate", new double[]{4.0d, 5.0d}));
- });
+ Object geometry = executeObject(
+ "WITH point({latitude: 5.0, longitude: 4.0}) as geom RETURN spatial.asMap(geom) AS geometry",
+ "geometry");
+ Assertions.assertThat(geometry)
+ .asInstanceOf(InstanceOfAssertFactories.MAP)
+ .containsExactly(
+ Assertions.entry("type", "Point"),
+ Assertions.entry("coordinate", new double[]{4.0d, 5.0d}));
}
@Test
@@ -95,12 +89,9 @@ WITH point({latitude: 5.0, longitude: 4.0}) as geom
@Test
public void literal_geometry_return() {
- docExample("spatial.asGeometry", "Creates a point geometry from a map")
- .runCypher("WITH spatial.asGeometry({latitude: 5.0, longitude: 4.0}) AS geometry RETURN geometry",
- ExampleCypher::storeResult)
- .assertSingleResult("geometry", geometry -> {
- assertInstanceOf(Geometry.class, geometry, "Should be Geometry type");
- });
+ Object geometry = executeObject(
+ "WITH spatial.asGeometry({latitude: 5.0, longitude: 4.0}) AS geometry RETURN geometry", "geometry");
+ assertInstanceOf(Geometry.class, geometry, "Should be Geometry type");
}
/**
@@ -113,319 +104,295 @@ class WktToGeoJson {
@Test
public void testPoint() {
- docExample("spatial.wktToGeoJson", "1. Converts a WKT POINT")
- .runCypher("RETURN spatial.wktToGeoJson('POINT (30 10)') as json", ExampleCypher::storeResult)
- .assertSingleResult("json", json -> {
- assertThat(json, equalTo(Map.of(
- "type", "Point",
- "coordinates", List.of(30., 10.)
- )));
- });
+ String wkt = "POINT (30 10)";
+ Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt),
+ "json");
+ assertThat(json, equalTo(Map.of(
+ "type", "Point",
+ "coordinates", List.of(30., 10.)
+ )));
}
@Test
public void testLineString() {
- docExample("spatial.wktToGeoJson", "2. Converts a WKT LINESTRING")
- .runCypher("RETURN spatial.wktToGeoJson('LINESTRING (30 10, 10 30, 40 40)') as json",
- ExampleCypher::storeResult)
- .assertSingleResult("json", json -> {
- assertThat(json, equalTo(Map.of(
- "type", "LineString",
- "coordinates", List.of(List.of(30., 10.), List.of(10., 30.), List.of(40., 40.))
- )));
- });
+ String wkt = "LINESTRING (30 10, 10 30, 40 40)";
+ Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt),
+ "json");
+ assertThat(json, equalTo(Map.of(
+ "type", "LineString",
+ "coordinates", List.of(List.of(30., 10.), List.of(10., 30.), List.of(40., 40.))
+ )));
}
@Test
public void testPolygon() {
- docExample("spatial.wktToGeoJson", "3. Converts a WKT POLYGON")
- .runCypher("RETURN spatial.wktToGeoJson('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))') as json",
- ExampleCypher::storeResult)
- .assertSingleResult("json", json -> {
- assertThat(json, equalTo(Map.of(
- "type", "Polygon",
- "coordinates",
- List.of( // Polygon
- List.of( // LineString
- List.of(30., 10.),
- List.of(40., 40.),
- List.of(20., 40.),
- List.of(10., 20.),
- List.of(30., 10.)
- )
- )
- )));
- });
+ String wkt = "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))";
+ Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt),
+ "json");
+ assertThat(json, equalTo(Map.of(
+ "type", "Polygon",
+ "coordinates",
+ List.of( // Polygon
+ List.of( // LineString
+ List.of(30., 10.),
+ List.of(40., 40.),
+ List.of(20., 40.),
+ List.of(10., 20.),
+ List.of(30., 10.)
+ )
+ )
+ )));
}
@Test
public void testPolygonWithHole() {
- docExample("spatial.wktToGeoJson", "4. Converts a WKT POLYGON with a hole")
- .runCypher(
- "RETURN spatial.wktToGeoJson('POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))') as json",
- ExampleCypher::storeResult)
- .assertSingleResult("json", json -> {
- assertThat(json, equalTo(Map.of(
- "type", "Polygon",
- "coordinates",
- List.of( // Polygon
- List.of( // LineString
- List.of(35., 10.),
- List.of(45., 45.),
- List.of(15., 40.),
- List.of(10., 20.),
- List.of(35., 10.)
- ),
- List.of( // hole
- List.of(20., 30.),
- List.of(35., 35.),
- List.of(30., 20.),
- List.of(20., 30.)
- )
- )
- )));
- });
+ String wkt = "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),\n"
+ + "(20 30, 35 35, 30 20, 20 30))";
+ Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt),
+ "json");
+ assertThat(json, equalTo(Map.of(
+ "type", "Polygon",
+ "coordinates",
+ List.of( // Polygon
+ List.of( // LineString
+ List.of(35., 10.),
+ List.of(45., 45.),
+ List.of(15., 40.),
+ List.of(10., 20.),
+ List.of(35., 10.)
+ ),
+ List.of( // hole
+ List.of(20., 30.),
+ List.of(35., 35.),
+ List.of(30., 20.),
+ List.of(20., 30.)
+ )
+ )
+ )));
}
@Test
public void testMultiPoint() {
- docExample("spatial.wktToGeoJson", "5a. Converts a WKT MULTIPOINT")
- .runCypher("RETURN spatial.wktToGeoJson('MULTIPOINT ((10 40), (40 30), (20 20), (30 10))') as json",
- ExampleCypher::storeResult)
- .assertSingleResult("json", json -> {
- assertThat(json, equalTo(Map.of(
- "type", "MultiPoint",
- "coordinates", List.of(
- List.of(10., 40.),
- List.of(40., 30.),
- List.of(20., 20.),
- List.of(30., 10.)
- ))));
- });
+ String wkt = "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))";
+ Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt),
+ "json");
+ assertThat(json, equalTo(Map.of(
+ "type", "MultiPoint",
+ "coordinates", List.of(
+ List.of(10., 40.),
+ List.of(40., 30.),
+ List.of(20., 20.),
+ List.of(30., 10.)
+ ))));
}
@Test
public void testMultiPoint2() {
- docExample("spatial.wktToGeoJson", "5b. Converts a WKT MULTIPOINT")
- .runCypher("RETURN spatial.wktToGeoJson('MULTIPOINT (10 40, 40 30, 20 20, 30 10)') as json",
- ExampleCypher::storeResult)
- .assertSingleResult("json", json -> {
- assertThat(json, equalTo(Map.of(
- "type", "MultiPoint",
- "coordinates", List.of(
- List.of(10., 40.),
- List.of(40., 30.),
- List.of(20., 20.),
- List.of(30., 10.)
- ))));
- });
+ String wkt = "MULTIPOINT (10 40, 40 30, 20 20, 30 10)";
+ Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt),
+ "json");
+ assertThat(json, equalTo(Map.of(
+ "type", "MultiPoint",
+ "coordinates", List.of(
+ List.of(10., 40.),
+ List.of(40., 30.),
+ List.of(20., 20.),
+ List.of(30., 10.)
+ ))));
}
@Test
public void testMultiLineString() {
- docExample("spatial.wktToGeoJson", "6. Converts a WKT MULTILINESTRING")
- .runCypher(
- "RETURN spatial.wktToGeoJson('MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))') as json",
- ExampleCypher::storeResult)
- .assertSingleResult("json", json -> {
- assertThat(json, equalTo(Map.of(
- "type", "MultiLineString",
- "coordinates", List.of(
- List.of( // LineString
- List.of(10., 10.),
- List.of(20., 20.),
- List.of(10., 40.)
- ),
- List.of( // LineString
- List.of(40., 40.),
- List.of(30., 30.),
- List.of(40., 20.),
- List.of(30., 10.)
- )
- ))));
- });
+ String wkt = "MULTILINESTRING ((10 10, 20 20, 10 40),\n"
+ + "(40 40, 30 30, 40 20, 30 10))";
+ Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt),
+ "json");
+ assertThat(json, equalTo(Map.of(
+ "type", "MultiLineString",
+ "coordinates", List.of(
+ List.of( // LineString
+ List.of(10., 10.),
+ List.of(20., 20.),
+ List.of(10., 40.)
+ ),
+ List.of( // LineString
+ List.of(40., 40.),
+ List.of(30., 30.),
+ List.of(40., 20.),
+ List.of(30., 10.)
+ )
+ ))));
}
@Test
public void testMultiPolygon() {
- docExample("spatial.wktToGeoJson", "7a. Converts a WKT MULTIPOLYGON")
- .runCypher(
- "RETURN spatial.wktToGeoJson('MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))') as json",
- ExampleCypher::storeResult)
- .assertSingleResult("json", json -> {
- assertThat(json, equalTo(Map.of(
- "type", "MultiPolygon",
- "coordinates", List.of( // MultiPolygon
- List.of( // Polygon
- List.of( // LineString
- List.of(30., 20.),
- List.of(45., 40.),
- List.of(10., 40.),
- List.of(30., 20.)
- )
- ),
- List.of( // Polygon
- List.of( // LineString
- List.of(15., 5.),
- List.of(40., 10.),
- List.of(10., 20.),
- List.of(5., 10.),
- List.of(15., 5.)
- )
- )
- ))));
- });
+ String wkt = "MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),\n"
+ + "((15 5, 40 10, 10 20, 5 10, 15 5)))";
+ Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt),
+ "json");
+ assertThat(json, equalTo(Map.of(
+ "type", "MultiPolygon",
+ "coordinates", List.of( // MultiPolygon
+ List.of( // Polygon
+ List.of( // LineString
+ List.of(30., 20.),
+ List.of(45., 40.),
+ List.of(10., 40.),
+ List.of(30., 20.)
+ )
+ ),
+ List.of( // Polygon
+ List.of( // LineString
+ List.of(15., 5.),
+ List.of(40., 10.),
+ List.of(10., 20.),
+ List.of(5., 10.),
+ List.of(15., 5.)
+ )
+ )
+ )
+ )));
}
@Test
public void testMultiPolygon2() {
- docExample("spatial.wktToGeoJson", "7b. Converts a WKT MULTIPOLYGON")
- .runCypher(
- "RETURN spatial.wktToGeoJson('MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))') as json",
- ExampleCypher::storeResult)
- .assertSingleResult("json", json -> {
- assertThat(json, equalTo(Map.of(
- "type", "MultiPolygon",
- "coordinates", List.of( // MultiPolygon
- List.of( // Polygon
- List.of( // LineString
- List.of(40., 40.),
- List.of(20., 45.),
- List.of(45., 30.),
- List.of(40., 40.)
- )
- ),
- List.of( // Polygon
- List.of( // LineString
- List.of(20., 35.),
- List.of(10., 30.),
- List.of(10., 10.),
- List.of(30., 5.),
- List.of(45., 20.),
- List.of(20., 35.)
- ),
- List.of( // hole
- List.of(30., 20.),
- List.of(20., 15.),
- List.of(20., 25.),
- List.of(30., 20.)
- )
- )
- ))));
- });
+ String wkt = """
+ MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),
+ ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
+ (30 20, 20 15, 20 25, 30 20)))""";
+ Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt),
+ "json");
+ assertThat(json, equalTo(Map.of(
+ "type", "MultiPolygon",
+ "coordinates", List.of( // MultiPolygon
+ List.of( // Polygon
+ List.of( // LineString
+ List.of(40., 40.),
+ List.of(20., 45.),
+ List.of(45., 30.),
+ List.of(40., 40.)
+ )
+ ),
+ List.of( // Polygon
+ List.of( // LineString
+ List.of(20., 35.),
+ List.of(10., 30.),
+ List.of(10., 10.),
+ List.of(30., 5.),
+ List.of(45., 20.),
+ List.of(20., 35.)
+ ),
+ List.of( // hole
+ List.of(30., 20.),
+ List.of(20., 15.),
+ List.of(20., 25.),
+ List.of(30., 20.)
+ )
+ )
+ )
+ )));
}
@Test
public void testGeometryCollection() {
- docExample("spatial.wktToGeoJson", "8. FConverts a WKT GEOMETRYCOLLECTION")
- .runCypher(
- "RETURN spatial.wktToGeoJson('GEOMETRYCOLLECTION (POINT (40 10), LINESTRING (10 10, 20 20, 10 40), POLYGON ((40 40, 20 45, 45 30, 40 40)))') as json",
- ExampleCypher::storeResult)
- .assertSingleResult("json", json -> {
- assertThat(json, equalTo(Map.of(
- "type", "GeometryCollection",
- "geometries", List.of(
- Map.of( // Point
- "type", "Point",
- "coordinates", List.of(40., 10.)
- ),
- Map.of( // LineString
- "type", "LineString",
- "coordinates", List.of(
- List.of(10., 10.),
- List.of(20., 20.),
- List.of(10., 40.)
- )
- ),
- Map.of( // Polygon
- "type", "Polygon",
- "coordinates", List.of(
- List.of( // LineString
- List.of(40., 40.),
- List.of(20., 45.),
- List.of(45., 30.),
- List.of(40., 40.)
- )
- )
- )
- )
- )));
- });
+ String wkt = """
+ GEOMETRYCOLLECTION (POINT (40 10),
+ LINESTRING (10 10, 20 20, 10 40),
+ POLYGON ((40 40, 20 45, 45 30, 40 40)))""";
+ Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt),
+ "json");
+ assertThat(json, equalTo(Map.of(
+ "type", "GeometryCollection",
+ "geometries", List.of(
+ Map.of( // Point
+ "type", "Point",
+ "coordinates", List.of(40., 10.)
+ ),
+ Map.of( // LineString
+ "type", "LineString",
+ "coordinates", List.of(
+ List.of(10., 10.),
+ List.of(20., 20.),
+ List.of(10., 40.)
+ )
+ ),
+ Map.of( // Polygon
+ "type", "Polygon",
+ "coordinates", List.of(
+ List.of( // LineString
+ List.of(40., 40.),
+ List.of(20., 45.),
+ List.of(45., 30.),
+ List.of(40., 40.)
+ )
+ )
+ )
+ )
+ )));
}
}
@Test
public void testPointToWkt() {
- docExample("spatial.neo4jGeometryToWkt", "Converting a point to WKT")
- .runCypher("RETURN spatial.neo4jGeometryToWkt(point({longitude: 1, latitude: 2})) as wkt",
- ExampleCypher::storeResult)
- .assertSingleResult("wkt", wkt -> {
- assertThat(wkt, equalTo("POINT ( 1 2 )"));
- });
+ Object wkt = executeObject("return spatial.neo4jGeometryToWkt(point({longitude: 1, latitude: 2})) as wkt",
+ "wkt");
+ assertThat(wkt, equalTo("POINT ( 1 2 )"));
}
@Test
public void testPointArrayToWkt() {
- docExample("spatial.neo4jGeometryToWkt", "Converting a point array to WKT")
- .runCypher(
- "RETURN spatial.neo4jGeometryToWkt([point({longitude: 1, latitude: 2}), point({longitude: 3, latitude: 4}) ]) as wkt",
- ExampleCypher::storeResult)
- .assertSingleResult("wkt", wkt -> {
- assertThat(wkt, equalTo("LINESTRING (1 2, 3 4)"));
- });
+ Object wkt = executeObject(
+ "return spatial.neo4jGeometryToWkt([point({longitude: 1, latitude: 2}), point({longitude: 3, latitude: 4}) ]) as wkt",
+ "wkt");
+ assertThat(wkt, equalTo("LINESTRING (1 2, 3 4)"));
}
@Test
public void testExtractAttributes() {
- docExample("spatial.extractAttributes", "Extracts attributes from a layer node")
- .runCypher("""
- CALL spatial.addPointLayer('attr_layer') YIELD node
- WITH node
- CREATE (n:Point {longitude: 10.0, latitude: 20.0, name: 'test_point', type: 'landmark', elevation: 100})
- WITH n
- CALL spatial.addNode('attr_layer', n) YIELD node as added_node
- WITH n
- RETURN spatial.extractAttributes('attr_layer', n) as attributes
- """,
- ExampleCypher::storeResult)
- .assertSingleResult("attributes", attributes -> {
- Assertions.assertThat(attributes)
- .asInstanceOf(InstanceOfAssertFactories.MAP)
- .containsKeys("name", "type", "elevation")
- .containsEntry("name", "test_point")
- .containsEntry("type", "landmark")
- .containsEntry("elevation", 100L);
- });
+ Object attributes = executeObject(
+ """
+ CALL spatial.addPointLayer('attr_layer') YIELD node
+ WITH node
+ CREATE (n:Point {longitude: 10.0, latitude: 20.0, name: 'test_point', type: 'landmark', elevation: 100})
+ WITH n
+ CALL spatial.addNode('attr_layer', n) YIELD node as added_node
+ WITH n
+ RETURN spatial.extractAttributes('attr_layer', n) as attributes
+ """,
+ "attributes");
+ Assertions.assertThat(attributes)
+ .asInstanceOf(InstanceOfAssertFactories.MAP)
+ .containsKeys("name", "type", "elevation")
+ .containsEntry("name", "test_point")
+ .containsEntry("type", "landmark")
+ .containsEntry("elevation", 100L);
}
@Test
public void testExtractAttributesWithWKT() {
- docExample("spatial.extractAttributes", "Extracts attributes from a WKT layer node")
- .runCypher("""
- CALL spatial.addLayer('wkt_attr_layer', 'WKT', '') YIELD node
- WITH node
- CREATE (n:Geometry {geometry: 'POINT (30 10)', name: 'test_wkt_point', category: 'marker', id: 42})
- WITH n
- CALL spatial.addNode('wkt_attr_layer', n) YIELD node as added_node
- WITH n
- RETURN spatial.extractAttributes('wkt_attr_layer', n) as attributes
- """,
- ExampleCypher::storeResult)
- .assertSingleResult("attributes", attributes -> {
- Assertions.assertThat(attributes)
- .asInstanceOf(InstanceOfAssertFactories.MAP)
- .containsKeys("name", "category", "id")
- .containsEntry("name", "test_wkt_point")
- .containsEntry("category", "marker")
- .containsEntry("id", 42L);
- });
+ Object attributes = executeObject(
+ """
+ CALL spatial.addLayer('wkt_attr_layer', 'WKT', '') YIELD node
+ WITH node
+ CREATE (n:Geometry {geometry: 'POINT (30 10)', name: 'test_wkt_point', category: 'marker', id: 42})
+ WITH n
+ CALL spatial.addNode('wkt_attr_layer', n) YIELD node as added_node
+ WITH n
+ RETURN spatial.extractAttributes('wkt_attr_layer', n) as attributes
+ """,
+ "attributes");
+ Assertions.assertThat(attributes)
+ .asInstanceOf(InstanceOfAssertFactories.MAP)
+ .containsKeys("name", "category", "id")
+ .containsEntry("name", "test_wkt_point")
+ .containsEntry("category", "marker")
+ .containsEntry("id", 42L);
}
@Test
public void testDecodeGeometryAndExtractAttributesTogether() {
- docExample("spatial.decodeGeometry", "Using both functions together")
- .additionalSignature("spatial.extractAttributes")
- .runCypher("""
+ try (Transaction tx = db.beginTx()) {
+ var result = tx.execute("""
CALL spatial.addPointLayer('combined_layer') YIELD node
WITH node
CREATE (n:Point {longitude: 5.5, latitude: 45.5, city: 'TestCity', population: 50000})
@@ -435,35 +402,29 @@ public void testDecodeGeometryAndExtractAttributesTogether() {
RETURN
spatial.decodeGeometry('combined_layer', n) as geometry,
spatial.extractAttributes('combined_layer', n) as attributes
- """,
- ExampleCypher::storeResult)
- .assertSingleResult("geometry", geometry -> {
- assertInstanceOf(Geometry.class, geometry, "Should be Geometry type");
- })
- .assertSingleResult("attributes", attributes -> {
- Assertions.assertThat(attributes)
- .asInstanceOf(InstanceOfAssertFactories.MAP)
- .containsKeys("city", "population")
- .containsEntry("city", "TestCity")
- .containsEntry("population", 50000L);
- });
+ """).next();
+ assertInstanceOf(Geometry.class, result.get("geometry"), "Should be Geometry type");
+ Assertions.assertThat(result.get("attributes"))
+ .asInstanceOf(InstanceOfAssertFactories.MAP)
+ .containsKeys("city", "population")
+ .containsEntry("city", "TestCity")
+ .containsEntry("population", 50000L);
+ }
}
@Test
public void testNodeAsWKT() {
- docExample("spatial.nodeAsWKT", "Converting a layer node to WKT")
- .runCypher("""
- CALL spatial.addPointLayer('wkt_layer') YIELD node
- WITH node
- CREATE (n:Point {longitude: 10.0, latitude: 20.0})
- WITH n
- CALL spatial.addNode('wkt_layer', n) YIELD node as added_node
- WITH n
- RETURN spatial.nodeAsWKT('wkt_layer', n) as wkt
- """,
- ExampleCypher::storeResult)
- .assertSingleResult("wkt", wkt -> {
- assertThat(wkt, equalTo("POINT (10 20)"));
- });
+ Object wkt = executeObject(
+ """
+ CALL spatial.addPointLayer('wkt_layer') YIELD node
+ WITH node
+ CREATE (n:Point {longitude: 10.0, latitude: 20.0})
+ WITH n
+ CALL spatial.addNode('wkt_layer', n) YIELD node as added_node
+ WITH n
+ RETURN spatial.nodeAsWKT('wkt_layer', n) as wkt
+ """,
+ "wkt");
+ assertThat(wkt, equalTo("POINT (10 20)"));
}
}
diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesPerformanceTest.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesPerformanceTest.java
index 8e675eb0f..669c29a30 100644
--- a/server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesPerformanceTest.java
+++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesPerformanceTest.java
@@ -22,20 +22,23 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.Coordinate;
-import org.neo4j.gis.spatial.Neo4jTestCase;
import org.neo4j.gis.spatial.SimplePointLayer;
import org.neo4j.gis.spatial.SpatialDatabaseRecord;
import org.neo4j.gis.spatial.SpatialDatabaseService;
+import org.neo4j.gis.spatial.functions.SpatialFunctions;
import org.neo4j.gis.spatial.index.IndexManagerImpl;
+import org.neo4j.gis.spatial.procedures.SpatialProcedures;
import org.neo4j.graphdb.Transaction;
import org.neo4j.internal.kernel.api.security.SecurityContext;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.spatial.api.layer.Layer;
+import org.neo4j.spatial.testutils.Neo4jTestCase;
public class GeoPipesPerformanceTest extends Neo4jTestCase {
@@ -44,6 +47,11 @@ public class GeoPipesPerformanceTest extends Neo4jTestCase {
private final int records = 10000;
private final int chunkSize = records / 10;
+ @Override
+ protected List> loadProceduresAndFunctions() {
+ return List.of(SpatialFunctions.class, SpatialProcedures.class);
+ }
+
@BeforeEach
public void setUp() throws Exception {
loadSamplePointData();
diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java
index cf7eb7e9a..2aea3a750 100644
--- a/server-plugin/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java
+++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java
@@ -42,7 +42,6 @@
import java.util.ArrayList;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Logger;
@@ -50,15 +49,11 @@
import java.util.stream.Stream;
import org.assertj.core.api.Assertions;
import org.hamcrest.MatcherAssert;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-import org.neo4j.doc.domain.examples.Example;
-import org.neo4j.doc.domain.examples.ExampleCypher;
import org.neo4j.exceptions.KernelException;
-import org.neo4j.gis.spatial.AbstractApiTest;
import org.neo4j.gis.spatial.SpatialDatabaseService;
import org.neo4j.gis.spatial.SpatialRelationshipTypes;
import org.neo4j.gis.spatial.functions.SpatialFunctions;
@@ -72,10 +67,10 @@
import org.neo4j.graphdb.spatial.Geometry;
import org.neo4j.graphdb.spatial.Point;
import org.neo4j.kernel.api.KernelTransaction;
-import org.neo4j.kernel.api.procedure.GlobalProcedures;
import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.spatial.api.layer.Layer;
+import org.neo4j.spatial.testutils.AbstractApiTest;
public class SpatialProceduresTest extends AbstractApiTest {
@@ -139,23 +134,6 @@ public static void testResult(GraphDatabaseService db, String call, Consumer params,
- Consumer resultConsumer) {
- try (Transaction tx = db.beginTx()) {
- Map p = (params == null) ? Map.of() : params;
- resultConsumer.accept(tx.execute(call, p));
- tx.commit();
- }
- }
-
- public static void registerProceduresAndFunctions(GraphDatabaseService db, Class> procedure)
- throws KernelException {
- GlobalProcedures procedures = ((GraphDatabaseAPI) db).getDependencyResolver()
- .resolveDependency(GlobalProcedures.class);
- procedures.registerProcedure(procedure);
- procedures.registerFunction(procedure);
- }
-
private static Layer makeLayerOfVariousTypes(SpatialDatabaseService spatial, Transaction tx, String name,
int index) {
return switch (index % 3) {
@@ -323,16 +301,11 @@ public void create_point_and_return() {
@Test
public void create_node_decode_to_geometry() {
- docExample("spatial.decodeGeometry", "Decode a geometry from a node property")
- .additionalSignature("spatial.addWKTLayer")
- .runCypher("CALL spatial.addWKTLayer('geom','geom')",
- config -> config.storeResult().setTitle("Create a WKT layer"))
- .runCypher(
- "CREATE (n:Node {geom:'POINT(4.0 5.0)'}) RETURN spatial.decodeGeometry('geom',n) AS geometry",
- config -> config.storeResult().setTitle("Decode a geometry"))
- .assertSingleResult("geometry", geometry -> {
- assertInstanceOf(Geometry.class, geometry, "Should be Geometry type");
- });
+ execute("CALL spatial.addWKTLayer('geom','geom')");
+ Object geometry = executeObject(
+ "CREATE (n:Node {geom:'POINT(4.0 5.0)'}) RETURN spatial.decodeGeometry('geom',n) AS geometry",
+ "geometry");
+ assertInstanceOf(Geometry.class, geometry, "Should be Geometry type");
}
@Test
@@ -349,56 +322,49 @@ public void create_node_and_convert_to_geometry() {
@Test
public void testAddNativePointLayerWithConfig() {
- docExample("spatial.addNativePointLayerWithConfig", "Create a native point layer with a configuration")
- .runCypher("CALL spatial.addNativePointLayerWithConfig('geom','pos:mbr','hilbert')",
- ExampleCypher::storeResult)
- .assertSingleResult("node", o -> {
- Assertions.assertThat(o).isInstanceOf(Node.class);
- Node node = (Node) o;
- assertEquals("geom", node.getProperty(PROP_LAYER));
- assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER));
- assertEquals("hilbert", node.getProperty(PROP_INDEX_TYPE));
- assertEquals("pos:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG));
- });
+ testCall(db, "CALL spatial.addNativePointLayerWithConfig('geom','pos:mbr','hilbert')", (result) -> {
+ Node node = (Node) result.get("node");
+ assertEquals("geom", node.getProperty(PROP_LAYER));
+ assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER));
+ assertEquals("hilbert",
+ node.getProperty(PROP_INDEX_TYPE));
+ assertEquals("pos:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG));
+ });
}
@Test
public void testAddNativePointLayerXY() {
- docExample("spatial.addNativePointLayerXY", "Create a native point layer")
- .additionalSignature("spatial.withinDistance")
- .additionalSignature("spatial.addNode")
- .runCypher("CALL spatial.addNativePointLayerXY('geom','x','y')",
- ExampleCypher::storeResult)
- .assertSingleResult("node", o -> {
- Assertions.assertThat(o).isInstanceOf(Node.class);
- Node node = (Node) o;
- assertEquals("geom", node.getProperty(PROP_LAYER));
- assertEquals("SimplePointEncoder", node.getProperty(PROP_GEOMENCODER));
- assertEquals("rtree", node.getProperty(PROP_INDEX_TYPE));
- assertEquals("x:y", node.getProperty(PROP_GEOMENCODER_CONFIG));
- })
- .runCypher(
- "CREATE (n:Node {id: 42, x: 5.0, y: 4.0}) WITH n CALL spatial.addNode('geom',n) YIELD node RETURN node",
- config -> config.setComment("create a node and add it to the index"))
- .runCypher("CALL spatial.withinDistance('geom',point({latitude:4.1,longitude:5.1}),100)",
- config -> config.storeResult().setComment("Find node within distance"))
- .assertSingleResult("node", o -> assertEquals(42L, ((Node) o).getProperty("id")));
+ testCall(db, "CALL spatial.addNativePointLayerXY('geom','x','y')", (result) -> {
+ var o = result.get("node");
+ Assertions.assertThat(o).isInstanceOf(Node.class);
+ Node node = (Node) o;
+ assertEquals("geom", node.getProperty(PROP_LAYER));
+ assertEquals("SimplePointEncoder", node.getProperty(PROP_GEOMENCODER));
+ assertEquals("rtree", node.getProperty(PROP_INDEX_TYPE));
+ assertEquals("x:y", node.getProperty(PROP_GEOMENCODER_CONFIG));
+ });
+
+ execute(
+ "CREATE (n:Node {id: 42, x: 5.0, y: 4.0}) WITH n CALL spatial.addNode('geom',n) YIELD node RETURN node");
+
+ testCall(db, "CALL spatial.withinDistance('geom',point({latitude:4.1,longitude:5.1}),100)", (result) -> {
+ var o = result.get("node");
+ Assertions.assertThat(o).isInstanceOf(Node.class);
+ assertEquals(42L, ((Node) o).getProperty("id"));
+ });
}
@Test
public void create_a_pointlayer_with_x_and_y() {
testCall(db, "CALL spatial.addPointLayerXY('geom','lon','lat')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
}
@Test
public void create_a_pointlayer_with_config() {
- docExample("spatial.addPointLayerWithConfig", "Create a point layer with X and Y properties")
- .runCypher("CALL spatial.addPointLayerWithConfig('geom','lon:lat')", ExampleCypher::storeResult)
- .assertSingleResult("node", node -> {
- assertEquals("geom", ((Node) node).getProperty(PROP_LAYER));
- });
+ testCall(db, "CALL spatial.addPointLayerWithConfig('geom','lon:lat')",
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
}
@Test
@@ -406,19 +372,7 @@ public void create_a_pointlayer_with_config_on_existing_wkt_layer() {
execute("CALL spatial.addWKTLayer('geom','wkt')");
try {
testCall(db, "CALL spatial.addPointLayerWithConfig('geom','lon:lat')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
- fail("Expected exception to be thrown");
- } catch (Exception e) {
- Assertions.assertThat(e.getMessage()).contains("Layer already exists: 'geom'");
- }
- }
-
- @Test
- public void create_a_pointlayer_with_config_on_existing_osm_layer() {
- execute("CALL spatial.addLayer('geom','OSM','')");
- try {
- testCall(db, "CALL spatial.addPointLayerWithConfig('geom','lon:lat')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
fail("Expected exception to be thrown");
} catch (Exception e) {
Assertions.assertThat(e.getMessage()).contains("Layer already exists: 'geom'");
@@ -428,31 +382,31 @@ public void create_a_pointlayer_with_config_on_existing_osm_layer() {
@Test
public void create_a_pointlayer_with_rtree() {
testCall(db, "CALL spatial.addPointLayer('geom')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
}
@Test
public void create_a_pointlayer_with_geohash() {
testCall(db, "CALL spatial.addPointLayerGeohash('geom')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
}
@Test
public void create_a_pointlayer_with_zorder() {
testCall(db, "CALL spatial.addPointLayerZOrder('geom')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
}
@Test
public void create_a_pointlayer_with_hilbert() {
testCall(db, "CALL spatial.addPointLayerHilbert('geom')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
}
@Test
public void create_and_delete_a_pointlayer_with_rtree() {
testCall(db, "CALL spatial.addPointLayer('geom')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
testCallCount(db, "CALL spatial.layers()", null, 1);
execute("CALL spatial.removeLayer('geom')");
testCallCount(db, "CALL spatial.layers()", null, 0);
@@ -461,7 +415,7 @@ public void create_and_delete_a_pointlayer_with_rtree() {
@Test
public void create_and_delete_a_pointlayer_with_geohash() {
testCall(db, "CALL spatial.addPointLayerGeohash('geom')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
testCallCount(db, "CALL spatial.layers()", null, 1);
execute("CALL spatial.removeLayer('geom')");
testCallCount(db, "CALL spatial.layers()", null, 0);
@@ -470,7 +424,7 @@ public void create_and_delete_a_pointlayer_with_geohash() {
@Test
public void create_and_delete_a_pointlayer_with_zorder() {
testCall(db, "CALL spatial.addPointLayerZOrder('geom')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
testCallCount(db, "CALL spatial.layers()", null, 1);
execute("CALL spatial.removeLayer('geom')");
testCallCount(db, "CALL spatial.layers()", null, 0);
@@ -479,7 +433,7 @@ public void create_and_delete_a_pointlayer_with_zorder() {
@Test
public void create_and_delete_a_pointlayer_with_hilbert() {
testCall(db, "CALL spatial.addPointLayerHilbert('geom')",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
testCallCount(db, "CALL spatial.layers()", null, 1);
execute("CALL spatial.removeLayer('geom')");
testCallCount(db, "CALL spatial.layers()", null, 0);
@@ -487,96 +441,68 @@ public void create_and_delete_a_pointlayer_with_hilbert() {
@Test
public void create_a_simple_pointlayer_using_named_encoder() {
- Example example = docExample("spatial.addLayerWithEncoder", "Create a `SimplePointEncoder`");
- example.runCypher("CALL spatial.addLayerWithEncoder('geom','SimplePointEncoder','')",
- ExampleCypher::storeResult)
- .assertSingleResult("node", o -> {
- Assertions.assertThat(o).isInstanceOf(Node.class);
- Node node = (Node) o;
- assertEquals("geom", node.getProperty(PROP_LAYER));
- assertEquals("SimplePointEncoder", node.getProperty(PROP_GEOMENCODER));
- assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
- assertFalse(node.hasProperty(PROP_GEOMENCODER_CONFIG));
- });
+ testCall(db, "CALL spatial.addLayerWithEncoder('geom','SimplePointEncoder','')", (r) -> {
+ Node node = (Node) r.get("node");
+ assertEquals("geom", node.getProperty(PROP_LAYER));
+ assertEquals("SimplePointEncoder",
+ node.getProperty(PROP_GEOMENCODER));
+ assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
+ assertFalse(node.hasProperty(PROP_GEOMENCODER_CONFIG));
+ });
}
@Test
public void create_a_simple_pointlayer_using_named_and_configured_encoder() {
- Example example = docExample("spatial.addLayerWithEncoder",
- "Create a `SimplePointEncoder` with a customized encoder configuration");
- example.runCypher("CALL spatial.addLayerWithEncoder('geom','SimplePointEncoder','x:y:mbr')",
- config -> config.storeResult().setComment("""
- Configures the encoder to use the nodes `x` property instead of `longitude`,
- the `y` property instead of `latitude`
- and the `mbr` property instead of `bbox`.
- """))
- .assertSingleResult("node", o -> {
- Assertions.assertThat(o).isInstanceOf(Node.class);
- Node node = (Node) o;
- assertEquals("geom", node.getProperty(PROP_LAYER));
- assertEquals("SimplePointEncoder", node.getProperty(PROP_GEOMENCODER));
- assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
- assertEquals("x:y:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG));
- });
+ testCall(db, "CALL spatial.addLayerWithEncoder('geom','SimplePointEncoder','x:y:mbr')", (r) -> {
+ Node node = (Node) r.get("node");
+ assertEquals("geom", node.getProperty(PROP_LAYER));
+ assertEquals("SimplePointEncoder",
+ node.getProperty(PROP_GEOMENCODER));
+ assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
+ assertEquals("x:y:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG));
+ });
}
@Test
public void create_a_native_pointlayer_using_named_encoder() {
- Example example = docExample("spatial.addLayerWithEncoder", "Create a `NativePointEncoder`");
- example.runCypher("CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','')",
- ExampleCypher::storeResult)
- .assertSingleResult("node", o -> {
- Assertions.assertThat(o).isInstanceOf(Node.class);
- Node node = (Node) o;
- assertEquals("geom", node.getProperty(PROP_LAYER));
- assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER));
- assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
- assertFalse(node.hasProperty(PROP_GEOMENCODER_CONFIG));
- });
+ testCall(db, "CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','')", (r) -> {
+ Node node = (Node) r.get("node");
+ assertEquals("geom", node.getProperty(PROP_LAYER));
+ assertEquals("NativePointEncoder",
+ node.getProperty(PROP_GEOMENCODER));
+ assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
+ assertFalse(node.hasProperty(PROP_GEOMENCODER_CONFIG));
+ });
}
@Test
public void create_a_native_pointlayer_using_named_and_configured_encoder() {
- Example example = docExample("spatial.addLayerWithEncoder",
- "Create a `NativePointEncoder` with a customized encoder configuration");
- example.runCypher("CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr')",
- config -> config.storeResult().setComment("""
- Configures the encoder to use the nodes `pos` property instead of `location`
- and the `mbr` property instead of `bbox`.
- """))
- .assertSingleResult("node", o -> {
- Assertions.assertThat(o).isInstanceOf(Node.class);
- Node node = (Node) o;
- assertEquals("geom", node.getProperty(PROP_LAYER));
- assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER));
- assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
- assertEquals("pos:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG));
- });
+ testCall(db, "CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr')", (r) -> {
+ Node node = (Node) r.get("node");
+ assertEquals("geom", node.getProperty(PROP_LAYER));
+ assertEquals("NativePointEncoder",
+ node.getProperty(PROP_GEOMENCODER));
+ assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
+ assertEquals("pos:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG));
+ });
}
@Test
public void create_a_native_pointlayer_using_named_and_configured_encoder_with_cartesian() {
- Example example = docExample("spatial.addLayerWithEncoder",
- "Create a `NativePointEncoder` with a customized encoder configuration using Cartesian coordinates");
- example.runCypher("CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr:Cartesian')",
- config -> config.storeResult().setComment("""
- Configures the encoder to use the nodes `pos` property instead of `location`,
- the `mbr` property instead of `bbox` and Cartesian coordinates.
- """))
- .assertSingleResult("node", o -> {
- Assertions.assertThat(o).isInstanceOf(Node.class);
- Node node = (Node) o;
- assertEquals("geom", node.getProperty(PROP_LAYER));
- assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER));
- assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
- assertEquals("pos:mbr:Cartesian", node.getProperty(PROP_GEOMENCODER_CONFIG));
- });
+ testCall(db, "CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr:Cartesian')", (r) -> {
+ Node node = (Node) r.get("node");
+ assertEquals("geom", node.getProperty(PROP_LAYER));
+ assertEquals("NativePointEncoder",
+ node.getProperty(PROP_GEOMENCODER));
+ assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
+ assertEquals("pos:mbr:Cartesian", node.getProperty(PROP_GEOMENCODER_CONFIG));
+ });
}
@Test
public void create_a_native_pointlayer_using_named_and_configured_encoder_with_geographic() {
testCall(db, "CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr:WGS-84')", (r) -> {
- Node node = dump((Node) r.get("node"));
+ Node node = (Node) r.get("node");
assertEquals("geom", node.getProperty(PROP_LAYER));
assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER));
assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE));
@@ -587,7 +513,7 @@ public void create_a_native_pointlayer_using_named_and_configured_encoder_with_g
@Test
public void create_a_wkt_layer_using_know_format() {
testCall(db, "CALL spatial.addLayer('geom','WKT',null)",
- (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER)));
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
}
@Test
@@ -605,14 +531,10 @@ public void list_layer_names() {
@Test
public void add_and_remove_layer() {
- docExample("spatial.layers", "Add and Remove a layer")
- .additionalSignature("spatial.removeLayer")
- .runCypher("CALL spatial.addWKTLayer('geom','wkt')", ExampleCypher::storeResult)
- .runCypher("CALL spatial.layers()", ExampleCypher::storeResult)
- .assertResult(res -> Assertions.assertThat(res).hasSize(1))
- .runCypher("CALL spatial.removeLayer('geom')", ExampleCypher::storeResult)
- .runCypher("CALL spatial.layers()", ExampleCypher::storeResult)
- .assertResult(res -> Assertions.assertThat(res).isEmpty());
+ execute("CALL spatial.addWKTLayer('geom','wkt')");
+ testCallCount(db, "CALL spatial.layers()", null, 1);
+ execute("CALL spatial.removeLayer('geom')");
+ testCallCount(db, "CALL spatial.layers()", null, 0);
}
@Test
@@ -637,15 +559,11 @@ public void add_and_remove_multiple_layers() {
@Test
public void get_and_set_feature_attributes() {
- docExample("spatial.getFeatureAttributes", "Get the feature attributes of a layer")
- .additionalSignature("spatial.setFeatureAttributes")
- .runCypher("CALL spatial.addWKTLayer('geom','wkt')", ExampleCypher::storeResult)
- .runCypher("CALL spatial.getFeatureAttributes('geom')", ExampleCypher::storeResult)
- .assertResult(res -> Assertions.assertThat(res).hasSize(0))
- .runCypher("CALL spatial.setFeatureAttributes('geom',['name','type','color'])",
- ExampleCypher::storeResult)
- .runCypher("CALL spatial.getFeatureAttributes('geom')", ExampleCypher::storeResult)
- .assertResult(res -> Assertions.assertThat(res).hasSize(3));
+ execute("CALL spatial.addWKTLayer('geom','wkt')");
+ testCallCount(db, "CALL spatial.layers()", null, 1);
+ testCallCount(db, "CALL spatial.getFeatureAttributes('geom')", null, 0);
+ execute("CALL spatial.setFeatureAttributes('geom',['name','type','color'])");
+ testCallCount(db, "CALL spatial.getFeatureAttributes('geom')", null, 3);
}
@Test
@@ -678,36 +596,34 @@ public void list_spatial_procedures() {
@Test
public void list_layer_types() {
- Example example = docExample("spatial.layerTypes", "List the available layer types");
- example.runCypher("CALL spatial.layerTypes()", ExampleCypher::storeResult)
- .assertResult(res -> {
- Assertions.assertThat(res).hasSize(12);
- // Convert result list to a Map keyed by 'id' for easy lookup
- Map> layerTypes = res.stream()
- .collect(Collectors.toMap(r -> r.get("id").toString(), r -> r));
-
- assertLayerType(layerTypes, "SimplePoint", "SimplePointEncoder", "SimplePointLayer", "rtree",
- "longitude:latitude");
- assertLayerType(layerTypes, "NativePoint", "NativePointEncoder", "SimplePointLayer", "rtree",
- "location");
- assertLayerType(layerTypes, "NativePoints", "NativePointsEncoder", "EditableLayer", "rtree",
- "geometry");
- assertLayerType(layerTypes, "WKT", "WKTGeometryEncoder", "EditableLayer", "rtree", "geometry");
- assertLayerType(layerTypes, "WKB", "WKBGeometryEncoder", "EditableLayer", "rtree", "geometry");
- assertLayerType(layerTypes, "Geohash", "SimplePointEncoder", "SimplePointLayer", "geohash",
- "longitude:latitude");
- assertLayerType(layerTypes, "ZOrder", "SimplePointEncoder", "SimplePointLayer", "zorder",
- "longitude:latitude");
- assertLayerType(layerTypes, "Hilbert", "SimplePointEncoder", "SimplePointLayer", "hilbert",
- "longitude:latitude");
- assertLayerType(layerTypes, "NativeGeohash", "NativePointEncoder", "SimplePointLayer", "geohash",
- "location");
- assertLayerType(layerTypes, "NativeZOrder", "NativePointEncoder", "SimplePointLayer", "zorder",
- "location");
- assertLayerType(layerTypes, "NativeHilbert", "NativePointEncoder", "SimplePointLayer", "hilbert",
- "location");
- assertLayerType(layerTypes, "OSM", "OSMGeometryEncoder", "OSMLayer", "rtree", "geometry");
- });
+ testResult(db, "CALL spatial.layerTypes()", (res) -> {
+ // Convert the result list to a Map keyed by 'id' for an easy lookup
+ Map> layerTypes = res.stream()
+ .collect(Collectors.toMap(r -> r.get("id").toString(), r -> r));
+ Assertions.assertThat(layerTypes).hasSize(12);
+
+ assertLayerType(layerTypes, "SimplePoint", "SimplePointEncoder", "SimplePointLayer", "rtree",
+ "longitude:latitude");
+ assertLayerType(layerTypes, "NativePoint", "NativePointEncoder", "SimplePointLayer", "rtree",
+ "location");
+ assertLayerType(layerTypes, "NativePoints", "NativePointsEncoder", "EditableLayer", "rtree",
+ "geometry");
+ assertLayerType(layerTypes, "WKT", "WKTGeometryEncoder", "EditableLayer", "rtree", "geometry");
+ assertLayerType(layerTypes, "WKB", "WKBGeometryEncoder", "EditableLayer", "rtree", "geometry");
+ assertLayerType(layerTypes, "Geohash", "SimplePointEncoder", "SimplePointLayer", "geohash",
+ "longitude:latitude");
+ assertLayerType(layerTypes, "ZOrder", "SimplePointEncoder", "SimplePointLayer", "zorder",
+ "longitude:latitude");
+ assertLayerType(layerTypes, "Hilbert", "SimplePointEncoder", "SimplePointLayer", "hilbert",
+ "longitude:latitude");
+ assertLayerType(layerTypes, "NativeGeohash", "NativePointEncoder", "SimplePointLayer", "geohash",
+ "location");
+ assertLayerType(layerTypes, "NativeZOrder", "NativePointEncoder", "SimplePointLayer", "zorder",
+ "location");
+ assertLayerType(layerTypes, "NativeHilbert", "NativePointEncoder", "SimplePointLayer", "hilbert",
+ "location");
+ assertLayerType(layerTypes, "OSM", "OSMGeometryEncoder", "OSMLayer", "rtree", "geometry");
+ });
}
private void assertLayerType(Map> layerTypes, String id, String encoder, String layer,
@@ -724,11 +640,12 @@ private void assertLayerType(Map> layerTypes, String
@Test
public void find_layer() {
- Example example = docExample("spatial.layer", "Find an existing layer");
- Example example1 = example.runCypher("CALL spatial.addWKTLayer('geom','wkt')",
- config -> config.setComment("Create a WKT layer"));
- example1.runCypher("CALL spatial.layer('geom')", ExampleCypher::storeResult)
- .assertSingleResult("node", r -> assertEquals("geom", ((Node) r).getProperty(PROP_LAYER)));
+ String wkt = "LINESTRING (15.2 60.1, 15.3 60.1)";
+ execute("CALL spatial.addWKTLayer('geom','wkt')");
+ execute("CALL spatial.addWKT('geom',$wkt)", Map.of("wkt", wkt));
+
+ testCall(db, "CALL spatial.layer('geom')",
+ (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER)));
testCallFails(db, "CALL spatial.layer('badname')", null, "No such layer 'badname'");
}
@@ -779,17 +696,11 @@ public void testPointLayer(String encoder, String indexType) {
procName += indexType;
}
String layerName = ("my-" + encoder + "-" + indexType + "-layer").toLowerCase();
+ execute("CALL " + procName + "('" + layerName + "')");
+ execute("CREATE (n:Node {id: 42, latitude:60.1,longitude:15.2}) SET n.location=point(n) RETURN n");
- Example example = docExample(procName, "Create a layer to index a node");
- example.runCypher("CALL " + procName + "('" + layerName + "')", ExampleCypher::storeResult)
- .runCypher("CREATE (n:Node {id: 42, latitude:60.1,longitude:15.2}) SET n.location=point(n) RETURN n",
- config -> config.setComment("Create a node to index"))
- .runCypher("MATCH (n:Node) WITH n CALL spatial.addNode('" + layerName + "',n) YIELD node RETURN node",
- config -> config.storeResult().setComment("Index node").storeResult())
- .assertSingleResult("node", o -> assertEquals(42L, ((Node) o).getProperty("id")))
- .runCypher("CALL spatial.withinDistance('" + layerName + "',{lon:15.0,lat:60.0},100)",
- config -> config.storeResult().setComment("Find node within distance"))
- .assertSingleResult("node", o -> assertEquals(42L, ((Node) o).getProperty("id")));
+ testCall(db, "MATCH (n:Node) WITH n CALL spatial.addNode('" + layerName + "',n) YIELD node RETURN node",
+ (result) -> assertEquals(42L, ((Node) result.get("node")).getProperty("id")));
testResult(db, "CALL spatial.layers()", (res) -> {
while (res.hasNext()) {
@@ -873,42 +784,50 @@ public void add_a_node_to_the_spatial_index_short_with_geohash() {
@Test
public void add_two_nodes_to_the_spatial_layer() {
- docExample("spatial.addPointLayerXY", "Create a point layer with X and Y properties")
- .additionalSignature("spatial.addNodes")
- .additionalSignature("spatial.removeNode")
- .additionalSignature("spatial.removeNode.byId")
- .runCypher("CALL spatial.addPointLayerXY('geom','lon','lat')")
- .runCypher(
- "CREATE (n1:Node {id: 1, lat:60.1,lon:15.2}),(n2:Node {id: 2, lat:60.1,lon:15.3}) WITH n1,n2 CALL spatial.addNodes('geom',[n1,n2]) YIELD count RETURN n1,n2,count",
- config -> config.storeResult().setComment("Add two nodes to the layer"))
- .assertSingleResult("n1", o -> assertEquals(60.1, ((Node) o).getProperty("lat")))
- .assertSingleResult("n2", o -> assertEquals(60.1, ((Node) o).getProperty("lat")))
- .assertSingleResult("count", o -> assertEquals(2L, o))
- .runCypher("CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)",
- config -> config.storeResult().setComment("Find nodes within distance"))
- .assertResult(res -> {
- assertEquals(2, res.size());
- assertEquals(1L, ((Node) res.get(0).get("node")).getProperty("id"));
- assertEquals(2L, ((Node) res.get(1).get("node")).getProperty("id"));
- })
- .runCypher("""
- MATCH (node) WHERE node.id = 1
- CALL spatial.removeNode('geom', node) YIELD nodeId
- RETURN nodeId
- """,
- config -> config.setComment("Remove node 1"))
- .runCypher("CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)",
- ExampleCypher::storeResult)
- .assertSingleResult("node", o -> assertEquals(2L, ((Node) o).getProperty("id")))
- .runCypher("""
- MATCH (node) WHERE node.id = 2
- CALL spatial.removeNode.byId('geom', elementId(node)) YIELD nodeId
- RETURN nodeId
- """,
- config -> config.setComment("Remove node 2"))
- .runCypher("CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)",
- ExampleCypher::storeResult)
- .assertResult(res -> Assertions.assertThat(res).isEmpty());
+ execute("CALL spatial.addPointLayerXY('geom','lon','lat')");
+ String node1;
+ String node2;
+ try (Transaction tx = db.beginTx()) {
+ Result result = tx.execute(
+ "CREATE (n1:Node {lat:60.1,lon:15.2}),(n2:Node {lat:60.1,lon:15.3}) WITH n1,n2 CALL spatial.addNodes('geom',[n1,n2]) YIELD count RETURN n1,n2,count");
+ Map row = result.next();
+ node1 = ((Node) row.get("n1")).getElementId();
+ node2 = ((Node) row.get("n2")).getElementId();
+ long count = (Long) row.get("count");
+ assertEquals(2L, count);
+ result.close();
+ tx.commit();
+ }
+ testResult(db, "CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)", res -> {
+ assertTrue(res.hasNext());
+ assertEquals(node1, ((Node) res.next().get("node")).getElementId());
+ assertTrue(res.hasNext());
+ assertEquals(node2, ((Node) res.next().get("node")).getElementId());
+ assertFalse(res.hasNext());
+ });
+ try (Transaction tx = db.beginTx()) {
+ Node node = (Node) tx.execute("MATCH (node) WHERE elementId(node) = $nodeId RETURN node",
+ Map.of("nodeId", node1)).next().get("node");
+ Result removeResult = tx.execute("CALL spatial.removeNode('geom',$node) YIELD nodeId RETURN nodeId",
+ Map.of("node", node));
+ assertEquals(node1, removeResult.next().get("nodeId"));
+ removeResult.close();
+ tx.commit();
+ }
+ testResult(db, "CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)", res -> {
+ assertTrue(res.hasNext());
+ assertEquals(node2, ((Node) res.next().get("node")).getElementId());
+ assertFalse(res.hasNext());
+ });
+ try (Transaction tx = db.beginTx()) {
+ Result removeResult = tx.execute("CALL spatial.removeNode.byId('geom',$nodeId) YIELD nodeId RETURN nodeId",
+ Map.of("nodeId", node2));
+ assertEquals(node2, removeResult.next().get("nodeId"));
+ removeResult.close();
+ tx.commit();
+ }
+ testResult(db, "CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)",
+ res -> assertFalse(res.hasNext()));
}
@Test
@@ -916,11 +835,12 @@ public void add_many_nodes_to_the_simple_point_layer_using_addNodes() {
// Playing with this number in both tests leads to rough benchmarking of the addNode/addNodes comparison
int count = 1000;
execute("CALL spatial.addLayer('simple_poi','SimplePoint','')");
- String query = "UNWIND range(1,$count) as i\n" +
- "CREATE (n:Point {id:i, latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)})\n" +
- "WITH collect(n) as points\n" +
- "CALL spatial.addNodes('simple_poi',points) YIELD count\n" +
- "RETURN count";
+ String query = """
+ UNWIND range(1,$count) as i
+ CREATE (n:Point {id:i, latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)})
+ WITH collect(n) as points
+ CALL spatial.addNodes('simple_poi',points) YIELD count
+ RETURN count""";
testCountQuery("addNodes", query, count, "count", Map.of("count", count));
testRemoveNodes("simple_poi", count);
}
@@ -930,11 +850,12 @@ public void add_many_nodes_to_the_simple_point_layer_using_addNode() {
// Playing with this number in both tests leads to rough benchmarking of the addNode/addNodes comparison
int count = 1000;
execute("CALL spatial.addLayer('simple_poi','SimplePoint','')");
- String query = "UNWIND range(1,$count) as i\n" +
- "CREATE (n:Point {id:i, latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)})\n" +
- "WITH n\n" +
- "CALL spatial.addNode('simple_poi',n) YIELD node\n" +
- "RETURN count(node)";
+ String query = """
+ UNWIND range(1,$count) as i
+ CREATE (n:Point {id:i, latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)})
+ WITH n
+ CALL spatial.addNode('simple_poi',n) YIELD node
+ RETURN count(node)""";
testCountQuery("addNode", query, count, "count(node)", Map.of("count", count));
testRemoveNode("simple_poi", count);
}
@@ -944,83 +865,29 @@ public void add_many_nodes_to_the_native_point_layer_using_addNodes() {
// Playing with this number in both tests leads to rough benchmarking of the addNode/addNodes comparison
int count = 1000;
execute("CALL spatial.addLayer('native_poi','NativePoint','')");
- String query = "UNWIND range(1,$count) as i\n" +
- "WITH i, Point({latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)}) AS location\n" +
- "CREATE (n:Point {id: i, location:location})\n" +
- "WITH collect(n) as points\n" +
- "CALL spatial.addNodes('native_poi',points) YIELD count\n" +
- "RETURN count";
+ String query = """
+ UNWIND range(1,$count) as i
+ WITH i, Point({latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)}) AS location
+ CREATE (n:Point {id: i, location:location})
+ WITH collect(n) as points
+ CALL spatial.addNodes('native_poi',points) YIELD count
+ RETURN count""";
testCountQuery("addNodes", query, count, "count", Map.of("count", count));
testRemoveNodes("native_poi", count);
}
- @Test
- public void add_node_to_multiple_indexes_in_chunks() {
- Example example = docExample("spatial.addLayer", "Add the same node to multiple layers");
- Example example1 = example.runCypher("""
- UNWIND range(1,$count) as i
- CREATE (n:Point {
- id: i,
- point1: point( { latitude: 56.0, longitude: 12.0 } ),
- point2: point( { latitude: 57.0, longitude: 13.0 } )
- })""", config -> config.setParams(Map.of("count", 100)).setTitle("Create some nodes"));
- Example example2 = example1.runCypher("""
- CALL spatial.addLayer(
- 'point1',
- 'NativePoint',
- 'point1:point1BB',
- '{"referenceRelationshipType": "RTREE_P1_TYPE"}'
- )
- """, config1 -> config1.setComment("""
- Create a layer `point1` to index property `point1` of node `Point`.
- Save the bounding box in the property `point1BB` of the `Point` node.
- Associate the node with the index layer via relationship type `RTREE_P1_TYPE`.
- """));
- Example example3 = example2.runCypher("""
- CALL spatial.addLayer(
- 'point2',
- 'NativePoint',
- 'point2:point2BB',
- '{"referenceRelationshipType": "RTREE_P2_TYPE"}'
- )
- """, config -> config.setComment("""
- Create a layer `point2` to index property `point2` of node `Point`.
- Save the bounding box in the property `point2BB` of the `Point` node.
- Associate the node with the index layer via relationship type `RTREE_P2_TYPE`.
- """));
- Example example4 = example3.runCypher("""
- MATCH (p:Point)
- WITH (count(p) / 10) AS pages, collect(p) AS nodes
- UNWIND range(0, pages) AS i CALL {
- WITH i, nodes
- CALL spatial.addNodes('point1', nodes[(i * 10)..((i + 1) * 10)]) YIELD count
- RETURN count AS count
- } IN TRANSACTIONS OF 1 ROWS
- RETURN sum(count) AS count
- """, config1 -> config1.storeResult().setComment("Index the nodes in layer `point1` in chunks of 10"));
- example4.runCypher("""
- MATCH (p:Point)
- WITH (count(p) / 10) AS pages, collect(p) AS nodes
- UNWIND range(0, pages) AS i CALL {
- WITH i, nodes
- CALL spatial.addNodes('point2', nodes[(i * 10)..((i + 1) * 10)]) YIELD count
- RETURN count AS count
- } IN TRANSACTIONS OF 1 ROWS
- RETURN sum(count) AS count
- """, config -> config.storeResult().setComment("Index the nodes in layer `point2` in chunks of 10"));
- }
-
@Test
public void add_many_nodes_to_the_native_point_layer_using_addNode() {
// Playing with this number in both tests leads to rough benchmarking of the addNode/addNodes comparison
int count = 1000;
execute("CALL spatial.addLayer('native_poi','NativePoint','')");
- String query = "UNWIND range(1,$count) as i\n" +
- "WITH i, Point({latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)}) AS location\n" +
- "CREATE (n:Point {id: i, location:location})\n" +
- "WITH n\n" +
- "CALL spatial.addNode('native_poi',n) YIELD node\n" +
- "RETURN count(node)";
+ String query = """
+ UNWIND range(1,$count) as i
+ WITH i, Point({latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)}) AS location
+ CREATE (n:Point {id: i, location:location})
+ WITH n
+ CALL spatial.addNode('native_poi',n) YIELD node
+ RETURN count(node)""";
testCountQuery("addNode", query, count, "count(node)", Map.of("count", count));
testRemoveNode("native_poi", count);
}
@@ -1063,36 +930,32 @@ private void testRemoveNodes(String layer, int count) {
@Test
public void import_shapefile() {
- docExample("spatial.importShapefile", "Import a shape-file")
- .runCypher("CALL spatial.importShapefile('shp/highway.shp')", ExampleCypher::storeResult)
- .assertSingleResult("count", count -> assertEquals(143L, count))
- .runCypher("CALL spatial.layers()", ExampleCypher::storeResult)
- .assertResult(r -> Assertions.assertThat(r).hasSize(1));
+ testCountQuery("importShapefile", "CALL spatial.importShapefile('../example-data/shp/highway.shp')", 143,
+ "count", null);
+ testCallCount(db, "CALL spatial.layers()", null, 1);
}
@Test
public void import_shapefile_without_extension() {
- testCountQuery("importShapefile", "CALL spatial.importShapefile('shp/highway')", 143, "count", null);
+ testCountQuery("importShapefile", "CALL spatial.importShapefile('../example-data/shp/highway')", 143, "count",
+ null);
testCallCount(db, "CALL spatial.layers()", null, 1);
}
@Test
public void import_shapefile_to_layer() {
- docExample("spatial.importShapefileToLayer", "Import a shape-file")
- .runCypher("CALL spatial.addWKTLayer('geom','wkt')")
- .runCypher("CALL spatial.importShapefileToLayer('geom', 'shp/highway.shp')", ExampleCypher::storeResult)
- .assertSingleResult("count", count -> assertEquals(143L, count))
- .runCypher("CALL spatial.layers()", ExampleCypher::storeResult)
- .assertResult(r -> Assertions.assertThat(r).hasSize(1));
+ execute("CALL spatial.addWKTLayer('geom','wkt')");
+ testCountQuery("importShapefileToLayer",
+ "CALL spatial.importShapefileToLayer('geom','../example-data/shp/highway.shp')", 143,
+ "count", null);
+ testCallCount(db, "CALL spatial.layers()", null, 1);
}
+
@Test
public void import_osm() {
- docExample("spatial.importOSM", "Import an OSM file")
- .runCypher("CALL spatial.importOSM('map.osm')", ExampleCypher::storeResult)
- .assertSingleResult("count", count -> assertEquals(55L, count))
- .runCypher("CALL spatial.layers()", ExampleCypher::storeResult)
- .assertResult(r -> Assertions.assertThat(r).hasSize(1));
+ testCountQuery("importOSM", "CALL spatial.importOSM('map.osm')", 55, "count", null);
+ testCallCount(db, "CALL spatial.layers()", null, 1);
}
@Test
@@ -1111,12 +974,9 @@ public void import_osm_without_extension() {
@Test
public void import_osm_to_layer() {
- docExample("spatial.importOSMToLayer", "Import an OSM file")
- .runCypher("CALL spatial.addLayer('geom','OSM','')")
- .runCypher("CALL spatial.importOSMToLayer('geom','map.osm')", ExampleCypher::storeResult)
- .assertSingleResult("count", count -> assertEquals(55L, count))
- .runCypher("CALL spatial.layers()", ExampleCypher::storeResult)
- .assertResult(r -> Assertions.assertThat(r).hasSize(1));
+ execute("CALL spatial.addLayer('geom','OSM','')");
+ testCountQuery("importOSMToLayer", "CALL spatial.importOSMToLayer('geom','map.osm')", 55, "count", null);
+ testCallCount(db, "CALL spatial.layers()", null, 1);
}
@Test
@@ -1138,15 +998,6 @@ public void import_osm_twice_should_pass_with_different_layers() {
217);
}
- @Disabled
- @Test
- public void import_cracow_to_layer() {
- execute("CALL spatial.addLayer('geom','OSM','')");
- testCountQuery("importCracowToLayer", "CALL spatial.importOSMToLayer('geom','issue-347/cra.osm')", 256253,
- "count", null);
- testCallCount(db, "CALL spatial.layers()", null, 1);
- }
-
@Test
public void import_osm_to_layer_without_changesets() {
execute("CALL spatial.addLayer('osm_example','OSM','')");
@@ -1195,7 +1046,7 @@ public void import_osm_and_polygons_withinDistance() {
Node node = (Node) r.get("node");
double distance = (Double) r.get("distance");
Node osmNode = (Node) r.get("osmNode");
- @SuppressWarnings("rawtypes") Map props = (Map) r.get("props");
+ @SuppressWarnings({"rawtypes", "unchecked"}) Map props = (Map) r.get("props");
LOGGER.fine(
"(node[" + node.getElementId() + "])<-[:GEOM {distance:" + distance + "}]-(osmNode["
+ osmNode.getElementId() + "] " + props + ") ");
@@ -1219,7 +1070,7 @@ public void import_osm_and_polygons_withinDistance() {
assertThat("Point has 2D coordinates",
((Point) geometry).getCoordinate().getCoordinate().length, equalTo(2));
} else if (geometry instanceof Map) {
- Map map = (Map) geometry;
+ @SuppressWarnings("unchecked") Map map = (Map) geometry;
assertThat("Geometry should contain a type", map, hasKey("type"));
assertThat("Geometry should contain coordinates", map, hasKey("coordinates"));
assertThat("Geometry should not be a point", map.get("type"), not(equalTo("Point")));
@@ -1230,23 +1081,6 @@ public void import_osm_and_polygons_withinDistance() {
});
}
- private void testCountQuery(String name, String query, long count, String column, Map params) {
- // warmup
- try (Transaction tx = db.beginTx()) {
- Result results = tx.execute("EXPLAIN " + query, params == null ? Map.of() : params);
- results.close();
- tx.commit();
- }
- long start = System.currentTimeMillis();
- testResult(db, query, params, res -> {
- assertTrue(res.hasNext(), "Expected a single result");
- long c = (Long) res.next().get(column);
- assertFalse(res.hasNext(), "Expected a single result");
- assertEquals(count, c, "Expected count of " + count + " nodes but got " + c);
- });
- LOGGER.fine(name + " query took " + (System.currentTimeMillis() - start) + "ms - " + params);
- }
-
@Test
public void find_geometries_in_a_bounding_box_short() {
execute("CALL spatial.addPointLayerXY('geom','lon','lat')");
@@ -1259,34 +1093,22 @@ public void find_geometries_in_a_bounding_box_short() {
@Test
public void find_geometries_in_a_bounding_box() {
- docExample("spatial.bbox", "Find geometries in a bounding box")
- .runCypher("CALL spatial.addPointLayer('geom')")
- .runCypher("""
- CREATE (n:Node {id: 1, latitude:60.1,longitude:15.2})
- WITH n CALL spatial.addNode('geom',n) YIELD node
- RETURN node
- """)
- .runCypher("CALL spatial.bbox('geom',{lon:15.0,lat:60.0}, {lon:15.3, lat:61.0})",
- c -> c.storeResult().setComment("Find node within bounding box"))
- .assertSingleResult("node", o -> assertEquals(1L, ((Node) o).getProperty("id")));
+ execute("CALL spatial.addPointLayer('geom')");
+ Node node = createNode(
+ "CREATE (n:Node {latitude:60.1,longitude:15.2}) WITH n CALL spatial.addNode('geom',n) YIELD node RETURN node",
+ "node");
+ testCall(db, "CALL spatial.bbox('geom',{lon:15.0,lat:60.0}, {lon:15.3, lat:61.0})",
+ r -> assertEquals(node, r.get("node")));
}
@Test
public void find_geometries_in_a_polygon() {
+ execute("CALL spatial.addPointLayer('geom')");
+ executeWrite(
+ "UNWIND [{name:'a',latitude:60.1,longitude:15.2},{name:'b',latitude:60.3,longitude:15.5}] as point CREATE (n:Node) SET n += point WITH n CALL spatial.addNode('geom',n) YIELD node RETURN node.name as name");
String polygon = "POLYGON((15.3 60.2, 15.3 60.4, 15.7 60.4, 15.7 60.2, 15.3 60.2))";
- docExample("spatial.intersects", "Find geometries in a polygon")
- .runCypher("CALL spatial.addPointLayer('geom')")
- .runCypher("""
- UNWIND [ {name:'a',latitude:60.1,longitude:15.2}, {name:'b',latitude:60.3,longitude:15.5} ] as point
- CREATE (n:Node)
- SET n += point
- WITH n
- CALL spatial.addNode('geom',n) YIELD node
- RETURN node.name as name
- """, ExampleCypher::storeResult)
- .runCypher("CALL spatial.intersects('geom','" + polygon + "') YIELD node\n RETURN node.name as name",
- ExampleCypher::storeResult)
- .assertSingleResult("name", name -> assertEquals("b", name));
+ testCall(db, "CALL spatial.intersects('geom','" + polygon + "') YIELD node RETURN node.name as name",
+ r -> assertEquals("b", r.get("name")));
}
@Test
@@ -1352,12 +1174,7 @@ public void find_geometries_in_a_polygon_hilbert() {
@Test
public void create_a_WKT_layer() {
testCall(db, "CALL spatial.addWKTLayer('geom','wkt')",
- r -> assertEquals("wkt", dump(((Node) r.get("node"))).getProperty(PROP_GEOMENCODER_CONFIG)));
- }
-
- private static Node dump(Node n) {
- LOGGER.fine("id " + n.getElementId() + " props " + n.getAllProperties());
- return n;
+ r -> assertEquals("wkt", ((Node) r.get("node")).getProperty(PROP_GEOMENCODER_CONFIG)));
}
@Test
@@ -1366,31 +1183,24 @@ public void add_a_WKT_geometry_to_a_layer() {
execute("CALL spatial.addWKTLayer('geom','wkt')");
testCall(db, "CALL spatial.addWKT('geom',$wkt)", Map.of("wkt", lineString),
- r -> assertEquals(lineString, dump(((Node) r.get("node"))).getProperty("wkt")));
+ r -> assertEquals(lineString, ((Node) r.get("node")).getProperty("wkt")));
}
@Test
public void find_geometries_close_to_a_point_wkt() {
String lineString = "LINESTRING (15.2 60.1, 15.3 60.1)";
- docExample("spatial.addWKT", "Add a WKT geometry to a layer")
- .additionalSignature("spatial.addWKTLayer")
- .runCypher("CALL spatial.addWKTLayer('geom', 'wkt')")
- .runCypher("CALL spatial.addWKT('geom',$wkt)",
- c -> c.setParams(Map.of("wkt", lineString)).storeResult())
- .runCypher("CALL spatial.closest('geom',{lon:15.2, lat:60.1}, 1.0)", ExampleCypher::storeResult)
- .assertSingleResult("node", node -> assertEquals(lineString, (((Node) node)).getProperty("wkt")));
+ execute("CALL spatial.addLayer('geom','WKT','wkt')");
+ execute("CALL spatial.addWKT('geom',$wkt)", Map.of("wkt", lineString));
+ testCall(db, "CALL spatial.closest('geom',{lon:15.2, lat:60.1}, 1.0)",
+ r -> assertEquals(lineString, ((Node) r.get("node")).getProperty("wkt")));
}
@Test
public void find_geometries_close_to_a_point_geohash() {
- var points = List.of("POINT (15.2 60.1)", "POINT (25.2 30.1)");
- docExample("spatial.addWKTs", "Add multiple WKT geometries to a layer")
- .additionalSignature("spatial.closest")
- .runCypher("CALL spatial.addLayer('geom','geohash','lon:lat')")
- .runCypher("CALL spatial.addWKTs('geom',$wkt)",
- c -> c.setParams(Map.of("wkt", points)).storeResult())
- .runCypher("CALL spatial.closest('geom',{lon:15.0, lat:60.0}, 1.0)", ExampleCypher::storeResult)
- .assertResult(list -> Assertions.assertThat(list).hasSize(1));
+ String lineString = "POINT (15.2 60.1)";
+ execute("CALL spatial.addLayer('geom','geohash','lon:lat')");
+ execute("CALL spatial.addWKT('geom',$wkt)", Map.of("wkt", lineString));
+ testCallCount(db, "CALL spatial.closest('geom',{lon:15.2, lat:60.1}, 1.0)", null, 1);
}
@Test
@@ -1428,12 +1238,11 @@ public void testNativePoints() {
@Test
public void testNativePoints3D() {
execute("CREATE (node:Foo { points: [point({latitude: 5.0, longitude: 4.0, height: 1.0}), point({latitude: 6.0, longitude: 5.0, height: 2.0})]})");
- Exception exception = assertThrows(QueryExecutionException.class, () -> {
- execute("CALL spatial.addLayer('line','NativePoints','points:bbox:Cartesian') YIELD node" +
- " MATCH (n:Foo)" +
- " WITH collect(n) AS nodes" +
- " CALL spatial.addNodes('line', nodes) YIELD count RETURN count");
- });
+ Exception exception = assertThrows(QueryExecutionException.class,
+ () -> execute("CALL spatial.addLayer('line','NativePoints','points:bbox:Cartesian') YIELD node" +
+ " MATCH (n:Foo)" +
+ " WITH collect(n) AS nodes" +
+ " CALL spatial.addNodes('line', nodes) YIELD count RETURN count"));
assertEquals(
"Failed to invoke procedure `spatial.addNodes`: Caused by: java.lang.IllegalStateException: Trying to decode geometry with wrong CRS: layer configured to crs=7203, but geometry has crs=4979",
@@ -1453,12 +1262,11 @@ public void testNativePointsCartesian() {
@Test
public void testNativePointsCartesian3D() {
execute("CREATE (node:Foo { points: [point({x: 5.0, y: 4.0, z: 1}), point({x: 6.0, y: 5.0, z: 2})]})");
- Exception exception = assertThrows(QueryExecutionException.class, () -> {
- execute("CALL spatial.addLayer('line','NativePoints','points:bbox:Cartesian') YIELD node" +
- " MATCH (n:Foo)" +
- " WITH collect(n) AS nodes" +
- " CALL spatial.addNodes('line', nodes) YIELD count RETURN count");
- });
+ Exception exception = assertThrows(QueryExecutionException.class,
+ () -> execute("CALL spatial.addLayer('line','NativePoints','points:bbox:Cartesian') YIELD node" +
+ " MATCH (n:Foo)" +
+ " WITH collect(n) AS nodes" +
+ " CALL spatial.addNodes('line', nodes) YIELD count RETURN count"));
assertEquals(
"Failed to invoke procedure `spatial.addNodes`: Caused by: java.lang.IllegalStateException: Trying to decode geometry with wrong CRS: layer configured to crs=7203, but geometry has crs=9157",
@@ -1467,9 +1275,8 @@ public void testNativePointsCartesian3D() {
@Test
public void testCQLQuery() {
- docExample("spatial.cql", "Find geometries using CQL")
- .runCypher("CALL spatial.addWKTLayer('geom','wkt') YIELD node", ExampleCypher::storeResult)
- .runCypher("""
+ execute("CALL spatial.addWKTLayer('geom','wkt') YIELD node");
+ execute("""
CREATE (n1:Node {wkt: 'POINT(15.2 60.1)', name: 'point1'})
CREATE (n2:Node {wkt: 'POINT(25.2 30.1)', name: 'point2'})
WITH n1, n2
@@ -1477,20 +1284,18 @@ public void testCQLQuery() {
WITH n2, added1
CALL spatial.addNode('geom', n2) YIELD node as added2
RETURN added1, added2
- """, config -> config.setComment("Create and add nodes with different coordinates"))
- .runCypher("CALL spatial.cql('geom', 'name = \\'point1\\'') YIELD node RETURN node.name as name",
- ExampleCypher::storeResult)
- .assertSingleResult("name", name -> assertEquals("point1", name));
+ """);
+ Object name = executeObject(
+ "CALL spatial.cql('geom', 'name = \\'point1\\'') YIELD node RETURN node.name as name", "name");
+ assertEquals("point1", name);
}
@Test
public void testGetFeatureCount() {
- docExample("spatial.getFeatureCount", "Get the number of features in a layer")
- .runCypher("CALL spatial.addPointLayer('count_layer') YIELD node", ExampleCypher::storeResult)
- .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count",
- config -> config.storeResult().setComment("Get count of empty layer"))
- .assertSingleResult("count", count -> assertEquals(0L, count))
- .runCypher("""
+ execute("CALL spatial.addPointLayer('count_layer') YIELD node");
+ var count = executeObject("CALL spatial.getFeatureCount('count_layer') YIELD count", "count");
+ assertEquals(0L, count);
+ execute("""
CREATE (n1:Point {latitude: 60.1, longitude: 15.2, name: 'point1'})
CREATE (n2:Point {latitude: 60.3, longitude: 15.5, name: 'point2'})
WITH n1, n2
@@ -1498,18 +1303,15 @@ public void testGetFeatureCount() {
WITH n2, added1
CALL spatial.addNode('count_layer', n2) YIELD node as added2
RETURN added1, added2
- """, config -> config.setComment("Add two points to the layer"))
- .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count",
- config -> config.storeResult().setComment("Get count after adding points"))
- .assertSingleResult("count", count -> assertEquals(2L, count));
+ """);
+ var count2 = executeObject("CALL spatial.getFeatureCount('count_layer') YIELD count", "count");
+ assertEquals(2L, count2);
}
@Test
public void testGetLayerBoundingBox() {
- docExample("spatial.getLayerBoundingBox", "Get the bounding box of a layer")
- .runCypher("CALL spatial.addPointLayer('bbox_layer', 'rtree', 'wgs84') YIELD node",
- ExampleCypher::storeResult)
- .runCypher("""
+ execute("CALL spatial.addPointLayer('bbox_layer', 'rtree', 'wgs84') YIELD node");
+ execute("""
CREATE (n1:Point {latitude: 60.0, longitude: 15.0, name: 'southwest'})
CREATE (n2:Point {latitude: 61.0, longitude: 16.0, name: 'northeast'})
WITH n1, n2
@@ -1517,102 +1319,91 @@ public void testGetLayerBoundingBox() {
WITH n2, added1
CALL spatial.addNode('bbox_layer', n2) YIELD node as added2
RETURN added1, added2
- """, config -> config.setComment("Add points at opposite corners"))
- .runCypher("CALL spatial.getLayerBoundingBox('bbox_layer') YIELD minX, minY, maxX, maxY, crs",
- ExampleCypher::storeResult)
- .assertSingleResult("minX", minX -> assertThat((Double) minX, closeTo(15.0, 0.0001)))
- .assertSingleResult("minY", minY -> assertThat((Double) minY, closeTo(60.0, 0.0001)))
- .assertSingleResult("maxX", maxX -> assertThat((Double) maxX, closeTo(16.0, 0.0001)))
- .assertSingleResult("maxY", maxY -> assertThat((Double) maxY, closeTo(61.0, 0.0001)))
- .assertSingleResult("crs", crs -> assertEquals("WGS84(DD)", crs));
+ """);
+ testCall(db, "CALL spatial.getLayerBoundingBox('bbox_layer') YIELD minX, minY, maxX, maxY, crs", (result) -> {
+ assertThat((Double) result.get("minX"), closeTo(15.0, 0.0001));
+ assertThat((Double) result.get("minY"), closeTo(60.0, 0.0001));
+ assertThat((Double) result.get("maxX"), closeTo(16.0, 0.0001));
+ assertThat((Double) result.get("maxY"), closeTo(61.0, 0.0001));
+ assertEquals("WGS84(DD)", result.get("crs"));
+ });
}
@Test
public void testLayerMeta() {
- docExample("spatial.layerMeta", "Get metadata about a layer")
- .runCypher("CALL spatial.addPointLayer('meta_layer', 'rtree', 'wgs84') YIELD node",
- ExampleCypher::storeResult)
- .runCypher("""
+ execute("CALL spatial.addPointLayer('meta_layer', 'rtree', 'wgs84') YIELD node");
+ execute("""
CREATE (n1:Point {latitude: 60.0, longitude: 15.0, name: 'southwest'})
WITH n1
CALL spatial.addNode('meta_layer', n1) YIELD node
RETURN node
- """, config -> config.setComment("Add points at opposite corners"))
- .runCypher(
- "CALL spatial.layerMeta('meta_layer') YIELD name, geometryType, crs, hasComplexAttributes, extraAttributes",
- ExampleCypher::storeResult)
- .assertSingleResult("name", name -> assertEquals("meta_layer", name))
- .assertSingleResult("geometryType",
- geomType -> assertEquals("org.locationtech.jts.geom.Point", geomType))
- .assertSingleResult("crs", crs -> Assertions.assertThat((String) crs).contains("WGS84(DD)"))
- .assertSingleResult("hasComplexAttributes", hasComplex -> assertFalse((Boolean) hasComplex));
+ """);
+ try (Transaction tx = db.beginTx()) {
+ var result = tx.execute(
+ "CALL spatial.layerMeta('meta_layer') YIELD name, geometryType, crs, hasComplexAttributes, extraAttributes")
+ .next();
+ assertEquals("meta_layer", result.get("name"));
+ assertEquals("org.locationtech.jts.geom.Point", result.get("geometryType"));
+ Assertions.assertThat((String) result.get("crs")).contains("WGS84(DD)");
+ assertFalse((Boolean) result.get("hasComplexAttributes"));
+ }
}
@Test
public void testUpdateWKT() {
- docExample("spatial.updateWKT", "Update a node's WKT geometry")
- .runCypher("CALL spatial.addWKTLayer('update_layer', 'wkt') YIELD node", ExampleCypher::storeResult)
- .runCypher("""
+ execute("CALL spatial.addWKTLayer('update_layer', 'wkt') YIELD node");
+ execute("""
CREATE (n:Node {wkt: 'POINT(15.2 60.1)', name: 'updatable_point'})
WITH n
CALL spatial.addNode('update_layer', n) YIELD node as added_node
RETURN n, added_node
- """, config -> config.setComment("Create and add a node with initial WKT"))
- .runCypher("""
+ """);
+ Object wkt = executeObject("""
MATCH (n:Node {name: 'updatable_point'})
CALL spatial.updateWKT('update_layer', n, 'POINT(25.5 65.5)') YIELD node
RETURN node.wkt as wkt
- """,
- config -> config.storeResult().setComment("Update the node's WKT geometry"))
- .assertSingleResult("wkt", wkt -> assertEquals("POINT (25.5 65.5)", wkt))
- .runCypher("""
+ """, "wkt");
+ assertEquals("POINT (25.5 65.5)", wkt);
+ Object name = executeObject("""
CALL spatial.withinDistance('update_layer', {longitude: 25.5, latitude: 65.5}, 1) YIELD node
RETURN node.name as name
- """,
- config -> config.storeResult().setComment("Verify the updated geometry is indexed correctly"))
- .assertSingleResult("name", name -> assertEquals("updatable_point", name));
+ """, "name");
+ assertEquals("updatable_point", name);
}
@Test
public void test_spatial_getFeatureCount() {
- docExample("spatial.getFeatureCount", "Count features in different layer types")
- .runCypher("CALL spatial.addPointLayer('count_layer')",
- config -> config.setComment("Create a point layer"))
- .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count",
- config -> config.storeResult().setComment("Count features in empty layer"))
- .assertSingleResult("count", count -> assertEquals(0L, count))
- .runCypher("""
+ execute("CALL spatial.addPointLayer('count_layer')");
+ Object count = executeObject("CALL spatial.getFeatureCount('count_layer') YIELD count", "count");
+ assertEquals(0L, count);
+
+ execute("""
CREATE (n:Node {latitude: 60.1, longitude: 15.2, name: 'first'})
WITH n
CALL spatial.addNode('count_layer', n) YIELD node
RETURN node
- """, config -> config.setComment("Add one node to the layer"))
- .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count",
- config -> config.storeResult().setComment("Count after adding one feature"))
- .assertSingleResult("count", count -> assertEquals(1L, count))
- .runCypher("""
+ """);
+ Object count2 = executeObject("CALL spatial.getFeatureCount('count_layer') YIELD count", "count");
+ assertEquals(1L, count2);
+
+ execute("""
UNWIND range(1,3) as i
CREATE (n:Node {id: i, latitude: (60.0 + i * 0.1), longitude: (15.0 + i * 0.1)})
WITH collect(n) as nodes
CALL spatial.addNodes('count_layer', nodes) YIELD count
RETURN count
- """, config -> config.setComment("Add multiple nodes at once"))
- .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count",
- config -> config.storeResult().setComment("Count after adding multiple features"))
- .assertSingleResult("count", count -> assertEquals(4L, count))
- .runCypher("CALL spatial.addWKTLayer('wkt_layer', 'wkt')",
- config -> config.setComment("Create a WKT layer"))
- .runCypher("CALL spatial.getFeatureCount('wkt_layer') YIELD count",
- config -> config.storeResult().setComment("Count features in empty WKT layer"))
- .assertSingleResult("count", count -> assertEquals(0L, count))
- .runCypher("CALL spatial.addWKT('wkt_layer', 'POINT(15.2 60.1)') YIELD node RETURN node",
- config -> config.setComment("Add a WKT point"))
- .runCypher(
- "CALL spatial.addWKT('wkt_layer', 'LINESTRING (15.2 60.1, 15.3 60.1)') YIELD node RETURN node",
- config -> config.setComment("Add a WKT linestring"))
- .runCypher("CALL spatial.getFeatureCount('wkt_layer') YIELD count",
- config -> config.storeResult().setComment("Count features in WKT layer"))
- .assertSingleResult("count", count -> assertEquals(2L, count));
+ """);
+ Object count3 = executeObject("CALL spatial.getFeatureCount('count_layer') YIELD count", "count");
+ assertEquals(4L, count3);
+
+ execute("CALL spatial.addWKTLayer('wkt_layer', 'wkt')");
+ Object count4 = executeObject("CALL spatial.getFeatureCount('wkt_layer') YIELD count", "count");
+ assertEquals(0L, count4);
+
+ execute("CALL spatial.addWKT('wkt_layer', 'POINT(15.2 60.1)') YIELD node RETURN node");
+ execute("CALL spatial.addWKT('wkt_layer', 'LINESTRING (15.2 60.1, 15.3 60.1)') YIELD node RETURN node");
+ Object count5 = executeObject("CALL spatial.getFeatureCount('wkt_layer') YIELD count", "count");
+ assertEquals(2L, count5);
// Test error for non-existent layer
testCallFails(db, "CALL spatial.getFeatureCount('non_existent_layer')", null,
diff --git a/server-plugin/src/test/java/org/neo4j/spatial/geotools/plugin/Neo4jSpatialDataStoreTest.java b/server-plugin/src/test/java/org/neo4j/spatial/geotools/plugin/Neo4jSpatialDataStoreTest.java
index 6aa93f51a..009fced27 100644
--- a/server-plugin/src/test/java/org/neo4j/spatial/geotools/plugin/Neo4jSpatialDataStoreTest.java
+++ b/server-plugin/src/test/java/org/neo4j/spatial/geotools/plugin/Neo4jSpatialDataStoreTest.java
@@ -28,6 +28,7 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import org.assertj.core.api.Assertions;
@@ -46,15 +47,21 @@
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.exceptions.ClientException;
import org.neo4j.gis.spatial.LogListener;
-import org.neo4j.gis.spatial.Neo4jTestCase;
+import org.neo4j.gis.spatial.functions.SpatialFunctions;
import org.neo4j.gis.spatial.osm.OSMImporter;
+import org.neo4j.gis.spatial.procedures.SpatialProcedures;
import org.neo4j.harness.Neo4jBuilder;
import org.neo4j.harness.Neo4jBuilders;
+import org.neo4j.spatial.testutils.Neo4jTestCase;
public class Neo4jSpatialDataStoreTest extends Neo4jTestCase {
private static final Logger LOGGER = Logger.getLogger(Neo4jSpatialDataStoreTest.class.getName());
+ @Override
+ protected List> loadProceduresAndFunctions() {
+ return List.of(SpatialFunctions.class, SpatialProcedures.class);
+ }
@BeforeEach
public void setup() throws Exception {
diff --git a/test-utils/pom.xml b/test-utils/pom.xml
new file mode 100644
index 000000000..64d873223
--- /dev/null
+++ b/test-utils/pom.xml
@@ -0,0 +1,61 @@
+
+
+
+
+ 4.0.0
+
+ org.neo4j
+ neo4j-spatial
+ 2025.10.1-SNAPSHOT
+
+
+ neo4j-spatial-test-utils
+
+
+
+ org.neo4j
+ neo4j-spatial-api
+ 2025.10.1-SNAPSHOT
+
+
+ org.neo4j.test
+ neo4j-harness
+ ${neo4j.version}
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.13.4
+
+
+ org.geotools
+ gt-main
+ ${geotools.version}
+
+
+ org.neo4j.community
+ it-test-support
+ ${neo4j.version}
+
+
+
diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/AbstractApiTest.java b/test-utils/src/main/java/org/neo4j/spatial/testutils/AbstractApiTest.java
similarity index 76%
rename from server-plugin/src/test/java/org/neo4j/gis/spatial/AbstractApiTest.java
rename to test-utils/src/main/java/org/neo4j/spatial/testutils/AbstractApiTest.java
index 3ec23a2cf..ec3006de8 100644
--- a/server-plugin/src/test/java/org/neo4j/gis/spatial/AbstractApiTest.java
+++ b/test-utils/src/main/java/org/neo4j/spatial/testutils/AbstractApiTest.java
@@ -18,8 +18,11 @@
* along with this program. If not, see .
*/
-package org.neo4j.gis.spatial;
+package org.neo4j.spatial.testutils;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME;
import java.io.File;
@@ -28,18 +31,16 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import javax.annotation.Nonnull;
-import org.junit.jupiter.api.AfterAll;
+import java.util.function.Consumer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.neo4j.configuration.GraphDatabaseSettings;
import org.neo4j.dbms.api.DatabaseManagementService;
-import org.neo4j.doc.domain.examples.Example;
-import org.neo4j.doc.domain.examples.ExamplesRepository;
import org.neo4j.exceptions.KernelException;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.ResourceIterator;
+import org.neo4j.graphdb.Result;
import org.neo4j.graphdb.Transaction;
import org.neo4j.internal.helpers.collection.Iterators;
import org.neo4j.io.fs.FileUtils;
@@ -51,7 +52,6 @@ public abstract class AbstractApiTest {
private DatabaseManagementService databases;
protected GraphDatabaseService db;
- static ExamplesRepository examples;
@BeforeEach
public void setUp() throws KernelException, IOException {
@@ -62,7 +62,6 @@ public void setUp() throws KernelException, IOException {
.impermanent()
.build();
db = databases.database(DEFAULT_DATABASE_NAME);
- examples = new ExamplesRepository(db);
registerApiProceduresAndFunctions();
}
@@ -124,14 +123,27 @@ protected Object executeObject(String call, Map params, String c
return obj;
}
-
- protected Example docExample(@Nonnull String signature, @Nonnull String title) {
- return examples.docExample(signature, title);
+ public static void testResult(GraphDatabaseService db, String call, Map params,
+ Consumer resultConsumer) {
+ try (Transaction tx = db.beginTx()) {
+ Map p = (params == null) ? Map.of() : params;
+ resultConsumer.accept(tx.execute(call, p));
+ tx.commit();
+ }
}
- @AfterAll
- public static void generateDocumentation() throws IOException {
- examples.write();
+ protected void testCountQuery(String name, String query, long count, String column, Map params) {
+ try (Transaction tx = db.beginTx()) {
+ Result results = tx.execute("EXPLAIN " + query, params == null ? Map.of() : params);
+ results.close();
+ tx.commit();
+ }
+ testResult(db, query, params, res -> {
+ assertTrue(res.hasNext(), "Expected a single result");
+ long c = (Long) res.next().get(column);
+ assertFalse(res.hasNext(), "Expected a single result");
+ assertEquals(count, c, "Expected count of " + count + " nodes but got " + c);
+ });
}
}
diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/Neo4jTestCase.java b/test-utils/src/main/java/org/neo4j/spatial/testutils/Neo4jTestCase.java
similarity index 82%
rename from server-plugin/src/test/java/org/neo4j/gis/spatial/Neo4jTestCase.java
rename to test-utils/src/main/java/org/neo4j/spatial/testutils/Neo4jTestCase.java
index 39c4039a6..910e2e9d3 100644
--- a/server-plugin/src/test/java/org/neo4j/gis/spatial/Neo4jTestCase.java
+++ b/test-utils/src/main/java/org/neo4j/spatial/testutils/Neo4jTestCase.java
@@ -17,38 +17,35 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.neo4j.gis.spatial;
-
-import static org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME;
+package org.neo4j.spatial.testutils;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.neo4j.configuration.GraphDatabaseInternalSettings;
import org.neo4j.configuration.GraphDatabaseSettings;
-import org.neo4j.dbms.api.DatabaseManagementService;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
-import org.neo4j.gis.spatial.functions.SpatialFunctions;
-import org.neo4j.gis.spatial.procedures.SpatialProcedures;
import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.config.Setting;
import org.neo4j.harness.Neo4j;
import org.neo4j.harness.Neo4jBuilder;
import org.neo4j.harness.Neo4jBuilders;
/**
- * Base class for the meta model tests.
+ * Base class for the metamodel tests.
*/
public abstract class Neo4jTestCase {
- static final Map, Object> NORMAL_CONFIG = new HashMap<>();
+ protected static final Map, Object> NORMAL_CONFIG = new HashMap<>();
static final List closeThreads = new ArrayList<>();
static {
@@ -61,7 +58,7 @@ public abstract class Neo4jTestCase {
NORMAL_CONFIG.put(GraphDatabaseInternalSettings.trace_cursors, true);
}
- static final Map, Object> LARGE_CONFIG = new HashMap<>();
+ protected static final Map, Object> LARGE_CONFIG = new HashMap<>();
static {
//LARGE_CONFIG.put( GraphDatabaseSettings.nodestore_mapped_memory_size.name(), "100M" );
@@ -72,11 +69,11 @@ public abstract class Neo4jTestCase {
LARGE_CONFIG.put(GraphDatabaseSettings.pagecache_memory, 100000000L);
}
- private DatabaseManagementService databases;
- private GraphDatabaseService graphDb;
private Neo4j neo4j;
protected Driver driver;
+ protected abstract List> loadProceduresAndFunctions();
+
/**
* Configurable options for text cases, with or without deleting the previous database, and with
* or without using the BatchInserter for higher creation speeds. Note that tests that need to
@@ -84,12 +81,15 @@ public abstract class Neo4jTestCase {
*/
@SuppressWarnings("unchecked")
@BeforeEach
- void setUpDatabase() throws Exception {
+ void setUpDatabase() {
Neo4jBuilder neo4jBuilder = Neo4jBuilders
.newInProcessBuilder(getDbPath())
- .withConfig(GraphDatabaseSettings.procedure_unrestricted, List.of("spatial.*"))
- .withProcedure(SpatialProcedures.class)
- .withFunction(SpatialFunctions.class);
+ .withConfig(GraphDatabaseSettings.procedure_unrestricted, List.of("spatial.*"));
+
+ loadProceduresAndFunctions().forEach(aClass -> {
+ neo4jBuilder.withProcedure(aClass);
+ neo4jBuilder.withFunction(aClass);
+ });
String largeMode = System.getProperty("spatial.test.large");
if (largeMode != null && largeMode.equalsIgnoreCase("true")) {
@@ -122,11 +122,18 @@ static Path getDbPath() {
return Path.of("target", "neo4j-db");
}
- void printDatabaseStats() {
- Neo4jTestUtils.printDatabaseStats(graphDb(), getDbPath().toFile());
+ protected void printDatabaseStats() {
+ SpatialTestUtils.printDatabaseStats(graphDb(), getDbPath().toFile());
}
protected GraphDatabaseService graphDb() {
- return neo4j.databaseManagementService().database(DEFAULT_DATABASE_NAME);
+ return neo4j.databaseManagementService().database(GraphDatabaseSettings.DEFAULT_DATABASE_NAME);
+ }
+
+ protected void inTx(Consumer txFunction) {
+ try (Transaction tx = graphDb().beginTx()) {
+ txFunction.accept(tx);
+ tx.commit();
+ }
}
}
diff --git a/test-utils/src/main/java/org/neo4j/spatial/testutils/SpatialTestUtils.java b/test-utils/src/main/java/org/neo4j/spatial/testutils/SpatialTestUtils.java
new file mode 100644
index 000000000..59abff694
--- /dev/null
+++ b/test-utils/src/main/java/org/neo4j/spatial/testutils/SpatialTestUtils.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j Spatial.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.neo4j.spatial.testutils;
+
+import java.io.File;
+import java.util.Objects;
+import org.locationtech.jts.geom.Envelope;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.graphdb.Transaction;
+import org.neo4j.spatial.api.layer.Layer;
+
+// TODO do we need this one?
+public class SpatialTestUtils {
+
+ public static void debugEnvelope(Envelope bbox, String layer, String name) {
+ System.out.println("Layer '" + layer + "' has envelope '" + name + "': " + bbox);
+ System.out.println("\tX: [" + bbox.getMinX() + ":" + bbox.getMaxX() + "]");
+ System.out.println("\tY: [" + bbox.getMinY() + ":" + bbox.getMaxY() + "]");
+ }
+
+ public static int checkIndexCount(Transaction tx, Layer layer) {
+ if (layer.getIndex().count(tx) < 1) {
+ System.out.println("Warning: index count zero: " + layer.getName());
+ }
+ System.out.println(
+ "Layer '" + layer.getName() + "' has " + layer.getIndex().count(tx) + " entries in the index");
+ return layer.getIndex().count(tx);
+ }
+
+ public static void printDatabaseStats(GraphDatabaseService db, File path) {
+ System.out.println("Database stats:");
+ System.out.println("\tTotal disk usage: " + (databaseDiskUsage(path)) / (1024.0 * 1024.0) + "MB");
+ System.out.println("\tTotal # nodes: " + getNumberOfNodes(db));
+ System.out.println("\tTotal # rels: " + getNumberOfRelationships(db));
+ }
+
+ private static long calculateDiskUsage(File file) {
+ if (file.isDirectory()) {
+ long count = 0;
+ for (File sub : Objects.requireNonNull(file.listFiles())) {
+ count += calculateDiskUsage(sub);
+ }
+ return count;
+ }
+ return file.length();
+ }
+
+ private static long databaseDiskUsage(File path) {
+ return calculateDiskUsage(path);
+ }
+
+ private static long getNumberOfNodes(GraphDatabaseService db) {
+ try (Transaction tx = db.beginTx()) {
+ return (Long) tx.execute("MATCH (n) RETURN count(n)").columnAs("count(n)").next();
+ }
+ }
+
+ private static long getNumberOfRelationships(GraphDatabaseService db) {
+ try (Transaction tx = db.beginTx()) {
+ return (Long) tx.execute("MATCH ()-[r]->() RETURN count(r)").columnAs("count(r)").next();
+ }
+ }
+
+}