diff --git a/pom.xml b/pom.xml
index fce635a9..85900e21 100644
--- a/pom.xml
+++ b/pom.xml
@@ -339,7 +339,6 @@
org.geotools
gt-geojson
${geotools.version}
- provided
org.geotools.xsd
diff --git a/src/main/java/org/neo4j/gis/spatial/ShapefileImporter.java b/src/main/java/org/neo4j/gis/spatial/ShapefileImporter.java
index daebac3e..012da835 100644
--- a/src/main/java/org/neo4j/gis/spatial/ShapefileImporter.java
+++ b/src/main/java/org/neo4j/gis/spatial/ShapefileImporter.java
@@ -103,7 +103,7 @@ public List importFile(String dataset, String layerName, Charset charset)
EditableLayerImpl layer;
try (Transaction tx = database.beginTx()) {
layer = (EditableLayerImpl) spatialDatabase.getOrCreateLayer(tx, layerName, WKBGeometryEncoder.class,
- layerClass);
+ layerClass, null);
tx.commit();
}
return importFile(dataset, layer, charset);
diff --git a/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseService.java b/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseService.java
index 61433229..cbe77bf9 100644
--- a/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseService.java
+++ b/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseService.java
@@ -61,6 +61,9 @@
*/
public class SpatialDatabaseService implements Constants {
+ public static final String RTREE_INDEX_NAME = "rtree";
+ public static final String GEOHASH_INDEX_NAME = "geohash";
+
public final IndexManager indexManager;
public SpatialDatabaseService(IndexManager indexManager) {
@@ -178,30 +181,29 @@ public DynamicLayer asDynamicLayer(Transaction tx, Layer layer) {
return (DynamicLayer) LayerUtilities.makeLayerFromNode(tx, indexManager, node);
}
- public DefaultLayer getOrCreateDefaultLayer(Transaction tx, String name) {
- return (DefaultLayer) getOrCreateLayer(tx, name, WKBGeometryEncoder.class, EditableLayerImpl.class, "");
+ public DefaultLayer getOrCreateDefaultLayer(Transaction tx, String name, String indexConfig) {
+ return (DefaultLayer) getOrCreateLayer(tx, name, WKBGeometryEncoder.class, EditableLayerImpl.class, "",
+ indexConfig);
}
- public EditableLayer getOrCreateEditableLayer(Transaction tx, String name, String format,
- String propertyNameConfig) {
+ public EditableLayer getOrCreateEditableLayer(Transaction tx, String name, String format, String propertyNameConfig,
+ String indexConfig) {
Class extends GeometryEncoder> geClass = WKBGeometryEncoder.class;
if (format != null && format.toUpperCase().startsWith("WKT")) {
geClass = WKTGeometryEncoder.class;
}
- return (EditableLayer) getOrCreateLayer(tx, name, geClass, EditableLayerImpl.class, propertyNameConfig);
+ return (EditableLayer) getOrCreateLayer(tx, name, geClass, EditableLayerImpl.class, propertyNameConfig,
+ indexConfig);
}
- public EditableLayer getOrCreateEditableLayer(Transaction tx, String name) {
- return getOrCreateEditableLayer(tx, name, "WKB", "");
+ public EditableLayer getOrCreateEditableLayer(Transaction tx, String name, String indexConfig) {
+ return getOrCreateEditableLayer(tx, name, "WKB", "", indexConfig);
}
- public EditableLayer getOrCreateEditableLayer(Transaction tx, String name, String wktProperty) {
- return getOrCreateEditableLayer(tx, name, "WKT", wktProperty);
+ public EditableLayer getOrCreateEditableLayer(Transaction tx, String name, String wktProperty, String indexConfig) {
+ return getOrCreateEditableLayer(tx, name, "WKT", wktProperty, indexConfig);
}
- public static final String RTREE_INDEX_NAME = "rtree";
- public static final String GEOHASH_INDEX_NAME = "geohash";
-
public static Class extends LayerIndexReader> resolveIndexClass(String index) {
if (index == null) {
return LayerRTreeIndex.class;
@@ -216,23 +218,25 @@ public static Class extends LayerIndexReader> resolveIndexClass(String index)
}
public EditableLayer getOrCreateSimplePointLayer(Transaction tx, String name, String index, String xProperty,
- String yProperty) {
- return getOrCreatePointLayer(tx, name, resolveIndexClass(index), SimplePointEncoder.class, xProperty,
+ String yProperty, String indexConfig) {
+ return getOrCreatePointLayer(tx, name, resolveIndexClass(index), SimplePointEncoder.class, indexConfig,
+ xProperty,
yProperty);
}
public EditableLayer getOrCreateNativePointLayer(Transaction tx, String name, String index,
- String locationProperty) {
- return getOrCreatePointLayer(tx, name, resolveIndexClass(index), SimplePointEncoder.class, locationProperty);
+ String locationProperty, String indexConfig) {
+ return getOrCreatePointLayer(tx, name, resolveIndexClass(index), SimplePointEncoder.class, indexConfig,
+ locationProperty);
}
public EditableLayer getOrCreatePointLayer(Transaction tx, String name,
Class extends LayerIndexReader> indexClass, Class extends GeometryEncoder> encoderClass,
- String... encoderConfig) {
+ String indexConfig, String... encoderConfig) {
Layer layer = getLayer(tx, name);
if (layer == null) {
return (EditableLayer) createLayer(tx, name, encoderClass, SimplePointLayer.class, indexClass,
- makeEncoderConfig(encoderConfig), DefaultGeographicCRS.WGS84);
+ makeEncoderConfig(encoderConfig), indexConfig, DefaultGeographicCRS.WGS84);
}
if (layer instanceof EditableLayer) {
return (EditableLayer) layer;
@@ -242,10 +246,10 @@ public EditableLayer getOrCreatePointLayer(Transaction tx, String name,
}
public Layer getOrCreateLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoder,
- Class extends Layer> layerClass, String config) {
+ Class extends Layer> layerClass, String encoderConfig, String indexConfig) {
Layer layer = getLayer(tx, name);
if (layer == null) {
- layer = createLayer(tx, name, geometryEncoder, layerClass, null, config);
+ layer = createLayer(tx, name, geometryEncoder, layerClass, null, encoderConfig, indexConfig);
} else if (!(layerClass == null || layerClass.isInstance(layer))) {
throw new SpatialDatabaseException(
"Existing layer '" + layer + "' is not of the expected type: " + layerClass);
@@ -254,8 +258,8 @@ public Layer getOrCreateLayer(Transaction tx, String name, Class extends Geome
}
public Layer getOrCreateLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoder,
- Class extends Layer> layerClass) {
- return getOrCreateLayer(tx, name, geometryEncoder, layerClass, "");
+ Class extends Layer> layerClass, String indexConfig) {
+ return getOrCreateLayer(tx, name, geometryEncoder, layerClass, "", indexConfig);
}
/**
@@ -298,8 +302,8 @@ public boolean containsLayer(Transaction tx, String name) {
return getLayer(tx, name) != null;
}
- public Layer createWKBLayer(Transaction tx, String name) {
- return createLayer(tx, name, WKBGeometryEncoder.class, EditableLayerImpl.class);
+ public Layer createWKBLayer(Transaction tx, String name, String indexConfig) {
+ return createLayer(tx, name, WKBGeometryEncoder.class, EditableLayerImpl.class, indexConfig);
}
public SimplePointLayer createSimplePointLayer(Transaction tx, String name) {
@@ -311,7 +315,7 @@ public SimplePointLayer createSimplePointLayer(Transaction tx, String name, Stri
}
public SimplePointLayer createSimplePointLayer(Transaction tx, String name, String... xybProperties) {
- return createPointLayer(tx, name, LayerRTreeIndex.class, SimplePointEncoder.class, xybProperties);
+ return createPointLayer(tx, name, LayerRTreeIndex.class, SimplePointEncoder.class, null, xybProperties);
}
public SimplePointLayer createNativePointLayer(Transaction tx, String name) {
@@ -324,13 +328,14 @@ public SimplePointLayer createNativePointLayer(Transaction tx, String name, Stri
}
public SimplePointLayer createNativePointLayer(Transaction tx, String name, String... encoderConfig) {
- return createPointLayer(tx, name, LayerRTreeIndex.class, NativePointEncoder.class, encoderConfig);
+ return createPointLayer(tx, name, LayerRTreeIndex.class, NativePointEncoder.class, null, encoderConfig);
}
public SimplePointLayer createPointLayer(Transaction tx, String name, Class extends LayerIndexReader> indexClass,
- Class extends GeometryEncoder> encoderClass, String... encoderConfig) {
+ Class extends GeometryEncoder> encoderClass, String indexConfig, String... encoderConfig
+ ) {
return (SimplePointLayer) createLayer(tx, name, encoderClass, SimplePointLayer.class, indexClass,
- makeEncoderConfig(encoderConfig), org.geotools.referencing.crs.DefaultGeographicCRS.WGS84);
+ makeEncoderConfig(encoderConfig), indexConfig, org.geotools.referencing.crs.DefaultGeographicCRS.WGS84);
}
public static String makeEncoderConfig(String... args) {
@@ -349,19 +354,27 @@ public static String makeEncoderConfig(String... args) {
}
public Layer createLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoderClass,
- Class extends Layer> layerClass) {
- return createLayer(tx, name, geometryEncoderClass, layerClass, null, null);
- }
-
- public Layer createLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoderClass,
- Class extends Layer> layerClass, Class extends LayerIndexReader> indexClass,
- String encoderConfig) {
- return createLayer(tx, name, geometryEncoderClass, layerClass, indexClass, encoderConfig, null);
+ Class extends Layer> layerClass, String indexConfig) {
+ return createLayer(tx, name, geometryEncoderClass, layerClass, null, null, indexConfig);
}
public Layer createLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoderClass,
Class extends Layer> layerClass, Class extends LayerIndexReader> indexClass,
- String encoderConfig, CoordinateReferenceSystem crs) {
+ String encoderConfig,
+ String indexConfig
+ ) {
+ return createLayer(tx, name, geometryEncoderClass, layerClass, indexClass, encoderConfig, indexConfig, null);
+ }
+
+ public Layer createLayer(Transaction tx,
+ String name,
+ Class extends GeometryEncoder> geometryEncoderClass,
+ Class extends Layer> layerClass,
+ Class extends LayerIndexReader> indexClass,
+ String encoderConfig,
+ String indexConfig,
+ CoordinateReferenceSystem crs
+ ) {
if (containsLayer(tx, name)) {
throw new SpatialDatabaseException("Layer " + name + " already exists");
}
@@ -379,6 +392,17 @@ public Layer createLayer(Transaction tx, String name, Class extends GeometryEn
+ geometryEncoderClass);
}
}
+ if (indexConfig != null && !indexConfig.isEmpty()) {
+ LayerIndexReader index = layer.getIndex();
+ if (index instanceof Configurable) {
+ ((Configurable) index).setConfiguration(indexConfig);
+ layer.getLayerNode(tx).setProperty(PROP_INDEX_CONFIG, indexConfig);
+ } else {
+ System.out.println(
+ "Warning: index configuration '" + indexConfig + "' passed to non-configurable index: "
+ + indexClass);
+ }
+ }
if (crs != null && layer instanceof EditableLayer) {
((EditableLayer) layer).setCoordinateReferenceSystem(tx, crs);
}
@@ -465,7 +489,7 @@ public static int convertJtsClassToGeometryType(Class extends Geometry> jtsCla
* @return new Layer with copy of all geometries
*/
public Layer createResultsLayer(Transaction tx, String layerName, List results) {
- EditableLayer layer = (EditableLayer) createWKBLayer(tx, layerName);
+ EditableLayer layer = (EditableLayer) createWKBLayer(tx, layerName, "");
for (SpatialDatabaseRecord record : results) {
layer.add(tx, record.getGeometry());
}
@@ -544,15 +568,16 @@ private static void addRegisteredLayerType(RegisteredLayerType type) {
registeredLayerTypes.put(type.typeName.toLowerCase(), type);
}
- public Layer getOrCreateRegisteredTypeLayer(Transaction tx, String name, String type, String config) {
+ public Layer getOrCreateRegisteredTypeLayer(Transaction tx, String name, String type, String encoderConfig,
+ String indexConfig) {
RegisteredLayerType registeredLayerType = registeredLayerTypes.get(type.toLowerCase());
- return getOrCreateRegisteredTypeLayer(tx, name, registeredLayerType, config);
+ return getOrCreateRegisteredTypeLayer(tx, name, registeredLayerType, encoderConfig, indexConfig);
}
public Layer getOrCreateRegisteredTypeLayer(Transaction tx, String name, RegisteredLayerType registeredLayerType,
- String config) {
+ String encoderConfig, String indexConfig) {
return getOrCreateLayer(tx, name, registeredLayerType.geometryEncoder, registeredLayerType.layerClass,
- (config == null) ? registeredLayerType.defaultConfig : config);
+ (encoderConfig == null) ? registeredLayerType.defaultConfig : encoderConfig, indexConfig);
}
public static Map getRegisteredLayerTypes() {
diff --git a/src/main/java/org/neo4j/gis/spatial/osm/OSMImporter.java b/src/main/java/org/neo4j/gis/spatial/osm/OSMImporter.java
index 22557f21..f343d209 100644
--- a/src/main/java/org/neo4j/gis/spatial/osm/OSMImporter.java
+++ b/src/main/java/org/neo4j/gis/spatial/osm/OSMImporter.java
@@ -250,7 +250,7 @@ public long reIndex(GraphDatabaseService database, int commitInterval, boolean i
OSMDataset dataset;
try (Transaction tx = beginTx(database)) {
layer = (OSMLayer) spatialDatabase.getOrCreateLayer(tx, layerName, OSMGeometryEncoder.class,
- OSMLayer.class);
+ OSMLayer.class, null);
dataset = OSMDataset.withDatasetId(tx, layer, osm_dataset);
tx.commit();
}
diff --git a/src/main/java/org/neo4j/gis/spatial/procedures/SpatialProcedures.java b/src/main/java/org/neo4j/gis/spatial/procedures/SpatialProcedures.java
index 02f7fc2e..b6aa9328 100644
--- a/src/main/java/org/neo4j/gis/spatial/procedures/SpatialProcedures.java
+++ b/src/main/java/org/neo4j/gis/spatial/procedures/SpatialProcedures.java
@@ -211,13 +211,15 @@ public Stream getAllLayerTypes() {
public Stream addSimplePointLayer(
@Name("name") String name,
@Name(value = "indexType", defaultValue = RTREE_INDEX_NAME) String indexType,
- @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName) {
+ @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig
+ ) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
return streamNode(sdb.createLayer(tx, name, SimplePointEncoder.class, SimplePointLayer.class,
- SpatialDatabaseService.resolveIndexClass(indexType), null,
- selectCRS(crsName)).getLayerNode(tx));
+ SpatialDatabaseService.resolveIndexClass(indexType), null, indexConfig, selectCRS(crsName))
+ .getLayerNode(tx));
}
throw new IllegalArgumentException("Cannot create existing layer: " + name);
}
@@ -226,37 +228,44 @@ public Stream addSimplePointLayer(
@Description("Adds a new simple point layer with geohash based index, returns the layer root node")
public Stream addSimplePointLayerGeohash(
@Name("name") String name,
- @Name(value = "crsName", defaultValue = WGS84_CRS_NAME) String crsName) {
+ @Name(value = "crsName", defaultValue = WGS84_CRS_NAME) String crsName,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
return streamNode(sdb.createLayer(tx, name, SimplePointEncoder.class, SimplePointLayer.class,
- LayerGeohashPointIndex.class, null,
- selectCRS(crsName)).getLayerNode(tx));
+ LayerGeohashPointIndex.class, null, indexConfig, selectCRS(crsName))
+ .getLayerNode(tx));
}
throw new IllegalArgumentException("Cannot create existing layer: " + name);
}
@Procedure(value = "spatial.addPointLayerZOrder", mode = WRITE)
@Description("Adds a new simple point layer with z-order curve based index, returns the layer root node")
- public Stream addSimplePointLayerZOrder(@Name("name") String name) {
+ public Stream addSimplePointLayerZOrder(@Name("name") String name,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
return streamNode(sdb.createLayer(tx, name, SimplePointEncoder.class, SimplePointLayer.class,
- LayerZOrderPointIndex.class, null, DefaultGeographicCRS.WGS84).getLayerNode(tx));
+ LayerZOrderPointIndex.class, null, indexConfig, DefaultGeographicCRS.WGS84)
+ .getLayerNode(tx));
}
throw new IllegalArgumentException("Cannot create existing layer: " + name);
}
@Procedure(value = "spatial.addPointLayerHilbert", mode = WRITE)
@Description("Adds a new simple point layer with hilbert curve based index, returns the layer root node")
- public Stream addSimplePointLayerHilbert(@Name("name") String name) {
+ public Stream addSimplePointLayerHilbert(
+ @Name("name") String name,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig
+ ) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
return streamNode(sdb.createLayer(tx, name, SimplePointEncoder.class, SimplePointLayer.class,
- LayerHilbertPointIndex.class, null, DefaultGeographicCRS.WGS84).getLayerNode(tx));
+ LayerHilbertPointIndex.class, null, indexConfig, DefaultGeographicCRS.WGS84)
+ .getLayerNode(tx));
}
throw new IllegalArgumentException("Cannot create existing layer: " + name);
}
@@ -268,15 +277,17 @@ public Stream addSimplePointLayer(
@Name("xProperty") String xProperty,
@Name("yProperty") String yProperty,
@Name(value = "indexType", defaultValue = RTREE_INDEX_NAME) String indexType,
- @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName) {
+ @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
if (xProperty != null && yProperty != null) {
return streamNode(sdb.createLayer(tx, name, SimplePointEncoder.class, SimplePointLayer.class,
- SpatialDatabaseService.resolveIndexClass(indexType),
- SpatialDatabaseService.makeEncoderConfig(xProperty, yProperty),
- selectCRS(hintCRSName(crsName, yProperty))).getLayerNode(tx));
+ SpatialDatabaseService.resolveIndexClass(indexType),
+ SpatialDatabaseService.makeEncoderConfig(xProperty, yProperty), indexConfig,
+ selectCRS(hintCRSName(crsName, yProperty)))
+ .getLayerNode(tx));
}
throw new IllegalArgumentException(
"Cannot create layer '" + name + "': Missing encoder config values: xProperty[" + xProperty
@@ -291,14 +302,16 @@ public Stream addSimplePointLayerWithConfig(
@Name("name") String name,
@Name("encoderConfig") String encoderConfig,
@Name(value = "indexType", defaultValue = RTREE_INDEX_NAME) String indexType,
- @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName) {
+ @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
if (encoderConfig.indexOf(':') > 0) {
return streamNode(sdb.createLayer(tx, name, SimplePointEncoder.class, SimplePointLayer.class,
- SpatialDatabaseService.resolveIndexClass(indexType), encoderConfig,
- selectCRS(hintCRSName(crsName, encoderConfig))).getLayerNode(tx));
+ SpatialDatabaseService.resolveIndexClass(indexType), encoderConfig, indexConfig,
+ selectCRS(hintCRSName(crsName, encoderConfig)))
+ .getLayerNode(tx));
}
throw new IllegalArgumentException(
"Cannot create layer '" + name + "': invalid encoder config '" + encoderConfig + "'");
@@ -311,12 +324,14 @@ public Stream addSimplePointLayerWithConfig(
public Stream addNativePointLayer(
@Name("name") String name,
@Name(value = "indexType", defaultValue = RTREE_INDEX_NAME) String indexType,
- @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName) {
+ @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
return streamNode(sdb.createLayer(tx, name, NativePointEncoder.class, SimplePointLayer.class,
- SpatialDatabaseService.resolveIndexClass(indexType), null, selectCRS(crsName)).getLayerNode(tx));
+ SpatialDatabaseService.resolveIndexClass(indexType), null, indexConfig, selectCRS(crsName))
+ .getLayerNode(tx));
}
throw new IllegalArgumentException("Cannot create existing layer: " + name);
}
@@ -325,36 +340,43 @@ public Stream addNativePointLayer(
@Description("Adds a new native point layer with geohash based index, returns the layer root node")
public Stream addNativePointLayerGeohash(
@Name("name") String name,
- @Name(value = "crsName", defaultValue = WGS84_CRS_NAME) String crsName) {
+ @Name(value = "crsName", defaultValue = WGS84_CRS_NAME) String crsName,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
return streamNode(sdb.createLayer(tx, name, NativePointEncoder.class, SimplePointLayer.class,
- LayerGeohashPointIndex.class, null, selectCRS(crsName)).getLayerNode(tx));
+ LayerGeohashPointIndex.class, null, indexConfig, selectCRS(crsName))
+ .getLayerNode(tx));
}
throw new IllegalArgumentException("Cannot create existing layer: " + name);
}
@Procedure(value = "spatial.addNativePointLayerZOrder", mode = WRITE)
@Description("Adds a new native point layer with z-order curve based index, returns the layer root node")
- public Stream addNativePointLayerZOrder(@Name("name") String name) {
+ public Stream addNativePointLayerZOrder(@Name("name") String name,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
return streamNode(sdb.createLayer(tx, name, NativePointEncoder.class, SimplePointLayer.class,
- LayerZOrderPointIndex.class, null, DefaultGeographicCRS.WGS84).getLayerNode(tx));
+ LayerZOrderPointIndex.class, null, indexConfig, DefaultGeographicCRS.WGS84)
+ .getLayerNode(tx));
}
throw new IllegalArgumentException("Cannot create existing layer: " + name);
}
@Procedure(value = "spatial.addNativePointLayerHilbert", mode = WRITE)
@Description("Adds a new native point layer with hilbert curve based index, returns the layer root node")
- public Stream addNativePointLayerHilbert(@Name("name") String name) {
+ public Stream addNativePointLayerHilbert(@Name("name") String name,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig
+ ) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
return streamNode(sdb.createLayer(tx, name, NativePointEncoder.class, SimplePointLayer.class,
- LayerHilbertPointIndex.class, null, DefaultGeographicCRS.WGS84).getLayerNode(tx));
+ LayerHilbertPointIndex.class, null, indexConfig, DefaultGeographicCRS.WGS84)
+ .getLayerNode(tx));
}
throw new IllegalArgumentException("Cannot create existing layer: " + name);
}
@@ -366,15 +388,17 @@ public Stream addNativePointLayer(
@Name("xProperty") String xProperty,
@Name("yProperty") String yProperty,
@Name(value = "indexType", defaultValue = RTREE_INDEX_NAME) String indexType,
- @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName) {
+ @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
if (xProperty != null && yProperty != null) {
return streamNode(sdb.createLayer(tx, name, NativePointEncoder.class, SimplePointLayer.class,
- SpatialDatabaseService.resolveIndexClass(indexType),
- SpatialDatabaseService.makeEncoderConfig(xProperty, yProperty),
- selectCRS(hintCRSName(crsName, yProperty))).getLayerNode(tx));
+ SpatialDatabaseService.resolveIndexClass(indexType),
+ SpatialDatabaseService.makeEncoderConfig(xProperty, yProperty), indexConfig,
+ selectCRS(hintCRSName(crsName, yProperty)))
+ .getLayerNode(tx));
}
throw new IllegalArgumentException(
"Cannot create layer '" + name + "': Missing encoder config values: xProperty[" + xProperty
@@ -389,14 +413,16 @@ public Stream addNativePointLayerWithConfig(
@Name("name") String name,
@Name("encoderConfig") String encoderConfig,
@Name(value = "indexType", defaultValue = RTREE_INDEX_NAME) String indexType,
- @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName) {
+ @Name(value = "crsName", defaultValue = UNSET_CRS_NAME) String crsName,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
if (encoderConfig.indexOf(':') > 0) {
return streamNode(sdb.createLayer(tx, name, NativePointEncoder.class, SimplePointLayer.class,
- SpatialDatabaseService.resolveIndexClass(indexType), encoderConfig,
- selectCRS(hintCRSName(crsName, encoderConfig))).getLayerNode(tx));
+ SpatialDatabaseService.resolveIndexClass(indexType), encoderConfig, indexConfig,
+ selectCRS(hintCRSName(crsName, encoderConfig)))
+ .getLayerNode(tx));
}
throw new IllegalArgumentException(
"Cannot create layer '" + name + "': invalid encoder config '" + encoderConfig + "'");
@@ -405,6 +431,7 @@ public Stream addNativePointLayerWithConfig(
}
public static final String UNSET_CRS_NAME = "";
+ public static final String UNSET_INDEX_CONFIG = "";
public static final String WGS84_CRS_NAME = "wgs84";
/**
@@ -437,15 +464,17 @@ private static String hintCRSName(String crsName, String hint) {
public Stream addLayerWithEncoder(
@Name("name") String name,
@Name("encoder") String encoderClassName,
- @Name("encoderConfig") String encoderConfig) {
+ @Name("encoderConfig") String encoderConfig,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
Class extends GeometryEncoder> encoderClass = encoderClasses.get(encoderClassName);
Class extends Layer> layerClass = SpatialDatabaseService.suggestLayerClassForEncoder(encoderClass);
if (encoderClass != null) {
- return streamNode(
- sdb.createLayer(tx, name, encoderClass, layerClass, null, encoderConfig).getLayerNode(tx));
+ return streamNode(sdb
+ .createLayer(tx, name, encoderClass, layerClass, null, encoderConfig, indexConfig)
+ .getLayerNode(tx));
}
throw new IllegalArgumentException(
"Cannot create layer '" + name + "': invalid encoder class '" + encoderClassName + "'");
@@ -458,13 +487,15 @@ public Stream addLayerWithEncoder(
public Stream addLayerOfType(
@Name("name") String name,
@Name("type") String type,
- @Name("encoderConfig") String encoderConfig) {
+ @Name("encoderConfig") String encoderConfig,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
SpatialDatabaseService sdb = spatial();
Layer layer = sdb.getLayer(tx, name);
if (layer == null) {
Map knownTypes = SpatialDatabaseService.getRegisteredLayerTypes();
if (knownTypes.containsKey(type.toLowerCase())) {
- return streamNode(sdb.getOrCreateRegisteredTypeLayer(tx, name, type, encoderConfig).getLayerNode(tx));
+ return streamNode(sdb.getOrCreateRegisteredTypeLayer(tx, name, type, encoderConfig, indexConfig)
+ .getLayerNode(tx));
}
throw new IllegalArgumentException(
"Cannot create layer '" + name + "': unknown type '" + type + "' - supported types are "
@@ -484,8 +515,9 @@ private static Stream streamNode(String nodeId) {
@Procedure(value = "spatial.addWKTLayer", mode = WRITE)
@Description("Adds a new WKT layer with the given node property to hold the WKT string, returns the layer root node")
public Stream addWKTLayer(@Name("name") String name,
- @Name("nodePropertyName") String nodePropertyName) {
- return addLayerOfType(name, "WKT", nodePropertyName);
+ @Name("nodePropertyName") String nodePropertyName,
+ @Name(value = "indexConfig", defaultValue = UNSET_INDEX_CONFIG) String indexConfig) {
+ return addLayerOfType(name, "WKT", nodePropertyName, indexConfig);
}
@Procedure(value = "spatial.layer", mode = WRITE)
@@ -671,7 +703,7 @@ public Stream importOSM(
// Delegate creating the layer to the inner thread, so we do not pollute the procedure transaction with anything that might conflict.
// Since the procedure transaction starts before, and ends after, all inner transactions.
BiFunction layerMaker = (tx, name) -> (OSMLayer) spatial().getOrCreateLayer(tx,
- name, OSMGeometryEncoder.class, OSMLayer.class);
+ name, OSMGeometryEncoder.class, OSMLayer.class, "");
return Stream.of(new CountResult(importOSMToLayer(uri, layerName, layerMaker)));
}
diff --git a/src/main/java/org/neo4j/gis/spatial/rtree/RTreeIndex.java b/src/main/java/org/neo4j/gis/spatial/rtree/RTreeIndex.java
index 11b357d7..b7c50b30 100644
--- a/src/main/java/org/neo4j/gis/spatial/rtree/RTreeIndex.java
+++ b/src/main/java/org/neo4j/gis/spatial/rtree/RTreeIndex.java
@@ -67,6 +67,7 @@ public class RTreeIndex implements SpatialIndexWriter, Configurable {
public static final String KEY_MAX_NODE_REFERENCES = "maxNodeReferences";
public static final String KEY_SHOULD_MERGE_TREES = "shouldMergeTrees";
+ public static final String REFERENCE_RELATIONSHIP_TYPE = "referenceRelationshipType";
public static final int MIN_MAX_NODE_REFERENCES = 10;
public static final int MAX_MAX_NODE_REFERENCES = 1000000;
public static final int DEFAULT_MAX_NODE_REFERENCES = 100;
@@ -77,6 +78,7 @@ public class RTreeIndex implements SpatialIndexWriter, Configurable {
private int maxNodeReferences;
private String splitMode = GREENES_SPLIT;
private boolean shouldMergeTrees = false;
+ private RelationshipType referenceRelationshipType = RTreeRelationshipTypes.RTREE_REFERENCE;
private int totalGeometryCount = 0;
private boolean countSaved = false;
@@ -106,6 +108,9 @@ public EnvelopeDecoder getEnvelopeDecoder() {
@Override
public void setConfiguration(String jsonConfig) {
+ if (jsonConfig == null || jsonConfig.isBlank()) {
+ return;
+ }
JSONObject jsonObject = (JSONObject) JSONValue.parse(jsonConfig);
HashMap config = new HashMap<>();
for (Object key : jsonObject.keySet()) {
@@ -120,26 +125,28 @@ public String getConfiguration() {
config.put(KEY_SPLIT, this.splitMode);
config.put(KEY_MAX_NODE_REFERENCES, this.maxNodeReferences);
config.put(KEY_SHOULD_MERGE_TREES, this.shouldMergeTrees);
+ config.put(REFERENCE_RELATIONSHIP_TYPE, this.referenceRelationshipType.name());
return JSONObject.toJSONString(config);
}
@Override
public void configure(Map config) {
- for (String key : config.keySet()) {
+ config.forEach((key, rawValue) -> {
switch (key) {
case KEY_SPLIT:
- String value = config.get(key).toString();
+ String value = rawValue.toString();
switch (value) {
case QUADRATIC_SPLIT:
case GREENES_SPLIT:
splitMode = value;
break;
default:
- throw new IllegalArgumentException("No such RTreeIndex value for '" + key + "': " + value);
+ throw new IllegalArgumentException(
+ "No such RTreeIndex value for '" + key + "': " + rawValue);
}
break;
case KEY_MAX_NODE_REFERENCES:
- int intValue = Integer.parseInt(config.get(key).toString());
+ int intValue = Integer.parseInt(rawValue.toString());
if (intValue < MIN_MAX_NODE_REFERENCES) {
throw new IllegalArgumentException(
"RTreeIndex does not allow " + key + " less than " + MIN_MAX_NODE_REFERENCES);
@@ -151,12 +158,15 @@ public void configure(Map config) {
this.maxNodeReferences = intValue;
break;
case KEY_SHOULD_MERGE_TREES:
- this.shouldMergeTrees = Boolean.parseBoolean(config.get(key).toString());
+ this.shouldMergeTrees = Boolean.parseBoolean(rawValue.toString());
+ break;
+ case REFERENCE_RELATIONSHIP_TYPE:
+ this.referenceRelationshipType = RelationshipType.withName(rawValue.toString());
break;
default:
throw new IllegalArgumentException("No such RTreeIndex configuration key: " + key);
}
- }
+ });
}
@Override
@@ -177,7 +187,7 @@ private void addBelow(Transaction tx, Node parent, Node geomNode) {
parent = chooseSubTree(parent, geomNode);
}
// bbox enlargement needed
- if (countChildren(parent, RTreeRelationshipTypes.RTREE_REFERENCE) >= maxNodeReferences) {
+ if (countChildren(parent, referenceRelationshipType) >= maxNodeReferences) {
insertInLeaf(parent, geomNode);
splitAndAdjustPathBoundingBox(tx, parent);
} else if (insertInLeaf(parent, geomNode)) {
@@ -404,7 +414,7 @@ private List bulkInsertion(Transaction tx, Node rootNode, int
int newHeight = getHeight(newRootNode, 0);
if (newHeight == 1) {
monitor.addCase("h_i > l_t (d==1)");
- try (var relationships = newRootNode.getRelationships(RTreeRelationshipTypes.RTREE_REFERENCE)) {
+ try (var relationships = newRootNode.getRelationships(referenceRelationshipType)) {
for (Relationship geom : relationships) {
addBelow(tx, child.node, geom.getEndNode());
geom.delete();
@@ -615,7 +625,7 @@ public void remove(Transaction tx, String geomNodeId, boolean deleteGeomNode, bo
// remove the entry
final Relationship geometryRtreeReference = geomNode.getSingleRelationship(
- RTreeRelationshipTypes.RTREE_REFERENCE, Direction.INCOMING);
+ referenceRelationshipType, Direction.INCOMING);
if (geometryRtreeReference != null) {
geometryRtreeReference.delete();
}
@@ -624,11 +634,11 @@ public void remove(Transaction tx, String geomNodeId, boolean deleteGeomNode, bo
}
// reorganize the tree if needed
- if (countChildren(indexNode, RTreeRelationshipTypes.RTREE_REFERENCE) == 0) {
- indexNode = deleteEmptyTreeNodes(indexNode, RTreeRelationshipTypes.RTREE_REFERENCE);
+ if (countChildren(indexNode, referenceRelationshipType) == 0) {
+ indexNode = deleteEmptyTreeNodes(indexNode, referenceRelationshipType);
adjustParentBoundingBox(indexNode, RTreeRelationshipTypes.RTREE_CHILD);
} else {
- adjustParentBoundingBox(indexNode, RTreeRelationshipTypes.RTREE_REFERENCE);
+ adjustParentBoundingBox(indexNode, referenceRelationshipType);
}
adjustPathBoundingBox(indexNode);
@@ -670,7 +680,7 @@ public boolean needsToVisit(Envelope indexNodeEnvelope) {
@Override
public void onIndexReference(Node geomNode) {
- geomNode.getSingleRelationship(RTreeRelationshipTypes.RTREE_REFERENCE, Direction.INCOMING).delete();
+ geomNode.getSingleRelationship(referenceRelationshipType, Direction.INCOMING).delete();
if (deleteGeomNodes) {
deleteNode(geomNode);
}
@@ -791,7 +801,7 @@ public Evaluation evaluate(Path path, BranchState SearchFilter.EnvelopFilterResult.FILTER)
.relationships(RTreeRelationshipTypes.RTREE_CHILD, Direction.OUTGOING)
- .relationships(RTreeRelationshipTypes.RTREE_REFERENCE, Direction.OUTGOING)
+ .relationships(referenceRelationshipType, Direction.OUTGOING)
.evaluator(searchEvaluator);
Traverser traverser = td.traverse(getIndexRoot(tx));
return new SearchResults(traverser.nodes());
@@ -840,9 +850,9 @@ public void visit(Transaction tx, SpatialIndexVisitor visitor, Node indexNode) {
}
}
} else // Node is a leaf
- if (indexNode.hasRelationship(Direction.OUTGOING, RTreeRelationshipTypes.RTREE_REFERENCE)) {
+ if (indexNode.hasRelationship(Direction.OUTGOING, referenceRelationshipType)) {
try (var relationships = indexNode.getRelationships(Direction.OUTGOING,
- RTreeRelationshipTypes.RTREE_REFERENCE)) {
+ referenceRelationshipType)) {
for (Relationship rel : relationships) {
visitor.onIndexReference(rel.getEndNode());
}
@@ -862,7 +872,7 @@ public Node getIndexRoot(Transaction tx) {
* know whether the child is a leaf or an index node.
*/
private Envelope getChildNodeEnvelope(Node child, RelationshipType relType) {
- if (relType.name().equals(RTreeRelationshipTypes.RTREE_REFERENCE.name())) {
+ if (relType.name().equals(referenceRelationshipType.name())) {
return getLeafNodeEnvelope(child);
}
return getIndexNodeEnvelope(child);
@@ -892,7 +902,7 @@ public static Envelope getIndexNodeEnvelope(Node indexNode) {
return new Envelope(bbox[0], bbox[2], bbox[1], bbox[3]);
}
- private static void visitInTx(Transaction tx, SpatialIndexVisitor visitor, String indexNodeId) {
+ private void visitInTx(Transaction tx, SpatialIndexVisitor visitor, String indexNodeId) {
Node indexNode = tx.getNodeByElementId(indexNodeId);
if (!visitor.needsToVisit(getIndexNodeEnvelope(indexNode))) {
return;
@@ -915,9 +925,9 @@ private static void visitInTx(Transaction tx, SpatialIndexVisitor visitor, Strin
visitInTx(tx, visitor, child);
}
} else // Node is a leaf
- if (indexNode.hasRelationship(Direction.OUTGOING, RTreeRelationshipTypes.RTREE_REFERENCE)) {
+ if (indexNode.hasRelationship(Direction.OUTGOING, referenceRelationshipType)) {
try (var relationships = indexNode.getRelationships(Direction.OUTGOING,
- RTreeRelationshipTypes.RTREE_REFERENCE)) {
+ referenceRelationshipType)) {
for (Relationship rel : relationships) {
visitor.onIndexReference(rel.getEndNode());
}
@@ -1071,7 +1081,7 @@ private static int countChildren(Node indexNode, RelationshipType relationshipTy
* @return is enlargement needed?
*/
private boolean insertInLeaf(Node indexNode, Node geomRootNode) {
- return addChild(indexNode, RTreeRelationshipTypes.RTREE_REFERENCE, geomRootNode);
+ return addChild(indexNode, referenceRelationshipType, geomRootNode);
}
private void splitAndAdjustPathBoundingBox(Transaction tx, Node indexNode) {
@@ -1098,14 +1108,14 @@ private void splitAndAdjustPathBoundingBox(Transaction tx, Node indexNode) {
private Node quadraticSplit(Transaction tx, Node indexNode) {
if (nodeIsLeaf(indexNode)) {
- return quadraticSplit(tx, indexNode, RTreeRelationshipTypes.RTREE_REFERENCE);
+ return quadraticSplit(tx, indexNode, referenceRelationshipType);
}
return quadraticSplit(tx, indexNode, RTreeRelationshipTypes.RTREE_CHILD);
}
private Node greenesSplit(Transaction tx, Node indexNode) {
if (nodeIsLeaf(indexNode)) {
- return greenesSplit(tx, indexNode, RTreeRelationshipTypes.RTREE_REFERENCE);
+ return greenesSplit(tx, indexNode, referenceRelationshipType);
}
return greenesSplit(tx, indexNode, RTreeRelationshipTypes.RTREE_CHILD);
}
@@ -1425,12 +1435,12 @@ private static void deleteRecursivelySubtree(Node node, Relationship incoming) {
node.delete();
}
- protected static boolean isGeometryNodeIndexed(Node geomNode) {
- return geomNode.hasRelationship(Direction.INCOMING, RTreeRelationshipTypes.RTREE_REFERENCE);
+ protected boolean isGeometryNodeIndexed(Node geomNode) {
+ return geomNode.hasRelationship(Direction.INCOMING, referenceRelationshipType);
}
- protected static Node findLeafContainingGeometryNode(Node geomNode) {
- return geomNode.getSingleRelationship(RTreeRelationshipTypes.RTREE_REFERENCE, Direction.INCOMING)
+ protected Node findLeafContainingGeometryNode(Node geomNode) {
+ return geomNode.getSingleRelationship(referenceRelationshipType, Direction.INCOMING)
.getStartNode();
}
@@ -1488,7 +1498,7 @@ public void onIndexReference(Node geomNode) {
* the objects from one type to another without loading all into memory,
* we need to use this ugly java-magic. Man, I miss Ruby right now!
*/
- private static class IndexNodeToGeometryNodeIterable implements Iterable {
+ private class IndexNodeToGeometryNodeIterable implements Iterable {
private final Iterator allIndexNodeIterator;
@@ -1512,7 +1522,7 @@ private void checkGeometryNodeIterator() {
MonoDirectionalTraversalDescription traversal = new MonoDirectionalTraversalDescription();
TraversalDescription td = traversal
.depthFirst()
- .relationships(RTreeRelationshipTypes.RTREE_REFERENCE, Direction.OUTGOING)
+ .relationships(referenceRelationshipType, Direction.OUTGOING)
.evaluator(Evaluators.excludeStartPosition())
.evaluator(Evaluators.toDepth(1));
while ((geometryNodeIterator == null || !geometryNodeIterator.hasNext()) &&
diff --git a/src/test/java/org/neo4j/gis/spatial/LayerSignatureTest.java b/src/test/java/org/neo4j/gis/spatial/LayerSignatureTest.java
index b0a367f7..5ae2056c 100644
--- a/src/test/java/org/neo4j/gis/spatial/LayerSignatureTest.java
+++ b/src/test/java/org/neo4j/gis/spatial/LayerSignatureTest.java
@@ -64,19 +64,19 @@ public void testDefaultSimplePointLayer() {
@Test
public void testSimpleWKBLayer() {
testLayerSignature("EditableLayer(name='test', encoder=WKBGeometryEncoder(geom='geometry', bbox='bbox'))",
- tx -> spatial.createWKBLayer(tx, "test"));
+ tx -> spatial.createWKBLayer(tx, "test", null));
}
@Test
public void testWKBLayer() {
testLayerSignature("EditableLayer(name='test', encoder=WKBGeometryEncoder(geom='wkb', bbox='bbox'))",
- tx -> spatial.getOrCreateEditableLayer(tx, "test", "wkb", "wkb"));
+ tx -> spatial.getOrCreateEditableLayer(tx, "test", "wkb", "wkb", null));
}
@Test
public void testWKTLayer() {
testLayerSignature("EditableLayer(name='test', encoder=WKTGeometryEncoder(geom='wkt', bbox='bbox'))",
- tx -> spatial.getOrCreateEditableLayer(tx, "test", "wkt", "wkt"));
+ tx -> spatial.getOrCreateEditableLayer(tx, "test", "wkt", "wkt", null));
}
private Layer testLayerSignature(String signature, Function layerMaker) {
@@ -100,7 +100,7 @@ private void inTx(Consumer txFunction) {
public void testDynamicLayer() {
Layer layer = testLayerSignature(
"EditableLayer(name='test', encoder=WKTGeometryEncoder(geom='wkt', bbox='bbox'))",
- tx -> spatial.getOrCreateEditableLayer(tx, "test", "wkt", "wkt"));
+ tx -> spatial.getOrCreateEditableLayer(tx, "test", "wkt", "wkt", null));
inTx(tx -> {
DynamicLayer dynamic = spatial.asDynamicLayer(tx, layer);
assertEquals("EditableLayer(name='test', encoder=WKTGeometryEncoder(geom='wkt', bbox='bbox'))",
diff --git a/src/test/java/org/neo4j/gis/spatial/LayersTest.java b/src/test/java/org/neo4j/gis/spatial/LayersTest.java
index 7eca583f..c3bb3a40 100644
--- a/src/test/java/org/neo4j/gis/spatial/LayersTest.java
+++ b/src/test/java/org/neo4j/gis/spatial/LayersTest.java
@@ -92,7 +92,7 @@ public void testBasicLayerOperations() {
assertNull(layer);
});
inTx(tx -> {
- Layer layer = spatial.createWKBLayer(tx, layerName);
+ Layer layer = spatial.createWKBLayer(tx, layerName, null);
assertNotNull(layer);
assertThat("Should be a default layer", layer instanceof DefaultLayer);
});
@@ -128,7 +128,7 @@ private void testPointLayer(Class extends LayerIndexReader> indexClass,
new IndexManager((GraphDatabaseAPI) graphDb, SecurityContext.AUTH_DISABLED));
inTx(tx -> {
EditableLayer layer = (EditableLayer) spatial.createLayer(tx, layerName, encoderClass,
- EditableLayerImpl.class, indexClass, null);
+ EditableLayerImpl.class, indexClass, null, null);
assertNotNull(layer);
});
inTx(tx -> {
@@ -178,7 +178,7 @@ private void testDeleteGeometry(Class extends GeometryEncoder> encoderClass) {
new IndexManager((GraphDatabaseAPI) graphDb, SecurityContext.AUTH_DISABLED));
inTx(tx -> {
EditableLayer layer = (EditableLayer) spatial.createLayer(tx, layerName, encoderClass,
- EditableLayerImpl.class, null, null);
+ EditableLayerImpl.class, null, null, null);
assertNotNull(layer);
});
inTx(tx -> {
@@ -197,7 +197,7 @@ public void testEditableLayer() {
SpatialDatabaseService spatial = new SpatialDatabaseService(
new IndexManager((GraphDatabaseAPI) graphDb, SecurityContext.AUTH_DISABLED));
inTx(tx -> {
- EditableLayer layer = spatial.getOrCreateEditableLayer(tx, layerName);
+ EditableLayer layer = spatial.getOrCreateEditableLayer(tx, layerName, null, null);
assertNotNull(layer);
});
inTx(tx -> {
@@ -234,7 +234,7 @@ public void testSnapToLine() {
SpatialDatabaseService spatial = new SpatialDatabaseService(
new IndexManager((GraphDatabaseAPI) graphDb, SecurityContext.AUTH_DISABLED));
inTx(tx -> {
- EditableLayer layer = spatial.getOrCreateEditableLayer(tx, "roads");
+ EditableLayer layer = spatial.getOrCreateEditableLayer(tx, "roads", null, null);
Coordinate crossing_bygg_forstadsgatan = new Coordinate(13.0171471, 55.6074148);
Coordinate[] waypoints_forstadsgatan = {new Coordinate(13.0201511, 55.6066846),
crossing_bygg_forstadsgatan};
@@ -272,7 +272,7 @@ private String testSpecificEditableLayer(String layerName, Class extends Geome
SpatialDatabaseService spatial = new SpatialDatabaseService(
new IndexManager((GraphDatabaseAPI) graphDb, SecurityContext.AUTH_DISABLED));
inTx(tx -> {
- Layer layer = spatial.createLayer(tx, layerName, geometryEncoderClass, layerClass);
+ Layer layer = spatial.createLayer(tx, layerName, geometryEncoderClass, layerClass, null);
assertNotNull(layer);
assertInstanceOf(EditableLayer.class, layer, "Should be an editable layer");
});
@@ -350,7 +350,7 @@ public void testIndexAccessAfterBulkInsertion() {
// GraphDatabaseService db = new GraphDatabaseFactory().newEmbeddedDatabase(dbPath.getCanonicalPath());
SpatialDatabaseService spatial = new SpatialDatabaseService(
new IndexManager((GraphDatabaseAPI) graphDb, SecurityContext.AUTH_DISABLED));
- inTx(tx -> spatial.getOrCreateSimplePointLayer(tx, "Coordinates", "rtree", "lat", "lon"));
+ inTx(tx -> spatial.getOrCreateSimplePointLayer(tx, "Coordinates", "rtree", "lat", "lon", null));
Random rand = new Random();
diff --git a/src/test/java/org/neo4j/gis/spatial/OsmAnalysisTest.java b/src/test/java/org/neo4j/gis/spatial/OsmAnalysisTest.java
index b64bbeac..3bc5ed65 100644
--- a/src/test/java/org/neo4j/gis/spatial/OsmAnalysisTest.java
+++ b/src/test/java/org/neo4j/gis/spatial/OsmAnalysisTest.java
@@ -353,7 +353,7 @@ private static SortedMap exportPoints(Transaction tx, String laye
}
EditableLayerImpl layer = (EditableLayerImpl) spatialService.createLayer(tx, name,
- WKBGeometryEncoder.class, EditableLayerImpl.class);
+ WKBGeometryEncoder.class, EditableLayerImpl.class, "");
layer.setExtraPropertyNames(
new String[]{"user_id", "user_name", "year", "month", "dayOfMonth", "weekOfYear"}, tx);
diff --git a/src/test/java/org/neo4j/gis/spatial/RTreeBulkInsertTest.java b/src/test/java/org/neo4j/gis/spatial/RTreeBulkInsertTest.java
index c0019de6..de9177af 100644
--- a/src/test/java/org/neo4j/gis/spatial/RTreeBulkInsertTest.java
+++ b/src/test/java/org/neo4j/gis/spatial/RTreeBulkInsertTest.java
@@ -179,7 +179,7 @@ private EditableLayer getOrCreateSimplePointLayer(String name, String index, Str
CoordinateReferenceSystem crs = DefaultEngineeringCRS.GENERIC_2D;
try (Transaction tx = db.beginTx()) {
SpatialDatabaseService sdbs = spatial();
- EditableLayer layer = sdbs.getOrCreateSimplePointLayer(tx, name, index, xProperty, yProperty);
+ EditableLayer layer = sdbs.getOrCreateSimplePointLayer(tx, name, index, xProperty, yProperty, null);
layer.setCoordinateReferenceSystem(tx, crs);
tx.commit();
return layer;
@@ -1255,7 +1255,7 @@ public void shouldPerformRTreeBulkInsertion() {
System.out.println("BulkLoadingTestRun " + j);
try (Transaction tx = db.beginTx()) {
- EditableLayer layer = sdbs.getOrCreateSimplePointLayer(tx, "BulkLoader", "rtree", "lon", "lat");
+ EditableLayer layer = sdbs.getOrCreateSimplePointLayer(tx, "BulkLoader", "rtree", "lon", "lat", null);
List coords = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
Node n = tx.createNode(Label.label("Coordinate"));
diff --git a/src/test/java/org/neo4j/gis/spatial/TestOSMImportBase.java b/src/test/java/org/neo4j/gis/spatial/TestOSMImportBase.java
index 4f97943f..cdb8bd58 100644
--- a/src/test/java/org/neo4j/gis/spatial/TestOSMImportBase.java
+++ b/src/test/java/org/neo4j/gis/spatial/TestOSMImportBase.java
@@ -66,7 +66,7 @@ protected static void checkOSMLayer(GraphDatabaseService db, String layerName) t
SpatialDatabaseService spatial = new SpatialDatabaseService(
new IndexManager((GraphDatabaseAPI) db, SecurityContext.AUTH_DISABLED));
OSMLayer layer = (OSMLayer) spatial.getOrCreateLayer(tx, layerName, OSMGeometryEncoder.class,
- OSMLayer.class);
+ OSMLayer.class, null);
Assertions.assertNotNull(layer.getIndex(), "OSM Layer index should not be null");
Assertions.assertNotNull(layer.getIndex().getBoundingBox(tx),
"OSM Layer index envelope should not be null");
diff --git a/src/test/java/org/neo4j/gis/spatial/TestRemove.java b/src/test/java/org/neo4j/gis/spatial/TestRemove.java
index d0daaa3f..2eda1ea8 100644
--- a/src/test/java/org/neo4j/gis/spatial/TestRemove.java
+++ b/src/test/java/org/neo4j/gis/spatial/TestRemove.java
@@ -37,7 +37,7 @@ public void testAddMoreThanMaxNodeRefThenDeleteAll() {
new IndexManager((GraphDatabaseAPI) graphDb(), SecurityContext.AUTH_DISABLED));
try (Transaction tx = graphDb().beginTx()) {
- spatial.createLayer(tx, layerName, WKTGeometryEncoder.class, EditableLayerImpl.class);
+ spatial.createLayer(tx, layerName, WKTGeometryEncoder.class, EditableLayerImpl.class, "");
tx.commit();
}
diff --git a/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java b/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java
index f04c6b22..6995c7ec 100644
--- a/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java
+++ b/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java
@@ -53,7 +53,7 @@ public void testSearchClosestWithShortLongLineStrings() throws ParseException {
Geometry longLineString;
Geometry point;
try (Transaction tx = graphDb().beginTx()) {
- EditableLayer layer = spatial.getOrCreateEditableLayer(tx, layerName, "WKT");
+ EditableLayer layer = spatial.getOrCreateEditableLayer(tx, layerName, "WKT", null);
WKTReader wkt = new WKTReader(layer.getGeometryFactory());
shortLineString = wkt.read("LINESTRING(16.3493032 48.199882,16.3479487 48.1997337)");
longLineString = wkt.read(
diff --git a/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java b/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java
index 964da8de..aca18f19 100644
--- a/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java
+++ b/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java
@@ -48,7 +48,7 @@ public void testJTSLinearRef() {
new IndexManager((GraphDatabaseAPI) graphDb(), SecurityContext.AUTH_DISABLED));
Geometry geometry;
try (Transaction tx = graphDb().beginTx()) {
- EditableLayer layer = spatial.getOrCreateEditableLayer(tx, "jts");
+ EditableLayer layer = spatial.getOrCreateEditableLayer(tx, "jts", null, null);
Coordinate[] coordinates = new Coordinate[]{new Coordinate(0, 0), new Coordinate(0, 1),
new Coordinate(1, 1)};
geometry = layer.getGeometryFactory().createLineString(coordinates);
@@ -145,7 +145,7 @@ public void testSnapping() throws Exception {
OSMDataset.fromLayer(tx, osmLayer); // cache for future usage below
GeometryFactory factory = osmLayer.getGeometryFactory();
EditableLayerImpl resultsLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx,
- "testSnapping_results");
+ "testSnapping_results", null, null);
String[] fieldsNames = new String[]{"snap-id", "description", "distance"};
resultsLayer.setExtraPropertyNames(fieldsNames, tx);
Point point = factory.createPoint(new Coordinate(12.9777, 56.0555));
diff --git a/src/test/java/org/neo4j/gis/spatial/index/LayerIndexTestBase.java b/src/test/java/org/neo4j/gis/spatial/index/LayerIndexTestBase.java
index 3f42774c..36e1237b 100644
--- a/src/test/java/org/neo4j/gis/spatial/index/LayerIndexTestBase.java
+++ b/src/test/java/org/neo4j/gis/spatial/index/LayerIndexTestBase.java
@@ -170,7 +170,7 @@ public void shouldCreateAndFindAndDeleteIndexViaLayer() {
private SimplePointLayer makeTestPointLayer() {
try (Transaction tx = graph.beginTx()) {
- SimplePointLayer layer = spatial.createPointLayer(tx, "test", getIndexClass(), getEncoderClass());
+ SimplePointLayer layer = spatial.createPointLayer(tx, "test", getIndexClass(), getEncoderClass(), null);
tx.commit();
return layer;
}
diff --git a/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesDocTest.java b/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesDocTest.java
index 58d4313d..7cdfa91d 100644
--- a/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesDocTest.java
+++ b/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesDocTest.java
@@ -946,7 +946,7 @@ private static void load() throws Exception {
loadTestOsmData("two-street.osm", 100);
osmLayer = spatial.getLayer(tx, "two-street.osm");
- boxesLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx, "boxes");
+ boxesLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx, "boxes", null, null);
boxesLayer.setExtraPropertyNames(new String[]{"name"}, tx);
boxesLayer.setCoordinateReferenceSystem(tx, DefaultEngineeringCRS.GENERIC_2D);
WKTReader reader = new WKTReader(boxesLayer.getGeometryFactory());
@@ -957,19 +957,19 @@ private static void load() throws Exception {
reader.read("POLYGON ((2 3, 2 5, 6 5, 6 3, 2 3))"),
new String[]{"name"}, new Object[]{"B"});
- concaveLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx, "concave");
+ concaveLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx, "concave", null, null);
concaveLayer.setCoordinateReferenceSystem(tx, DefaultEngineeringCRS.GENERIC_2D);
reader = new WKTReader(concaveLayer.getGeometryFactory());
concaveLayer.add(tx, reader.read("POLYGON ((0 0, 2 5, 0 10, 10 10, 10 0, 0 0))"));
- intersectionLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx, "intersection");
+ intersectionLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx, "intersection", null, null);
intersectionLayer.setCoordinateReferenceSystem(tx, DefaultEngineeringCRS.GENERIC_2D);
reader = new WKTReader(intersectionLayer.getGeometryFactory());
intersectionLayer.add(tx, reader.read("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))"));
intersectionLayer.add(tx, reader.read("POLYGON ((4 4, 4 10, 10 10, 10 4, 4 4))"));
intersectionLayer.add(tx, reader.read("POLYGON ((2 2, 2 6, 6 6, 6 2, 2 2))"));
- equalLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx, "equal");
+ equalLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx, "equal", null, null);
equalLayer.setExtraPropertyNames(new String[]{"id", "name"}, tx);
equalLayer.setCoordinateReferenceSystem(tx, DefaultEngineeringCRS.GENERIC_2D);
reader = new WKTReader(intersectionLayer.getGeometryFactory());
@@ -984,7 +984,7 @@ private static void load() throws Exception {
reader.read("POLYGON ((0 0, 0 2, 0 4, 0 5, 5 5, 5 3, 5 2, 5 0, 0 0))"),
new String[]{"id", "name"}, new Object[]{4, "topo equal"});
- linesLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx, "lines");
+ linesLayer = (EditableLayerImpl) spatial.getOrCreateEditableLayer(tx, "lines", null, null);
linesLayer.setCoordinateReferenceSystem(tx, DefaultEngineeringCRS.GENERIC_2D);
reader = new WKTReader(intersectionLayer.getGeometryFactory());
linesLayer.add(tx, reader.read("LINESTRING (12 26, 15 27, 18 32, 20 38, 23 34)"));
diff --git a/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java b/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java
index a8562200..6a1811c6 100644
--- a/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java
+++ b/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java
@@ -141,15 +141,13 @@ public static void registerProceduresAndFunctions(GraphDatabaseService db, Class
private static Layer makeLayerOfVariousTypes(SpatialDatabaseService spatial, Transaction tx, String name,
int index) {
- switch (index % 3) {
- case 0:
- return spatial.getOrCreateSimplePointLayer(tx, name, SpatialDatabaseService.RTREE_INDEX_NAME, "x", "y");
- case 1:
- return spatial.getOrCreateNativePointLayer(tx, name, SpatialDatabaseService.RTREE_INDEX_NAME,
- "location");
- default:
- return spatial.getOrCreateDefaultLayer(tx, name);
- }
+ return switch (index % 3) {
+ case 0 -> spatial.getOrCreateSimplePointLayer(tx, name, SpatialDatabaseService.RTREE_INDEX_NAME, "x", "y",
+ null);
+ case 1 -> spatial.getOrCreateNativePointLayer(tx, name, SpatialDatabaseService.RTREE_INDEX_NAME, "location",
+ null);
+ default -> spatial.getOrCreateDefaultLayer(tx, name, null);
+ };
}
private void makeOldSpatialModel(Transaction tx, String... layers) {
@@ -568,7 +566,7 @@ public void list_spatial_procedures() {
procs.get("spatial.layers"));
assertEquals("spatial.layer(name :: STRING) :: (node :: NODE)", procs.get("spatial.layer"));
assertEquals(
- "spatial.addLayer(name :: STRING, type :: STRING, encoderConfig :: STRING) :: (node :: NODE)",
+ "spatial.addLayer(name :: STRING, type :: STRING, encoderConfig :: STRING, indexConfig = :: STRING) :: (node :: NODE)",
procs.get("spatial.addLayer"));
assertEquals("spatial.addNode(layerName :: STRING, node :: NODE) :: (node :: NODE)",
procs.get("spatial.addNode"));
@@ -868,6 +866,41 @@ public void add_many_nodes_to_the_native_point_layer_using_addNodes() {
testRemoveNodes("native_poi", count);
}
+ @Test
+ public void add_node_to_multiple_indexes_in_chunks() {
+ // Playing with this number in both tests leads to rough benchmarking of the addNode/addNodes comparison
+ int count = 100;
+ execute("""
+ 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 } )
+ })""", Map.of("count", count));
+ execute("CALL spatial.addLayer('point1','NativePoint','point1:point1BB', '{\"referenceRelationshipType\": \"RTREE_P1_TYPE\"}')");
+ execute("CALL spatial.addLayer('point2','NativePoint','point2:point2BB', '{\"referenceRelationshipType\": \"RTREE_P2_TYPE\"}')");
+ db.executeTransactionally("""
+ 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
+ """);
+ db.executeTransactionally("""
+ 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
+ """);
+ }
+
@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