Skip to content

Commit a97db03

Browse files
authored
TINKERPOP-3148 Prevent multiple by modulators from being used for the dedup step by throwing IllegalStateException if modulateBy is called more than once. (#3105)
1 parent 007fd22 commit a97db03

8 files changed

Lines changed: 24 additions & 2 deletions

File tree

CHANGELOG.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ This release also includes changes from <<release-3-7-XXX, 3.7.XXX>>.
6666
* Added missing strategies in `gremlin-go`, updated certain strategies to take varargs and updated `GoTranslatorVisitor` for corresponding translations.
6767
* Fixed `BigInt` and `BigDecimal` parsing in `gremlin-go` cucumber test suite, fixed `UnscaledValue` type in `BigDecimal` struct and added `ParseBigDecimal` method.
6868
* Added validation to `valueMap()`/`propertyMap()`/`groupCount()` to prevent an invalid usage of multiple `by()` modulators.
69-
* Added validation to `groupCount()` and `sack()` to prevent an invalid usage of multiple `by()` modulators.
69+
* Added validation to `groupCount()`, `sack()` and `dedup()` to prevent an invalid usage of multiple `by()` modulators.
7070
* Deprecated `ProcessLimitedStandardSuite` and `ProcessLimitedComputerSuite` in favor of `ProcessEmbeddedStandardSuite` and `ProcessEmbeddedComputerSuite` respectively.
7171
* Deprecated `ProcessStandardSuite` and the `ProcessComputerSuite` in favor of Gherkin testing and the `ProcessEmbeddedStandardSuite` and `ProcessEmbeddedComputerSuite` for testing JVM-specific Gremlin behaviors.
7272
* Removed lambda oriented Gremlin testing from Gherkin test suite.

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ public List<Traversal<S, Object>> getLocalChildren() {
122122

123123
@Override
124124
public void modulateBy(final Traversal.Admin<?, ?> dedupTraversal) {
125+
if (this.dedupTraversal != null) {
126+
throw new IllegalStateException("Dedup step can only have one by modulator");
127+
}
125128
this.dedupTraversal = this.integrateChild(dedupTraversal);
126129
}
127130

gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStepTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import java.util.Arrays;
2626
import java.util.List;
27+
import org.junit.Test;
2728

2829
/**
2930
* @author Daniel Kuppitz (http://gremlin.guru)
@@ -37,4 +38,9 @@ protected List<Traversal> getTraversals() {
3738
__.dedup().by("name")
3839
);
3940
}
41+
42+
@Test(expected = IllegalStateException.class)
43+
public void shouldThrowForMultipleByModulators() {
44+
__.dedup().by("name").by("age");
45+
}
4046
}

gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ private static IDictionary<string, List<Func<GraphTraversalSource, IDictionary<s
200200
{"g_VX1X_valuesXageX_dedupXlocalX_unfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values<object>("age").Dedup(Scope.Local).Unfold<object>()}},
201201
{"g_V_properties_dedup_count", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "josh").AddV("person").Property("name", "josh").AddV("person").Property("name", "josh"), (g,p) =>g.V().Properties<object>("name").Dedup().Count()}},
202202
{"g_V_properties_dedup_byXvalueX_count", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "josh").AddV("person").Property("name", "josh").AddV("person").Property("name", "josh"), (g,p) =>g.V().Properties<object>("name").Dedup().By(T.Value).Count()}},
203+
{"g_V_both_hasXlabel_softwareX_dedup_byXlangX_byXnameX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Both().Has(T.Label, "software").Dedup().By("lang").By("name").Values<object>("name")}},
203204
{"g_V_drop", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV().As("a").AddV().As("b").AddE("knows").To("a"), (g,p) =>g.V().Drop(), (g,p) =>g.V(), (g,p) =>g.E()}},
204205
{"g_V_outE_drop", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV().As("a").AddV().As("b").AddE("knows").To("a"), (g,p) =>g.V().OutE().Drop(), (g,p) =>g.V(), (g,p) =>g.E()}},
205206
{"g_V_properties_drop", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV().Property("name", "bob").AddV().Property("name", "alice"), (g,p) =>g.V().Properties<object>().Drop(), (g,p) =>g.V(), (g,p) =>g.V().Properties<object>()}},

gremlin-go/driver/cucumber/gremlin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[
169169
"g_VX1X_valuesXageX_dedupXlocalX_unfold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Values("age").Dedup(gremlingo.Scope.Local).Unfold()}},
170170
"g_V_properties_dedup_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "josh").AddV("person").Property("name", "josh").AddV("person").Property("name", "josh")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Properties("name").Dedup().Count()}},
171171
"g_V_properties_dedup_byXvalueX_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "josh").AddV("person").Property("name", "josh").AddV("person").Property("name", "josh")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Properties("name").Dedup().By(gremlingo.T.Value).Count()}},
172+
"g_V_both_hasXlabel_softwareX_dedup_byXlangX_byXnameX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Both().Has(gremlingo.T.Label, "software").Dedup().By("lang").By("name").Values("name")}},
172173
"g_V_drop": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().As("a").AddV().As("b").AddE("knows").To("a")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Drop()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E()}},
173174
"g_V_outE_drop": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().As("a").AddV().As("b").AddE("knows").To("a")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().OutE().Drop()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E()}},
174175
"g_V_properties_drop": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().Property("name", "bob").AddV().Property("name", "alice")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Properties().Drop()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Properties()}},

gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gremlin-python/src/main/python/radish/gremlin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@
172172
'g_VX1X_valuesXageX_dedupXlocalX_unfold': [(lambda g, vid1=None:g.V(vid1).values('age').dedup(Scope.local).unfold())],
173173
'g_V_properties_dedup_count': [(lambda g:g.add_v('person').property('name', 'josh').add_v('person').property('name', 'josh').add_v('person').property('name', 'josh')), (lambda g:g.V().properties('name').dedup().count())],
174174
'g_V_properties_dedup_byXvalueX_count': [(lambda g:g.add_v('person').property('name', 'josh').add_v('person').property('name', 'josh').add_v('person').property('name', 'josh')), (lambda g:g.V().properties('name').dedup().by(T.value).count())],
175+
'g_V_both_hasXlabel_softwareX_dedup_byXlangX_byXnameX_name': [(lambda g:g.V().both().has(T.label, 'software').dedup().by('lang').by('name').values('name'))],
175176
'g_V_drop': [(lambda g:g.add_v().as_('a').add_v().as_('b').add_e('knows').to('a')), (lambda g:g.V().drop()), (lambda g:g.V()), (lambda g:g.E())],
176177
'g_V_outE_drop': [(lambda g:g.add_v().as_('a').add_v().as_('b').add_e('knows').to('a')), (lambda g:g.V().out_e().drop()), (lambda g:g.V()), (lambda g:g.E())],
177178
'g_V_properties_drop': [(lambda g:g.add_v().property('name', 'bob').add_v().property('name', 'alice')), (lambda g:g.V().properties().drop()), (lambda g:g.V()), (lambda g:g.V().properties())],

gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Dedup.feature

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,4 +421,13 @@ Feature: Step - dedup()
421421
When iterated to list
422422
Then the result should be unordered
423423
| result |
424-
| d[1].l |
424+
| d[1].l |
425+
426+
Scenario: g_V_both_hasXlabel_softwareX_dedup_byXlangX_byXnameX_name
427+
Given the modern graph
428+
And the traversal of
429+
"""
430+
g.V().both().has(T.label, "software").dedup().by("lang").by("name").values("name")
431+
"""
432+
When iterated to list
433+
Then the traversal will raise an error with message containing text of "Dedup step can only have one by modulator"

0 commit comments

Comments
 (0)