Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/modules/ROOT/partials/generated/api/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ Removes the given node from the layer, returns the geometry-node

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

Sets the feature attributes of the given layer
|label:procedure[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ CALL spatial.getFeatureAttributes('geom')

.Result

[opts="header",cols="1"]
[opts="header",cols="2"]
|===
|name
|name
|type
|color
|className|name
|java.lang.String|color
|java.lang.String|name
|java.lang.String|type
|===

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Returns feature attributes of the given layer

[source]
----
spatial.getFeatureAttributes(name :: STRING) :: (name :: STRING)
spatial.getFeatureAttributes(name :: STRING) :: (name :: STRING, className :: STRING)
----

== Input parameters
Expand All @@ -30,5 +30,6 @@ a|The name of the layer
|===
|Name|Type|Description
|name|STRING|
|className|STRING|
|===

Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ CALL spatial.getFeatureAttributes('geom')

.Result

[opts="header",cols="1"]
[opts="header",cols="2"]
|===
|name
|name
|type
|color
|className|name
|java.lang.String|color
|java.lang.String|name
|java.lang.String|type
|===

Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@

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

label:procedure[]
label:procedure[] label:deprecated[]

[.emphasis]
Sets the feature attributes of the given layer

[WARNING]
====

This procedure is deprecated by: feature attributes are now automatically discovered when a new node is added to the index
====

== Signature

[source]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@
package org.geotools.data.neo4j;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.api.feature.type.AttributeDescriptor;
Expand Down Expand Up @@ -53,30 +51,27 @@ public class Neo4jFeatureBuilder {

private static final String FEATURE_PROP_GEOM = "the_geom";
private final SimpleFeatureBuilder builder;
private final List<String> extraPropertyNames;
private final Map<String, Class<?>> extraProperties;

public Neo4jFeatureBuilder(SimpleFeatureType sft, List<String> extraPropertyNames) {
public Neo4jFeatureBuilder(SimpleFeatureType sft, Map<String, Class<?>> extraProperties) {
this.builder = new SimpleFeatureBuilder(sft);
this.extraPropertyNames = extraPropertyNames;
this.extraProperties = extraProperties == null ? Collections.emptyMap() : extraProperties;
}

/**
* If it is necessary to look up the layer type with a transaction, use this factory method to make the feature
* builder
*/
public static Neo4jFeatureBuilder fromLayer(Transaction tx, Layer layer) {
return new Neo4jFeatureBuilder(getTypeFromLayer(tx, layer), Arrays.asList(layer.getExtraPropertyNames(tx)));
return new Neo4jFeatureBuilder(getTypeFromLayer(tx, layer), layer.getExtraProperties(tx));
}

public SimpleFeature buildFeature(String id, Geometry geometry, Map<String, Object> properties) {
builder.reset();
builder.set(FEATURE_PROP_GEOM, geometry);
if (extraPropertyNames != null) {
for (String name : extraPropertyNames) {
builder.set(name, properties.get(name));
}
for (String name : extraProperties.keySet()) {
builder.set(name, properties.get(name));
}

return builder.buildFeature(id);
}

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

public static SimpleFeatureType getTypeFromLayer(Transaction tx, Layer layer) {
return getType(layer.getName(), layer.getGeometryType(tx), layer.getCoordinateReferenceSystem(tx),
layer.getExtraPropertyNames(tx));
layer.getExtraProperties(tx));
}

public static SimpleFeatureType getType(String name, Integer geometryTypeId, CoordinateReferenceSystem crs,
String[] extraPropertyNames) {
List<AttributeDescriptor> types = readAttributes(geometryTypeId, crs, extraPropertyNames);
Map<String, Class<?>> extraProperties) {
List<AttributeDescriptor> types = readAttributes(geometryTypeId, crs, extraProperties);

// find Geometry type
SimpleFeatureType parent = null;
Expand Down Expand Up @@ -121,7 +116,7 @@ public static SimpleFeatureType getType(String name, Integer geometryTypeId, Coo
}

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

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

if (extraPropertyNames != null) {
Set<String> usedNames = new HashSet<>();
if (extraProperties != null) {
// record names in case of duplicates
usedNames.add(BasicFeatureTypes.GEOMETRY_ATTRIBUTE_NAME);

for (String propertyName : extraPropertyNames) {
if (!usedNames.contains(propertyName)) {
usedNames.add(propertyName);

build.setNillable(true);
build.setBinding(String.class);

attributes.add(build.buildDescriptor(propertyName));
}
}
extraProperties.forEach((propertyName, aClass) -> {
build.setNillable(true);
build.setBinding(aClass);
attributes.add(build.buildDescriptor(propertyName));
});
}

return attributes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public void clearCache() {
protected ContentFeatureSource createFeatureSource(ContentEntry contentEntry) throws IOException {
Layer layer;
ArrayList<SpatialDatabaseRecord> records = new ArrayList<>();
String[] extraPropertyNames;
Map<String, Class<?>> extraProperties;
try (Transaction tx = database.beginTx()) {
layer = spatialDatabase.getLayer(tx, contentEntry.getTypeName(), false);
SearchRecords results = layer.getIndex().search(tx, new SearchAll());
Expand All @@ -171,11 +171,11 @@ protected ContentFeatureSource createFeatureSource(ContentEntry contentEntry) th
for (SpatialDatabaseRecord record : results) {
records.add(record);
}
extraPropertyNames = layer.getExtraPropertyNames(tx);
extraProperties = layer.getExtraProperties(tx);
tx.commit();
}
Neo4jSpatialFeatureSource source = new Neo4jSpatialFeatureSource(contentEntry, database, layer,
buildFeatureType(contentEntry.getTypeName()), records, extraPropertyNames);
buildFeatureType(contentEntry.getTypeName()), records, extraProperties.keySet());
if (layer instanceof EditableLayer) {
return new Neo4jSpatialFeatureStore(contentEntry, database, (EditableLayer) layer, source);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import org.geotools.api.data.FeatureReader;
import org.geotools.api.data.Query;
import org.geotools.api.feature.simple.SimpleFeature;
Expand Down Expand Up @@ -51,10 +52,10 @@ public class Neo4jSpatialFeatureSource extends ContentFeatureSource {
private final SimpleFeatureType featureType;
private final SimpleFeatureBuilder builder;
private final Iterable<SpatialDatabaseRecord> results;
private final String[] extraPropertyNames;
private final Set<String> extraPropertyNames;

public Neo4jSpatialFeatureSource(ContentEntry contentEntry, GraphDatabaseService database, Layer layer,
SimpleFeatureType featureType, Iterable<SpatialDatabaseRecord> results, String[] extraPropertyNames) {
SimpleFeatureType featureType, Iterable<SpatialDatabaseRecord> results, Set<String> extraPropertyNames) {
super(contentEntry, Query.ALL);
this.database = database;
this.layer = layer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
package org.neo4j.gis.spatial;

import java.util.Set;
import org.apache.commons.lang3.ArrayUtils;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
Expand Down Expand Up @@ -127,6 +128,11 @@ public String getSignature() {
return "GeometryEncoder(bbox='" + bboxProperty + "')";
}

@Override
public Set<String> getEncoderProperties() {
return Set.of(bboxProperty);
}

// Attributes

protected Layer layer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public interface Constants {

String PROP_BBOX = "bbox";
String PROP_LAYER = "layer";
String PROP_PREFIX_EXTRA_PROP_V2 = "extraProp.";
String PROP_LAYERNODEEXTRAPROPS = "layerprops";
String PROP_CRS = "layercrs";
String PROP_GEOMENCODER = "geomencoder";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@
import static org.neo4j.gis.spatial.Constants.PROP_INDEX_CLASS;
import static org.neo4j.gis.spatial.Constants.PROP_INDEX_CONFIG;
import static org.neo4j.gis.spatial.Constants.PROP_LAYERNODEEXTRAPROPS;
import static org.neo4j.gis.spatial.Constants.PROP_PREFIX_EXTRA_PROP_V2;
import static org.neo4j.gis.spatial.Constants.PROP_TYPE;

import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.Nonnull;
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.locationtech.jts.geom.Geometry;
Expand Down Expand Up @@ -69,6 +72,8 @@ public class DefaultLayer implements Layer, SpatialDataset {

/**
* The constructor is protected because we should not construct this class
* directly but use the factory methods to create Layers based on
* configurations
* directly but use the factory methods to create Layers based on configurations
*/
protected DefaultLayer() {
Expand Down Expand Up @@ -178,16 +183,35 @@ public Integer getGeometryType(Transaction tx) {
return geomTypeSearch.firstFoundType;
}

@Nonnull
@Override
public String[] getExtraPropertyNames(Transaction tx) {
public Map<String, Class<?>> getExtraProperties(Transaction tx) {
Node layerNode = getLayerNode(tx);
String[] extraPropertyNames;
var extraProps = new TreeMap<String, Class<?>>();
layerNode.getAllProperties().forEach((name, o) -> {
if (!name.startsWith(PROP_PREFIX_EXTRA_PROP_V2)) {
return;
}
Class<?> clazz = String.class;
if (o instanceof String className) {
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException ignore) {
}
}

var key = name.substring(PROP_PREFIX_EXTRA_PROP_V2.length());
extraProps.put(key, clazz);
});
if (layerNode.hasProperty(PROP_LAYERNODEEXTRAPROPS)) {
extraPropertyNames = (String[]) layerNode.getProperty(PROP_LAYERNODEEXTRAPROPS);
} else {
extraPropertyNames = new String[]{};
Object legacyProps = layerNode.getProperty(PROP_LAYERNODEEXTRAPROPS);
if (legacyProps instanceof String[] props) {
for (String s : props) {
extraProps.putIfAbsent(s, String.class);
}
}
}
return extraPropertyNames;
return extraProps;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.Nonnull;
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.filter.text.cql2.CQLException;
import org.locationtech.jts.geom.GeometryFactory;
Expand Down Expand Up @@ -101,12 +103,17 @@ public SpatialDataset getDataset() {
return parent.getDataset();
}

@Nonnull
@Override
public String[] getExtraPropertyNames(Transaction tx) {
public Map<String, Class<?>> getExtraProperties(Transaction tx) {
if (propertyNames != null && propertyNames.length > 0) {
return propertyNames;
var result = new TreeMap<String, Class<?>>();
for (String propertyName : propertyNames) {
result.put(propertyName, String.class);
}
return result;
}
return parent.getExtraPropertyNames(tx);
return parent.getExtraProperties(tx);
}

private static class PropertyUsageSearch implements SearchFilter {
Expand Down Expand Up @@ -163,16 +170,16 @@ public void restrictLayerProperties(Transaction tx) {
System.out.println("Restricted property names already exists - will be overwritten");
}
System.out.println(
"Before property scan we have " + getExtraPropertyNames(tx).length + " known attributes for layer "
"Before property scan we have " + getExtraProperties(tx).size() + " known attributes for layer "
+ getName());

PropertyUsageSearch search = new PropertyUsageSearch(this);
getIndex().searchIndex(tx, search).count();
setExtraPropertyNames(tx, search.getNames());

System.out.println(
"After property scan of " + search.getNodeCount() + " nodes, we have " + getExtraPropertyNames(
tx).length + " known attributes for layer " + getName());
"After property scan of " + search.getNodeCount() + " nodes, we have " + getExtraProperties(
tx).size() + " known attributes for layer " + getName());
}

public Node configNode(Transaction tx) {
Expand Down
Loading