From 3a7805c73301ad370e70a410326f23aa92b6e99e Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 25 Oct 2016 13:50:48 -0400 Subject: [PATCH] TINKERPOP-919 Added supportsDuplicateMultiProperties() That feature allows a graph to specify whether or not it supports multi-properties that allow the same value to be supplied on the same key. --- CHANGELOG.asciidoc | 1 + .../upgrade/release-3.2.x-incubating.asciidoc | 8 ++++++ .../tinkerpop/gremlin/structure/Graph.java | 12 +++++++++ .../gremlin/structure/VertexProperty.java | 4 +++ .../gremlin/structure/FeatureSupportTest.java | 27 +++++++++++++++++-- .../gremlin/structure/VertexPropertyTest.java | 26 +++++++++++++++--- 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 73bfa1e8c8b..1df17de6133 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima TinkerPop 3.2.4 (Release Date: NOT OFFICIALLY RELEASED YET) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Added `VertexFeatures.supportsDuplicateMultiProperties()` for graphs that only support unique values in multi-properties. * Deprecated the "performance" tests in `OptIn`. * Fixed a severe bug where `GraphComputer` strategies are not being loaded until the second use of the traversal source. diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc b/docs/src/upgrade/release-3.2.x-incubating.asciidoc index 0fba8f5cb41..30daae1e5ce 100644 --- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc +++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc @@ -35,6 +35,14 @@ Upgrading for Providers Graph Database Providers ^^^^^^^^^^^^^^^^^^^^^^^^ +Duplicate Multi-Properties +++++++++++++++++++++++++++ + +Added `supportsDuplicateMultiProperties` to `VertexFeatures` so that graph provider who only support unique values as +multi-properties have more flexibility in describing their graph capabilities. + +See: https://issues.apache.org/jira/browse/TINKERPOP-919[TINKERPOP-919] + Deprecated Performance OptIn ++++++++++++++++++++++++++++ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java index ed3f12d4cfb..255fbca0e61 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java @@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.engine.ComputerTraversalEngine; import org.apache.tinkerpop.gremlin.structure.io.Io; import org.apache.tinkerpop.gremlin.structure.io.IoRegistry; import org.apache.tinkerpop.gremlin.structure.util.FeatureDescriptor; @@ -512,6 +513,7 @@ public default VariableFeatures variables() { public interface VertexFeatures extends ElementFeatures { public static final String FEATURE_ADD_VERTICES = "AddVertices"; public static final String FEATURE_MULTI_PROPERTIES = "MultiProperties"; + public static final String FEATURE_DUPLICATE_MULTI_PROPERTIES = "DuplicateMultiProperties"; public static final String FEATURE_META_PROPERTIES = "MetaProperties"; public static final String FEATURE_REMOVE_VERTICES = "RemoveVertices"; @@ -549,6 +551,16 @@ public default boolean supportsMultiProperties() { return true; } + /** + * Determines if a {@link Vertex} can support non-unique values on the same key. For this value to be + * {@code true}, then {@link #supportsMetaProperties()} must also return true. By default this method, + * just returns what {@link #supportsMultiProperties()} returns. + */ + @FeatureDescriptor(name = FEATURE_DUPLICATE_MULTI_PROPERTIES) + public default boolean supportsDuplicateMultiProperties() { + return supportsMultiProperties(); + } + /** * Determines if a {@link Vertex} can support properties on vertex properties. It is assumed that a * graph will support all the same data types for meta-properties that are supported for regular diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java index ecbefb3f598..c6b443c2e29 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java @@ -99,6 +99,10 @@ public static UnsupportedOperationException multiPropertiesNotSupported() { return new UnsupportedOperationException("Multiple properties on a vertex is not supported"); } + public static UnsupportedOperationException identicalMultiPropertiesNotSupported() { + return new UnsupportedOperationException("Multiple properties on a vertex is supported, but a single key may not hold the same value more than once"); + } + public static UnsupportedOperationException metaPropertiesNotSupported() { return new UnsupportedOperationException("Properties on a vertex property is not supported"); } diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/FeatureSupportTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/FeatureSupportTest.java index 98406b4b0df..c73474ce296 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/FeatureSupportTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/FeatureSupportTest.java @@ -697,7 +697,8 @@ public void shouldEnableFeatureOnGraphIfNotEnabled() throws Exception { "multiPropertiesNotSupported", "metaPropertiesNotSupported", "userSuppliedIdsNotSupported", - "userSuppliedIdsOfThisTypeNotSupported" + "userSuppliedIdsOfThisTypeNotSupported", + "identicalMultiPropertiesNotSupported" }) @ExceptionCoverage(exceptionClass = Element.Exceptions.class, methods = { "propertyRemovalNotSupported" @@ -873,6 +874,22 @@ public void shouldSupportRemovePropertyIfAPropertyCanBeRemoved() throws Exceptio } } + @Test + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = VertexFeatures.FEATURE_MULTI_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = VertexFeatures.FEATURE_DUPLICATE_MULTI_PROPERTIES, supported = false) + public void shouldSupportIdenticalMultiPropertyIfTheSameKeyCanBeAssignedSameValueMoreThanOnce() throws Exception { + try { + final Vertex v = graph.addVertex("name", "stephen", "name", "stephen"); + if (2 == IteratorUtils.count(v.properties())) + fail(String.format(INVALID_FEATURE_SPECIFICATION, VertexFeatures.class.getSimpleName(), VertexFeatures.FEATURE_DUPLICATE_MULTI_PROPERTIES)); + } catch (Exception ex) { + validateException(VertexProperty.Exceptions.identicalMultiPropertiesNotSupported(), ex); + } + } + @Test @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) @@ -1023,7 +1040,13 @@ public void shouldSupportADataTypeIfVertexHasPropertyEnabled() { @Test public void shouldSupportRegularTransactionsIfThreadedTransactionsAreEnabled() { if (graphFeatures.supportsThreadedTransactions()) - assertTrue(graphFeatures.supportsThreadedTransactions()); + assertThat(graphFeatures.supportsThreadedTransactions(), is(true)); + } + + @Test + public void shouldSupportMultiPropertiesIfSupportingIdenticalMultiProperties() { + if (vertexFeatures.supportsDuplicateMultiProperties()) + assertThat(vertexFeatures.supportsMultiProperties(), is(true)); } } } diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexPropertyTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexPropertyTest.java index 845be70e351..04b431da8ea 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexPropertyTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexPropertyTest.java @@ -34,6 +34,7 @@ import java.util.UUID; import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThat; @@ -288,11 +289,8 @@ public void shouldHandleSetVertexProperties() { } } }); - - } - @Test @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES) @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES) @@ -329,6 +327,28 @@ public void shouldRespectWhatAreEdgesAndWhatArePropertiesInMultiProperties() { assertEquals(0, IteratorUtils.count(newMexico.properties(T.key.getAccessor()))); assertEquals(0, IteratorUtils.count(newMexico.properties(T.value.getAccessor()))); } + + @Test + @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_DUPLICATE_MULTI_PROPERTIES) + public void shouldAllowIdenticalValuedMultiProperties() { + final Vertex v = graph.addVertex(); + v.property(VertexProperty.Cardinality.list, "name", "stephen"); + v.property(VertexProperty.Cardinality.list, "name", "stephen"); + v.property(VertexProperty.Cardinality.list, "name", "steve"); + v.property(VertexProperty.Cardinality.list, "name", "stephen"); + v.property(VertexProperty.Cardinality.list, "color", "red"); + + tryCommit(graph, g -> { + final Vertex vertex = graph.vertices(v).next(); + assertEquals(4, IteratorUtils.count(vertex.properties("name"))); + assertEquals(1, IteratorUtils.count(vertex.properties("color"))); + assertEquals(5, IteratorUtils.count(vertex.properties())); + + assertThat(IteratorUtils.set(vertex.values("name")), contains("stephen", "steve")); + }); + } } public static class VertexPropertyRemoval extends AbstractGremlinTest {