diff --git a/README.md b/README.md
index 7d999bc..d712a64 100644
--- a/README.md
+++ b/README.md
@@ -283,6 +283,7 @@ Graph configuration properties are prefixed with `gremlin.arangodb.conf.graph`:
| `gremlin.arangodb.conf.graph.name` | ArangoDB graph name | `tinkerpop` |
| `gremlin.arangodb.conf.graph.enableDataDefinition` | Flag to allow data definition changes | `false` |
| `gremlin.arangodb.conf.graph.type` | Graph type: `SIMPLE` or `COMPLEX` | `SIMPLE` |
+| `gremlin.arangodb.conf.graph.labelField` | Label field name | `_label` |
| `gremlin.arangodb.conf.graph.orphanCollections` | List of orphan collections names | - |
| `gremlin.arangodb.conf.graph.edgeDefinitions` | List of edge definitions | - |
@@ -395,6 +396,11 @@ The ArangoDB TinkerPop Provider supports two graph types, which can be configure
From an application perspective, this is the most flexible graph type that is backed by an ArangoDB graph composed of
only 1 vertex collection and 1 edge definition.
+The `label` of each element is stored in a database document field. The label field name is configurable by setting the
+configuration property `graph.labelField`, `_label` by default.
+
+The `SIMPLE` graph type is the default graph type.
+
It has the following advantages:
- It closely matches the Tinkerpop property graph
@@ -407,7 +413,7 @@ It has the following disadvantages:
- All vertex types will be stored in the same vertex collection
- All edge types will be stored in the same edge collection
- It could not leverage the full potential of ArangoDB graph traversal
-- It could require an index on the `_label` field to improve performance
+- It could require an index on the label field to improve performance
Example configuration:
@@ -443,7 +449,11 @@ to `v/foo`).
### COMPLEX Graph Type
The `COMPLEX` graph type is backed by an ArangoDB graph composed potentially of multiple vertex collections and multiple
-edge definitions. It has the following advantages:
+edge definitions.
+
+The `label` of each element is used as name for the related database collection.
+
+It has the following advantages:
- It closely matches the ArangoDB graph structure
- It allows multiple vertex collections and multiple edge collections
@@ -502,10 +512,12 @@ collection, by default named `vertex`. In a `COMPLEX` graph, vertices are stored
Each vertex document contains:
- Standard ArangoDB fields (`_id`, `_key`, `_rev`)
-- The field `_label`
- Vertex properties as document fields
- Meta-properties nested in the nested map `_meta`
+Additionally, in a `SIMPLE` graph:
+- The label field (`_label` by default)
+
For example, the following Java code:
[//]: <> (@formatter:off)
@@ -542,9 +554,11 @@ default named `edge`. In a `COMPLEX` graph, edges are stored in collections name
Each edge document contains:
- Standard ArangoDB edge fields (`_id`, `_key`, `_rev`, `_from`, `_to`)
-- The field `_label`
- Edge properties as document fields
+Additionally, in a `SIMPLE` graph:
+- The label field (`_label` by default)
+
For example, the following Java code:
[//]: <> (@formatter:off)
diff --git a/spotbugs/spotbugs-exclude.xml b/spotbugs/spotbugs-exclude.xml
index 2de7d05..44486ea 100644
--- a/spotbugs/spotbugs-exclude.xml
+++ b/spotbugs/spotbugs-exclude.xml
@@ -30,4 +30,9 @@
+
+
+
+
+
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java
index 0ffcb9f..d26b1f7 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java
@@ -54,7 +54,7 @@ public ArangoDBGraphClient(ArangoDBGraphConfig config, ElementIdFactory idFactor
.orElse(ArangoDefaults.DEFAULT_PROTOCOL);
ObjectMapper mapper = JacksonMapperProvider
.of(ContentTypeFactory.of(protocol))
- .registerModule(new SerdeModule(idFactory, config.graphType));
+ .registerModule(new SerdeModule(idFactory, config));
aqlDeserializer = new AqlDeserializer(graph, mapper);
db = new ArangoDB.Builder()
.loadProperties(config.driverConfig)
@@ -305,7 +305,7 @@ public void updateVertex(ArangoDBVertex vertex) {
public Iterator getVertexNeighbors(ElementId vertexId, Set edgeCollections, Direction direction, String[] labels) {
logger.debug("Get vertex {}:{} Neighbors, in {}, from collections {}", vertexId, direction, config.graphName, edgeCollections);
- String query = ArangoDBQueryBuilder.readVertexNeighbors(config.graphName, direction, config.graphType, labels);
+ String query = ArangoDBQueryBuilder.readVertexNeighbors(config.graphName, direction, config, labels);
Map params = new HashMap<>();
params.put("vertexId", vertexId);
params.put("edgeCollections", edgeCollections);
@@ -317,7 +317,7 @@ public Iterator getVertexNeighbors(ElementId vertexId, Set e
public Iterator getVertexEdges(ElementId vertexId, Set edgeCollections, Direction direction, String[] labels) {
logger.debug("Get vertex {}:{} Edges, in {}, from collections {}", vertexId, direction, config.graphName, edgeCollections);
- String query = ArangoDBQueryBuilder.readVertexEdges(config.graphName, direction, config.graphType, labels);
+ String query = ArangoDBQueryBuilder.readVertexEdges(config.graphName, direction, config, labels);
Map params = new HashMap<>();
params.put("vertexId", vertexId);
params.put("edgeCollections", edgeCollections);
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java
index f2cfc76..4d19585 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java
@@ -22,27 +22,25 @@
import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraphConfig;
import org.apache.tinkerpop.gremlin.structure.Direction;
-import static com.arangodb.tinkerpop.gremlin.utils.Fields.LABEL;
-
public class ArangoDBQueryBuilder {
private ArangoDBQueryBuilder() {
}
- public static String readVertexNeighbors(String graphName, Direction direction, ArangoDBGraphConfig.GraphType type, String[] labels) {
- return oneStepTraversal(graphName, direction, type, labels)
+ public static String readVertexNeighbors(String graphName, Direction direction, ArangoDBGraphConfig config, String[] labels) {
+ return oneStepTraversal(graphName, direction, config, labels)
.append(" RETURN v")
.toString();
}
- public static String readVertexEdges(String graphName, Direction direction, ArangoDBGraphConfig.GraphType type, String[] labels) {
- return oneStepTraversal(graphName, direction, type, labels)
+ public static String readVertexEdges(String graphName, Direction direction, ArangoDBGraphConfig config, String[] labels) {
+ return oneStepTraversal(graphName, direction, config, labels)
.append(" RETURN e")
.toString();
}
- private static StringBuilder oneStepTraversal(String graphName, Direction direction, ArangoDBGraphConfig.GraphType type, String[] labels) {
+ private static StringBuilder oneStepTraversal(String graphName, Direction direction, ArangoDBGraphConfig config, String[] labels) {
StringBuilder query = new StringBuilder()
.append("FOR v, e IN 1..1 ")
.append(toArangoDirection(direction))
@@ -50,8 +48,8 @@ private static StringBuilder oneStepTraversal(String graphName, Direction direct
.append(escape(graphName))
.append(" OPTIONS {edgeCollections: @edgeCollections}");
if (labels.length > 0) {
- if (type == ArangoDBGraphConfig.GraphType.SIMPLE) {
- query.append(" FILTER e." + LABEL + " IN @labels");
+ if (config.graphType == ArangoDBGraphConfig.GraphType.SIMPLE) {
+ query.append(" FILTER e." + config.labelField + " IN @labels");
} else {
query.append(" FILTER PARSE_COLLECTION(e) IN @edgeCollections");
}
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/EdgeDataDeserializer.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/EdgeDataDeserializer.java
index a796fb9..ddda2b7 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/EdgeDataDeserializer.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/EdgeDataDeserializer.java
@@ -19,7 +19,6 @@
import com.arangodb.tinkerpop.gremlin.persistence.EdgeData;
import com.arangodb.tinkerpop.gremlin.persistence.ElementId;
import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraphConfig;
-import com.arangodb.tinkerpop.gremlin.utils.Fields;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
@@ -34,10 +33,10 @@
public class EdgeDataDeserializer extends JsonDeserializer {
- private final ArangoDBGraphConfig.GraphType type;
+ private final ArangoDBGraphConfig config;
- public EdgeDataDeserializer(ArangoDBGraphConfig.GraphType type) {
- this.type = type;
+ public EdgeDataDeserializer(ArangoDBGraphConfig config) {
+ this.config = config;
}
@Override
@@ -46,8 +45,8 @@ public EdgeData deserialize(JsonParser p, DeserializationContext ctx) throws IOE
ObjectNode root = c.readTree(p);
ElementId id = c.treeToValue(root.get(ID), ElementId.class);
String label;
- if (type == ArangoDBGraphConfig.GraphType.SIMPLE) {
- label = root.get(LABEL).asText();
+ if (config.graphType == ArangoDBGraphConfig.GraphType.SIMPLE) {
+ label = root.get(config.labelField).asText();
} else {
label = id.getLabel();
}
@@ -56,7 +55,7 @@ public EdgeData deserialize(JsonParser p, DeserializationContext ctx) throws IOE
EdgeData data = new EdgeData(label, id, from, to);
for (Map.Entry prop : root.properties()) {
- if (!Fields.isReserved(prop.getKey())) {
+ if (!config.isReservedField(prop.getKey())) {
data.put(prop.getKey(), c.treeToValue(prop.getValue(), Object.class));
}
}
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/EdgeDataSerializer.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/EdgeDataSerializer.java
index c7dfd1a..14ec797 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/EdgeDataSerializer.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/EdgeDataSerializer.java
@@ -30,10 +30,10 @@
public class EdgeDataSerializer extends JsonSerializer {
- private final ArangoDBGraphConfig.GraphType type;
+ private final ArangoDBGraphConfig config;
- public EdgeDataSerializer(ArangoDBGraphConfig.GraphType type) {
- this.type = type;
+ public EdgeDataSerializer(ArangoDBGraphConfig config) {
+ this.config = config;
}
@Override
@@ -42,8 +42,8 @@ public void serialize(EdgeData data, JsonGenerator gen, SerializerProvider seria
if (data.getKey() != null) {
gen.writeStringField(Fields.KEY, data.getKey());
}
- if (type == ArangoDBGraphConfig.GraphType.SIMPLE) {
- gen.writeStringField(LABEL, data.getLabel());
+ if (config.graphType == ArangoDBGraphConfig.GraphType.SIMPLE) {
+ gen.writeStringField(config.labelField, data.getLabel());
}
gen.writeObjectField(FROM, data.getFrom());
gen.writeObjectField(TO, data.getTo());
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/SerdeModule.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/SerdeModule.java
index b2b1d88..d51fe79 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/SerdeModule.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/SerdeModule.java
@@ -24,27 +24,27 @@
public class SerdeModule extends SimpleModule {
private final ElementIdFactory idFactory;
- private final ArangoDBGraphConfig.GraphType graphType;
+ private final ArangoDBGraphConfig config;
- public SerdeModule(ElementIdFactory idFactory, ArangoDBGraphConfig.GraphType graphType) {
+ public SerdeModule(ElementIdFactory idFactory, ArangoDBGraphConfig config) {
this.idFactory = idFactory;
- this.graphType = graphType;
+ this.config = config;
}
@Override
public void setupModule(SetupContext context) {
SimpleSerializers serializers = new SimpleSerializers();
serializers.addSerializer(ElementId.class, new ElementIdSerializer());
- serializers.addSerializer(VertexData.class, new VertexDataSerializer(graphType));
- serializers.addSerializer(EdgeData.class, new EdgeDataSerializer(graphType));
+ serializers.addSerializer(VertexData.class, new VertexDataSerializer(config));
+ serializers.addSerializer(EdgeData.class, new EdgeDataSerializer(config));
serializers.addSerializer(VariablesData.class, new VariablesDataSerializer());
context.addSerializers(serializers);
SimpleDeserializers deserializers = new SimpleDeserializers();
deserializers.addDeserializer(ElementId.class, new ElementIdDeserializer(idFactory));
- deserializers.addDeserializer(VertexData.class, new VertexDataDeserializer(graphType));
- deserializers.addDeserializer(EdgeData.class, new EdgeDataDeserializer(graphType));
- deserializers.addDeserializer(VariablesData.class, new VariablesDataDeserializer());
+ deserializers.addDeserializer(VertexData.class, new VertexDataDeserializer(config));
+ deserializers.addDeserializer(EdgeData.class, new EdgeDataDeserializer(config));
+ deserializers.addDeserializer(VariablesData.class, new VariablesDataDeserializer(config));
context.addDeserializers(deserializers);
}
}
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VariablesDataDeserializer.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VariablesDataDeserializer.java
index c6af12d..33620fd 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VariablesDataDeserializer.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VariablesDataDeserializer.java
@@ -17,7 +17,7 @@
package com.arangodb.tinkerpop.gremlin.persistence.serde;
import com.arangodb.tinkerpop.gremlin.persistence.VariablesData;
-import com.arangodb.tinkerpop.gremlin.utils.Fields;
+import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraphConfig;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
@@ -31,6 +31,12 @@
import static com.arangodb.tinkerpop.gremlin.utils.Fields.*;
public class VariablesDataDeserializer extends JsonDeserializer {
+ private final ArangoDBGraphConfig config;
+
+ public VariablesDataDeserializer(ArangoDBGraphConfig config) {
+ this.config = config;
+ }
+
@Override
public VariablesData deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
ObjectCodec c = p.getCodec();
@@ -40,7 +46,7 @@ public VariablesData deserialize(JsonParser p, DeserializationContext ctx) throw
VariablesData data = new VariablesData(key, version);
for (Map.Entry prop : root.properties()) {
- if (!Fields.isReserved(prop.getKey())) {
+ if (!config.isReservedField(prop.getKey())) {
data.put(prop.getKey(), c.treeToValue(prop.getValue(), Object.class));
}
}
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VertexDataDeserializer.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VertexDataDeserializer.java
index 059b0fb..b754e88 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VertexDataDeserializer.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VertexDataDeserializer.java
@@ -20,7 +20,6 @@
import com.arangodb.tinkerpop.gremlin.persistence.VertexData;
import com.arangodb.tinkerpop.gremlin.persistence.VertexPropertyData;
import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraphConfig;
-import com.arangodb.tinkerpop.gremlin.utils.Fields;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
@@ -36,10 +35,10 @@
class VertexDataDeserializer extends JsonDeserializer {
- private final ArangoDBGraphConfig.GraphType type;
+ private final ArangoDBGraphConfig config;
- VertexDataDeserializer(ArangoDBGraphConfig.GraphType type) {
- this.type = type;
+ VertexDataDeserializer(ArangoDBGraphConfig config) {
+ this.config = config;
}
@Override
@@ -48,8 +47,8 @@ public VertexData deserialize(JsonParser p, DeserializationContext ctx) throws I
ObjectNode root = c.readTree(p);
ElementId id = c.treeToValue(root.get(ID), ElementId.class);
String label;
- if (type == ArangoDBGraphConfig.GraphType.SIMPLE) {
- label = root.get(LABEL).asText();
+ if (config.graphType == ArangoDBGraphConfig.GraphType.SIMPLE) {
+ label = root.get(config.labelField).asText();
} else {
label = id.getLabel();
}
@@ -60,7 +59,7 @@ public VertexData deserialize(JsonParser p, DeserializationContext ctx) throws I
: Collections.emptyMap();
for (Map.Entry prop : root.properties()) {
- if (!Fields.isReserved(prop.getKey())) {
+ if (!config.isReservedField(prop.getKey())) {
VertexPropertyData pd = new VertexPropertyData(c.treeToValue(prop.getValue(), Object.class));
String key = prop.getKey();
pd.putAll(meta.get(key));
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VertexDataSerializer.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VertexDataSerializer.java
index 159e693..9f62066 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VertexDataSerializer.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/serde/VertexDataSerializer.java
@@ -28,14 +28,12 @@
import java.util.HashMap;
import java.util.Map;
-import static com.arangodb.tinkerpop.gremlin.utils.Fields.LABEL;
-
class VertexDataSerializer extends JsonSerializer {
- private final ArangoDBGraphConfig.GraphType type;
+ private final ArangoDBGraphConfig config;
- VertexDataSerializer(ArangoDBGraphConfig.GraphType type) {
- this.type = type;
+ VertexDataSerializer(ArangoDBGraphConfig config) {
+ this.config = config;
}
@Override
@@ -44,8 +42,8 @@ public void serialize(VertexData data, JsonGenerator gen, SerializerProvider ser
if (data.getKey() != null) {
gen.writeStringField(Fields.KEY, data.getKey());
}
- if (type == ArangoDBGraphConfig.GraphType.SIMPLE) {
- gen.writeStringField(LABEL, data.getLabel());
+ if (config.graphType == ArangoDBGraphConfig.GraphType.SIMPLE) {
+ gen.writeStringField(config.labelField, data.getLabel());
}
Map> meta = new HashMap<>();
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java
index a3628f4..85d295a 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java
@@ -46,7 +46,7 @@ public class ArangoDBGraph implements Graph {
private final ArangoDBGraphClient client;
private final ElementIdFactory idFactory;
- private final ArangoDBGraphConfig config;
+ public final ArangoDBGraphConfig config;
/**
* Open a new {@code ArangoDBGraph} instance.
@@ -280,7 +280,7 @@ public ArangoDBVertex createVertex(Object... keyValues) {
ElementId elementId = idFactory.createVertexId(label, id);
for (int i = 0; i < keyValues.length; i = i + 2) {
if (keyValues[i] instanceof String) {
- ArangoDBUtil.validateProperty((String) keyValues[i], keyValues[i + 1]);
+ ArangoDBUtil.validateProperty((String) keyValues[i], keyValues[i + 1], config);
}
}
String inferredLabel = label != null ? label : Optional.ofNullable(elementId.getLabel()).orElse(Vertex.DEFAULT_LABEL);
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraphConfig.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraphConfig.java
index d71f589..ded3d70 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraphConfig.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraphConfig.java
@@ -18,6 +18,7 @@
import com.arangodb.config.ArangoConfigProperties;
import com.arangodb.entity.EdgeDefinition;
+import com.arangodb.tinkerpop.gremlin.utils.Fields;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.ConfigurationConverter;
import org.apache.commons.lang3.StringUtils;
@@ -43,12 +44,14 @@ public class ArangoDBGraphConfig {
public static final String KEY_GRAPH_ORPHAN_COLLECTIONS = "graph.orphanCollections";
public static final String KEY_GRAPH_EDGE_DEFINITIONS = "graph.edgeDefinitions";
public static final String KEY_ENABLE_DATA_DEFINITION = "graph.enableDataDefinition";
+ public static final String KEY_LABEL_FIELD = "graph.labelField";
// default values
public static final String DEFAULT_DB_NAME = "_system";
public static final String DEFAULT_GRAPH_NAME = "tinkerpop";
public static final GraphType DEFAULT_GRAPH_TYPE = GraphType.SIMPLE;
public static final boolean DEFAULT_ENABLE_DATA_DEFINITION = false;
+ public static final String DEFAULT_LABEL_FIELD = "_label";
public final Configuration configuration;
public final String dbName;
@@ -60,6 +63,7 @@ public class ArangoDBGraphConfig {
public final Set edges;
public final ArangoConfigProperties driverConfig;
public final boolean enableDataDefinition;
+ public final String labelField;
public ArangoDBGraphConfig(Configuration configuration) {
this.configuration = configuration;
@@ -73,9 +77,14 @@ public ArangoDBGraphConfig(Configuration configuration) {
edges = edgeDefinitions.stream().map(EdgeDef::getCollection).collect(Collectors.toSet());
driverConfig = ArangoConfigProperties.fromProperties(ConfigurationConverter.getProperties(conf.subset(KEY_DRIVER_PREFIX)), null);
enableDataDefinition = conf.getBoolean(KEY_ENABLE_DATA_DEFINITION, DEFAULT_ENABLE_DATA_DEFINITION);
+ labelField = conf.getString(KEY_LABEL_FIELD, DEFAULT_LABEL_FIELD);
validate();
}
+ public boolean isReservedField(String key) {
+ return labelField.equals(key) || Fields.ALL_STATIC.contains(key);
+ }
+
private void validate() {
validateName(graphName);
vertices.forEach(this::validateName);
@@ -88,6 +97,10 @@ private void validate() {
throw new IllegalArgumentException("Simple graph allows only 1 edge collection");
}
}
+ Objects.requireNonNull(labelField, "Label field must not be null");
+ if (labelField.isEmpty()) {
+ throw new IllegalArgumentException("Label field must not be empty");
+ }
}
private Set computeOrphanCollections(List orphanCollections) {
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraphVariables.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraphVariables.java
index 1ba43df..bd90346 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraphVariables.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraphVariables.java
@@ -63,7 +63,7 @@ public Optional get(String key) {
@Override
public void set(String key, Object value) {
- ArangoDBUtil.validateVariable(key, value);
+ ArangoDBUtil.validateVariable(key, value, graph.config);
data.put(key, value);
update();
}
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java
index 91cbfa9..a4aa7fa 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java
@@ -34,7 +34,7 @@ protected Property createProperty(String key, Object value) {
@Override
public Property property(String key, V value) {
if (removed()) throw Exceptions.elementAlreadyRemoved(id());
- ArangoDBUtil.validateProperty(key, value);
+ ArangoDBUtil.validateProperty(key, value, graph.config);
data().put(key, value);
doUpdate();
return createProperty(key, value);
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBConfigurationBuilder.java b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBConfigurationBuilder.java
index 9b49c61..96a102c 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBConfigurationBuilder.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBConfigurationBuilder.java
@@ -225,6 +225,18 @@ public ArangoDBConfigurationBuilder enableDataDefinition(boolean enableDataDefin
return setProperty(KEY_ENABLE_DATA_DEFINITION, enableDataDefinition);
}
+ /**
+ * Sets the field name used to store element labels in ArangoDB documents, when using {@code GraphType.SIMPLE}
+ * graph type.
+ * Default: {@code "_label"}
+ *
+ * @param labelField the name of the document field to store labels in
+ * @return this
+ */
+ public ArangoDBConfigurationBuilder labelField(String labelField) {
+ return setProperty(KEY_LABEL_FIELD, labelField);
+ }
+
/**
* Retrieves the current BaseConfiguration instance.
*
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java
index 0502372..c79f611 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java
@@ -54,9 +54,9 @@ public static void checkExistingGraph(GraphEntity info, ArangoDBGraphConfig conf
}
}
- public static void validateProperty(final String key, final Object value) {
+ public static void validateProperty(final String key, final Object value, ArangoDBGraphConfig config) {
ElementHelper.validateProperty(key, value);
- if (Fields.isReserved(key)) {
+ if (config.isReservedField(key)) {
throw new IllegalArgumentException("Property key can not be a reserved key: " + key);
}
if (!supportsDataType(value)) {
@@ -64,9 +64,9 @@ public static void validateProperty(final String key, final Object value) {
}
}
- public static void validateVariable(String key, Object value) {
+ public static void validateVariable(String key, Object value, ArangoDBGraphConfig config) {
GraphVariableHelper.validateVariable(key, value);
- if (Fields.isReserved(key)) {
+ if (config.isReservedField(key)) {
throw new IllegalArgumentException("Graph variable key can not be a reserved key: " + key);
}
if (!supportsDataType(value)) {
diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/Fields.java b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/Fields.java
index b650b85..85354e7 100644
--- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/Fields.java
+++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/Fields.java
@@ -17,6 +17,7 @@
package com.arangodb.tinkerpop.gremlin.utils;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -26,17 +27,12 @@ public class Fields {
public static final String REV = "_rev";
public static final String FROM = "_from";
public static final String TO = "_to";
- public static final String LABEL = "_label";
public static final String META = "_meta";
public static final String VERSION = "_version";
- private static final Set ALL = new HashSet<>(Arrays.asList(
- ID, KEY, REV, FROM, TO, LABEL, META, VERSION
- ));
-
- public static boolean isReserved(String key) {
- return ALL.contains(key);
- }
+ public static final Set ALL_STATIC = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+ ID, KEY, REV, FROM, TO, META, VERSION
+ )));
private Fields() {
}
diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/complex/ComplexPersistenceTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/complex/ComplexPersistenceTest.java
index f182f5f..75370f2 100644
--- a/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/complex/ComplexPersistenceTest.java
+++ b/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/complex/ComplexPersistenceTest.java
@@ -19,6 +19,7 @@
import com.arangodb.ArangoCollection;
import com.arangodb.tinkerpop.gremlin.TestGraphClient;
import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph;
+import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraphConfig;
import com.arangodb.tinkerpop.gremlin.utils.Fields;
import org.apache.tinkerpop.gremlin.AbstractGremlinTest;
import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -50,7 +51,7 @@ public void vertices() {
.containsEntry(Fields.KEY, "foo")
.containsEntry(Fields.ID, Vertex.DEFAULT_LABEL + "/foo")
.containsKey(Fields.REV)
- .doesNotContainKey(Fields.LABEL)
+ .doesNotContainKey(ArangoDBGraphConfig.DEFAULT_LABEL_FIELD)
.containsEntry("key", "value");
Map> meta = (Map>) doc.get(Fields.META);
@@ -79,7 +80,7 @@ public void edges() {
.containsKey(Fields.REV)
.containsEntry(Fields.FROM, Vertex.DEFAULT_LABEL + "/a")
.containsEntry(Fields.TO, Vertex.DEFAULT_LABEL + "/b")
- .doesNotContainKey(Fields.LABEL)
+ .doesNotContainKey(ArangoDBGraphConfig.DEFAULT_LABEL_FIELD)
.containsEntry("key", "value");
}
}
diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/AqlTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/AqlTest.java
index b19f247..f214320 100644
--- a/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/AqlTest.java
+++ b/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/AqlTest.java
@@ -232,9 +232,9 @@ public void compareAqlAndGremlin() {
graph().aql("" +
"FOR start IN vertex" +
" FOR b, e1 IN 1..1 INBOUND start GRAPH standard" +
- " FILTER e1._label == 'sungBy'" +
+ " FILTER e1." + graph().config.labelField + " == 'sungBy'" +
" FOR a, e2 IN 1..1 OUTBOUND b GRAPH standard" +
- " FILTER e2._label == 'writtenBy'" +
+ " FILTER e2." + graph().config.labelField + " == 'writtenBy'" +
" FILTER a == start" +
" RETURN {a, b}")
.select("a", "b").by("name")
@@ -244,9 +244,9 @@ public void compareAqlAndGremlin() {
sungAndWrittenBySame,
graph().aql("" +
"FOR e1 IN edge" +
- " FILTER e1._label == \"sungBy\"" +
+ " FILTER e1." + graph().config.labelField + " == \"sungBy\"" +
" FOR e2 IN edge" +
- " FILTER e2._label == \"writtenBy\"" +
+ " FILTER e2." + graph().config.labelField + " == \"writtenBy\"" +
" FILTER e1._from == e2._from" +
" FILTER e1._to == e2._to" +
" RETURN {a: DOCUMENT(e1._to), b: DOCUMENT(e1._from)}")
@@ -258,8 +258,8 @@ public void compareAqlAndGremlin() {
graph().aql("" +
"FOR start IN vertex" +
" FOR a, e, p IN 2..2 OUTBOUND start GRAPH standard" +
- " FILTER p.edges[0]._label == 'followedBy'" +
- " FILTER p.edges[1]._label == 'followedBy'" +
+ " FILTER p.edges[0]." + graph().config.labelField + " == 'followedBy'" +
+ " FILTER p.edges[1]." + graph().config.labelField + " == 'followedBy'" +
" FILTER a == start" +
" RETURN {a: p.vertices[0], b: p.vertices[1]}")
.select("a", "b").by("name")
@@ -269,9 +269,9 @@ public void compareAqlAndGremlin() {
followEachOther,
graph().aql("" +
"FOR e1 IN edge" +
- " FILTER e1._label == 'followedBy'" +
+ " FILTER e1." + graph().config.labelField + " == 'followedBy'" +
" FOR e2 IN edge" +
- " FILTER e2._label == 'followedBy'" +
+ " FILTER e2." + graph().config.labelField + " == 'followedBy'" +
" FILTER e1._to == e2._from" +
" FILTER e1._from == e2._to" +
" RETURN {a: DOCUMENT(e1._from), b: DOCUMENT(e1._to)}")
@@ -284,14 +284,14 @@ public void compareAqlAndGremlin() {
"FOR start IN vertex" +
" LET outCount = (" +
" FOR a, e IN 1..1 OUTBOUND start GRAPH standard" +
- " FILTER e._label == 'followedBy'" +
+ " FILTER e." + graph().config.labelField + " == 'followedBy'" +
" COLLECT WITH COUNT INTO c" +
" RETURN c" +
" )[0]" +
" FILTER outCount > 10" +
" LET inCount = (" +
" FOR a, e IN 1..1 INBOUND start GRAPH standard" +
- " FILTER e._label == 'followedBy'" +
+ " FILTER e." + graph().config.labelField + " == 'followedBy'" +
" COLLECT WITH COUNT INTO c" +
" RETURN c" +
" )[0]" +
@@ -307,7 +307,7 @@ public void compareAqlAndGremlin() {
"FOR start IN vertex" +
" LET outCount = (" +
" FOR e IN edge" +
- " FILTER e._label == 'followedBy'" +
+ " FILTER e." + graph().config.labelField + " == 'followedBy'" +
" FILTER e._from == start._id" +
" COLLECT WITH COUNT INTO c" +
" RETURN c" +
@@ -315,7 +315,7 @@ public void compareAqlAndGremlin() {
" FILTER outCount > 10" +
" LET inCount = (" +
" FOR e IN edge" +
- " FILTER e._label == 'followedBy'" +
+ " FILTER e." + graph().config.labelField + " == 'followedBy'" +
" FILTER e._to == start._id " +
" COLLECT WITH COUNT INTO c" +
" RETURN c" +
diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/DriverTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/DriverTest.java
index 195bd6a..58e54b6 100644
--- a/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/DriverTest.java
+++ b/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/DriverTest.java
@@ -38,7 +38,7 @@ public void shouldGetDriverVersion() {
@Test
public void shouldGetDatabaseInfo() {
assertThat(graph().getArangoDatabase().getInfo().getName())
- .isEqualTo("SimpleGraphProvider");
+ .isEqualTo("CustomLabelSimpleGraphProvider");
}
@Test
diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/SimplePersistenceTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/SimplePersistenceTest.java
index 66eaabf..7ada141 100644
--- a/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/SimplePersistenceTest.java
+++ b/src/test/java/com/arangodb/tinkerpop/gremlin/arangodb/simple/SimplePersistenceTest.java
@@ -32,6 +32,7 @@
import static com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph.GRAPH_VARIABLES_COLLECTION;
import static org.assertj.core.api.Assertions.assertThat;
+@SuppressWarnings("resource")
public class SimplePersistenceTest extends AbstractGremlinTest {
private TestGraphClient client() {
@@ -42,6 +43,10 @@ private String graphName() {
return ((ArangoDBGraph) graph).name();
}
+ private ArangoDBGraph graph() {
+ return (ArangoDBGraph) this.graph;
+ }
+
@Test
@SuppressWarnings("unchecked")
public void variables() {
@@ -74,7 +79,7 @@ public void vertices() {
.containsEntry(Fields.KEY, "foo")
.containsEntry(Fields.ID, Vertex.DEFAULT_LABEL + "/foo")
.containsKey(Fields.REV)
- .containsEntry(Fields.LABEL, "bar")
+ .containsEntry(graph().config.labelField, "bar")
.containsEntry("key", "value");
Map> meta = (Map>) doc.get(Fields.META);
@@ -103,7 +108,7 @@ public void edges() {
.containsKey(Fields.REV)
.containsEntry(Fields.FROM, Vertex.DEFAULT_LABEL + "/a")
.containsEntry(Fields.TO, Vertex.DEFAULT_LABEL + "/b")
- .containsEntry(Fields.LABEL, "foo")
+ .containsEntry(graph().config.labelField, "foo")
.containsEntry("key", "value");
}
}
diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/simple/CustomLabelSimpleGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/simple/CustomLabelSimpleGraphProvider.java
new file mode 100644
index 0000000..dbab209
--- /dev/null
+++ b/src/test/java/com/arangodb/tinkerpop/gremlin/simple/CustomLabelSimpleGraphProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2025 ArangoDB GmbH and The University of York
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.arangodb.tinkerpop.gremlin.simple;
+
+import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder;
+
+public class CustomLabelSimpleGraphProvider extends SimpleGraphProvider {
+
+ @Override
+ protected void customizeBuilder(ArangoDBConfigurationBuilder builder) {
+ super.customizeBuilder(builder);
+ builder.labelField("type");
+ }
+}
diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/simple/SimpleArangoDBSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/simple/SimpleArangoDBSuiteTest.java
index 4cdb322..6cd0883 100644
--- a/src/test/java/com/arangodb/tinkerpop/gremlin/simple/SimpleArangoDBSuiteTest.java
+++ b/src/test/java/com/arangodb/tinkerpop/gremlin/simple/SimpleArangoDBSuiteTest.java
@@ -21,7 +21,7 @@
import org.junit.runner.RunWith;
@RunWith(SimpleArangoDBSuite.class)
-@GraphProviderClass(provider = SimpleGraphProvider.class, graph = SimpleTestGraph.class)
+@GraphProviderClass(provider = CustomLabelSimpleGraphProvider.class, graph = SimpleTestGraph.class)
public class SimpleArangoDBSuiteTest {
}