Skip to content

Commit 7107e00

Browse files
authored
Improve Feature-Attribute handling (#442)
This commit refactors feature-attribute handling across the plugin by replacing `String[]`-based APIs with `Map<String,Class<?>>` and `Set<String>` for extra properties, adding dynamic attribute discovery/storage in layers, and updating tests and encoders to use the new API. - Migrate all `getExtraPropertyNames` and related APIs to `getExtraProperties` returning a `Map<String,Class<?>>`. - Update `CopyDatabaseRecordProperties`, `GeoPipeline`, `GeoPipeFlow`, and other classes to use `Set<String>` instead of `String[]`. - Introduce dynamic attribute metadata capture in `EditableLayerImpl` and deprecate manual `setFeatureAttributes`.
1 parent 239be8e commit 7107e00

39 files changed

+339
-184
lines changed

docs/docs/modules/ROOT/partials/generated/api/index.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ Removes the given node from the layer, returns the geometry-node
169169

170170
Removes the given nodes from the layer, returns the count of nodes removed
171171
|label:procedure[]
172-
|xref:api/spatial/spatial.setFeatureAttributes.adoc[spatial.setFeatureAttributes icon:book[]]
172+
|xref:api/spatial/spatial.setFeatureAttributes.adoc[spatial.setFeatureAttributes icon:book[]] label:deprecated[]
173173

174174
Sets the feature attributes of the given layer
175175
|label:procedure[]

docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureAttributes-examples.adoc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ CALL spatial.getFeatureAttributes('geom')
6565

6666
.Result
6767

68-
[opts="header",cols="1"]
68+
[opts="header",cols="2"]
6969
|===
70-
|name
71-
|name
72-
|type
73-
|color
70+
|className|name
71+
|java.lang.String|color
72+
|java.lang.String|name
73+
|java.lang.String|type
7474
|===
7575

docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureAttributes.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Returns feature attributes of the given layer
1212

1313
[source]
1414
----
15-
spatial.getFeatureAttributes(name :: STRING) :: (name :: STRING)
15+
spatial.getFeatureAttributes(name :: STRING) :: (name :: STRING, className :: STRING)
1616
----
1717

1818
== Input parameters
@@ -30,5 +30,6 @@ a|The name of the layer
3030
|===
3131
|Name|Type|Description
3232
|name|STRING|
33+
|className|STRING|
3334
|===
3435

docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.setFeatureAttributes-examples.adoc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ CALL spatial.getFeatureAttributes('geom')
6565

6666
.Result
6767

68-
[opts="header",cols="1"]
68+
[opts="header",cols="2"]
6969
|===
70-
|name
71-
|name
72-
|type
73-
|color
70+
|className|name
71+
|java.lang.String|color
72+
|java.lang.String|name
73+
|java.lang.String|type
7474
|===
7575

docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.setFeatureAttributes.adoc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33

44
:description: This section contains reference documentation for the spatial.setFeatureAttributes procedure.
55

6-
label:procedure[]
6+
label:procedure[] label:deprecated[]
77

88
[.emphasis]
99
Sets the feature attributes of the given layer
1010

11+
[WARNING]
12+
====
13+
14+
This procedure is deprecated by: feature attributes are now automatically discovered when a new node is added to the index
15+
====
16+
1117
== Signature
1218

1319
[source]

server-plugin/src/main/java/org/geotools/data/neo4j/Neo4jFeatureBuilder.java

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@
2020
package org.geotools.data.neo4j;
2121

2222
import java.util.ArrayList;
23-
import java.util.Arrays;
24-
import java.util.HashSet;
23+
import java.util.Collections;
2524
import java.util.List;
2625
import java.util.Map;
27-
import java.util.Set;
2826
import org.geotools.api.feature.simple.SimpleFeature;
2927
import org.geotools.api.feature.simple.SimpleFeatureType;
3028
import org.geotools.api.feature.type.AttributeDescriptor;
@@ -53,30 +51,27 @@ public class Neo4jFeatureBuilder {
5351

5452
private static final String FEATURE_PROP_GEOM = "the_geom";
5553
private final SimpleFeatureBuilder builder;
56-
private final List<String> extraPropertyNames;
54+
private final Map<String, Class<?>> extraProperties;
5755

58-
public Neo4jFeatureBuilder(SimpleFeatureType sft, List<String> extraPropertyNames) {
56+
public Neo4jFeatureBuilder(SimpleFeatureType sft, Map<String, Class<?>> extraProperties) {
5957
this.builder = new SimpleFeatureBuilder(sft);
60-
this.extraPropertyNames = extraPropertyNames;
58+
this.extraProperties = extraProperties == null ? Collections.emptyMap() : extraProperties;
6159
}
6260

6361
/**
6462
* If it is necessary to look up the layer type with a transaction, use this factory method to make the feature
6563
* builder
6664
*/
6765
public static Neo4jFeatureBuilder fromLayer(Transaction tx, Layer layer) {
68-
return new Neo4jFeatureBuilder(getTypeFromLayer(tx, layer), Arrays.asList(layer.getExtraPropertyNames(tx)));
66+
return new Neo4jFeatureBuilder(getTypeFromLayer(tx, layer), layer.getExtraProperties(tx));
6967
}
7068

7169
public SimpleFeature buildFeature(String id, Geometry geometry, Map<String, Object> properties) {
7270
builder.reset();
7371
builder.set(FEATURE_PROP_GEOM, geometry);
74-
if (extraPropertyNames != null) {
75-
for (String name : extraPropertyNames) {
76-
builder.set(name, properties.get(name));
77-
}
72+
for (String name : extraProperties.keySet()) {
73+
builder.set(name, properties.get(name));
7874
}
79-
8075
return builder.buildFeature(id);
8176
}
8277

@@ -86,12 +81,12 @@ public SimpleFeature buildFeature(Transaction tx, SpatialRecord rec) {
8681

8782
public static SimpleFeatureType getTypeFromLayer(Transaction tx, Layer layer) {
8883
return getType(layer.getName(), layer.getGeometryType(tx), layer.getCoordinateReferenceSystem(tx),
89-
layer.getExtraPropertyNames(tx));
84+
layer.getExtraProperties(tx));
9085
}
9186

9287
public static SimpleFeatureType getType(String name, Integer geometryTypeId, CoordinateReferenceSystem crs,
93-
String[] extraPropertyNames) {
94-
List<AttributeDescriptor> types = readAttributes(geometryTypeId, crs, extraPropertyNames);
88+
Map<String, Class<?>> extraProperties) {
89+
List<AttributeDescriptor> types = readAttributes(geometryTypeId, crs, extraProperties);
9590

9691
// find Geometry type
9792
SimpleFeatureType parent = null;
@@ -121,7 +116,7 @@ public static SimpleFeatureType getType(String name, Integer geometryTypeId, Coo
121116
}
122117

123118
private static List<AttributeDescriptor> readAttributes(Integer geometryTypeId, CoordinateReferenceSystem crs,
124-
String[] extraPropertyNames) {
119+
Map<String, Class<?>> extraProperties) {
125120
Class<? extends Geometry> geometryClass = SpatialDatabaseService.convertGeometryTypeToJtsClass(geometryTypeId);
126121

127122
AttributeTypeBuilder build = new AttributeTypeBuilder();
@@ -135,21 +130,13 @@ private static List<AttributeDescriptor> readAttributes(Integer geometryTypeId,
135130
List<AttributeDescriptor> attributes = new ArrayList<>();
136131
attributes.add(build.buildDescriptor(BasicFeatureTypes.GEOMETRY_ATTRIBUTE_NAME, geometryType));
137132

138-
if (extraPropertyNames != null) {
139-
Set<String> usedNames = new HashSet<>();
133+
if (extraProperties != null) {
140134
// record names in case of duplicates
141-
usedNames.add(BasicFeatureTypes.GEOMETRY_ATTRIBUTE_NAME);
142-
143-
for (String propertyName : extraPropertyNames) {
144-
if (!usedNames.contains(propertyName)) {
145-
usedNames.add(propertyName);
146-
147-
build.setNillable(true);
148-
build.setBinding(String.class);
149-
150-
attributes.add(build.buildDescriptor(propertyName));
151-
}
152-
}
135+
extraProperties.forEach((propertyName, aClass) -> {
136+
build.setNillable(true);
137+
build.setBinding(aClass);
138+
attributes.add(build.buildDescriptor(propertyName));
139+
});
153140
}
154141

155142
return attributes;

server-plugin/src/main/java/org/geotools/data/neo4j/Neo4jSpatialDataStore.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public void clearCache() {
162162
protected ContentFeatureSource createFeatureSource(ContentEntry contentEntry) throws IOException {
163163
Layer layer;
164164
ArrayList<SpatialDatabaseRecord> records = new ArrayList<>();
165-
String[] extraPropertyNames;
165+
Map<String, Class<?>> extraProperties;
166166
try (Transaction tx = database.beginTx()) {
167167
layer = spatialDatabase.getLayer(tx, contentEntry.getTypeName(), false);
168168
SearchRecords results = layer.getIndex().search(tx, new SearchAll());
@@ -171,11 +171,11 @@ protected ContentFeatureSource createFeatureSource(ContentEntry contentEntry) th
171171
for (SpatialDatabaseRecord record : results) {
172172
records.add(record);
173173
}
174-
extraPropertyNames = layer.getExtraPropertyNames(tx);
174+
extraProperties = layer.getExtraProperties(tx);
175175
tx.commit();
176176
}
177177
Neo4jSpatialFeatureSource source = new Neo4jSpatialFeatureSource(contentEntry, database, layer,
178-
buildFeatureType(contentEntry.getTypeName()), records, extraPropertyNames);
178+
buildFeatureType(contentEntry.getTypeName()), records, extraProperties.keySet());
179179
if (layer instanceof EditableLayer) {
180180
return new Neo4jSpatialFeatureStore(contentEntry, database, (EditableLayer) layer, source);
181181
}

server-plugin/src/main/java/org/geotools/data/neo4j/Neo4jSpatialFeatureSource.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import java.util.Iterator;
2323
import java.util.NoSuchElementException;
24+
import java.util.Set;
2425
import org.geotools.api.data.FeatureReader;
2526
import org.geotools.api.data.Query;
2627
import org.geotools.api.feature.simple.SimpleFeature;
@@ -51,10 +52,10 @@ public class Neo4jSpatialFeatureSource extends ContentFeatureSource {
5152
private final SimpleFeatureType featureType;
5253
private final SimpleFeatureBuilder builder;
5354
private final Iterable<SpatialDatabaseRecord> results;
54-
private final String[] extraPropertyNames;
55+
private final Set<String> extraPropertyNames;
5556

5657
public Neo4jSpatialFeatureSource(ContentEntry contentEntry, GraphDatabaseService database, Layer layer,
57-
SimpleFeatureType featureType, Iterable<SpatialDatabaseRecord> results, String[] extraPropertyNames) {
58+
SimpleFeatureType featureType, Iterable<SpatialDatabaseRecord> results, Set<String> extraPropertyNames) {
5859
super(contentEntry, Query.ALL);
5960
this.database = database;
6061
this.layer = layer;

server-plugin/src/main/java/org/neo4j/gis/spatial/AbstractGeometryEncoder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020
package org.neo4j.gis.spatial;
2121

22+
import java.util.Set;
2223
import org.apache.commons.lang3.ArrayUtils;
2324
import org.locationtech.jts.geom.Geometry;
2425
import org.locationtech.jts.geom.GeometryFactory;
@@ -127,6 +128,11 @@ public String getSignature() {
127128
return "GeometryEncoder(bbox='" + bboxProperty + "')";
128129
}
129130

131+
@Override
132+
public Set<String> getEncoderProperties() {
133+
return Set.of(bboxProperty);
134+
}
135+
130136
// Attributes
131137

132138
protected Layer layer;

server-plugin/src/main/java/org/neo4j/gis/spatial/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public interface Constants {
3030

3131
String PROP_BBOX = "bbox";
3232
String PROP_LAYER = "layer";
33+
String PROP_PREFIX_EXTRA_PROP_V2 = "extraProp.";
3334
String PROP_LAYERNODEEXTRAPROPS = "layerprops";
3435
String PROP_CRS = "layercrs";
3536
String PROP_GEOMENCODER = "geomencoder";

0 commit comments

Comments
 (0)