You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2024/02/06 23:48:37 UTC

(tinkerpop) branch master updated: TINKERPOP-3049 Fixed Cardinality function in onCreate

This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/master by this push:
     new 66db18e17a TINKERPOP-3049 Fixed Cardinality function in onCreate
     new b837fc7eb2 Merge branch '3.7-dev'
66db18e17a is described below

commit 66db18e17ad2937c71a3b2167fd0d7989ab2cbd7
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Tue Feb 6 18:46:13 2024 -0500

    TINKERPOP-3049 Fixed Cardinality function in onCreate
    
    Not sure why this wasn't implemented earlier, but I think the intent was that providers would natively handled the CardinalityValueTraversal. Don't think that was a great choice becasue then this feature doesn't work out of the box. CTR
---
 CHANGELOG.asciidoc                                 |  1 +
 .../traversal/step/map/MergeVertexStep.java        | 21 +++++++++--
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |  3 ++
 gremlin-go/driver/cucumber/gremlin.go              |  3 ++
 .../gremlin-javascript/test/cucumber/gremlin.js    |  3 ++
 gremlin-python/src/main/python/radish/gremlin.py   |  3 ++
 .../gremlin/test/features/map/MergeVertex.feature  | 44 +++++++++++++++++++++-
 7 files changed, 73 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index cd9d7e2b11..3029735004 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -24,6 +24,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 === TinkerPop 3.7.2 (NOT OFFICIALLY RELEASED YET)
 
 * Deprecated `ltrim()` and `rTrim()` in favor of `l_trim()` and `r_trim` in Python.
+* Fixed bug in `onCreate` for `mergeV()` where use of the `Cardinality` functions was not properly handled.
 
 [[release-3-7-1]]
 === TinkerPop 3.7.1 (November 20, 2023)
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
index 1b7ba4bafc..713b839e8d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
@@ -145,10 +145,23 @@ public class MergeVertexStep<S> extends MergeStep<S, Vertex, Map> {
          */
         final Map<?,?> onCreateMap = onCreateMap(traverser, mergeMap);
 
-        final Object[] flatArgs = onCreateMap.entrySet().stream()
-                .flatMap(e -> Stream.of(e.getKey(), e.getValue())).collect(toList()).toArray();
-
-        final Vertex vertex = graph.addVertex(flatArgs);
+        // extract the key/value pairs from the map and flatten them into an array but exclude any that have a
+        // CardinalityValueTraversal as the value. you have to ignore those in a call to addVertex because that would
+        // make it so that the Graph had to know how to deal with the CardinalityValueTraversal which it doesn't. this
+        // allows this feature to work out of the box.
+        final Object[] flatArgsWithoutExplicitCardinality = onCreateMap.entrySet().stream().
+                filter(e -> !(e.getValue() instanceof CardinalityValueTraversal)).
+                flatMap(e -> Stream.of(e.getKey(), e.getValue())).collect(toList()).toArray();
+
+        final Vertex vertex = graph.addVertex(flatArgsWithoutExplicitCardinality);
+
+        // deal with values that have the cardinality explicitly set which should only occur on string keys
+        onCreateMap.entrySet().stream().
+                filter(e -> e.getKey() instanceof String && e.getValue() instanceof CardinalityValueTraversal).
+                forEach(e -> {
+                    final CardinalityValueTraversal cardinalityValueTraversal = (CardinalityValueTraversal) e.getValue();
+                    vertex.property(cardinalityValueTraversal.getCardinality(), (String) e.getKey(), cardinalityValueTraversal.getValue());
+                });
 
         // trigger callbacks for eventing - in this case, it's a VertexAddedEvent
         if (this.callbackRegistry != null && !callbackRegistry.getCallbacks().isEmpty()) {
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 4df9adfe09..12fd0ddb9f 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -965,6 +965,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_mergeVXname_markoX_optionXonMatch_age_33_singleX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property(Cardinality.List,"age",29).Property(Cardinality.List,"age",31).Property(Cardinality.List,"age",32), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "marko"}}).Option(Merge.OnMatch,new Dictionary<object,object> {{"age", 33}},Cardinality.Single), ( [...]
                {"g_mergeVXname_markoX_optionXonMatch_name_allen_age_setX31X_singleX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property(Cardinality.List,"age",29).Property(Cardinality.List,"age",31).Property(Cardinality.List,"age",32), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "marko"}}).Option(Merge.OnMatch,new Dictionary<object,object> {{"name", "allen"},  [...]
                {"g_mergeVXname_markoX_optionXonMatch_name_allen_age_singleX31X_singleX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property(Cardinality.List,"age",29).Property(Cardinality.List,"age",31).Property(Cardinality.List,"age",32), (g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "marko"}}).Option(Merge.OnMatch,new Dictionary<object,object> {{"name", "allen" [...]
+               {"g_mergeVXname_aliceX_optionXonCreate_age_singleX81XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "alice"}, {T.Label, "person"}}).Option(Merge.OnCreate, (IDictionary<object,object>) new Dictionary<object,object> {{"age", CardinalityValue.Single(81)}}), (g,p) =>g.V().Has("person","name","alice").Has("age",81), (g,p) =>g.V().Has("person","name","alice") [...]
+               {"g_mergeVXname_aliceX_optionXonCreate_age_setX81XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "alice"}, {T.Label, "person"}}).Option(Merge.OnCreate, (IDictionary<object,object>) new Dictionary<object,object> {{"age", CardinalityValue.Set(81)}}), (g,p) =>g.V().Has("person","name","alice").Has("age",81), (g,p) =>g.V().Has("person","name","alice").Has(" [...]
+               {"g_mergeVXname_aliceX_optionXonCreate_age_singleX81X_age_81_setX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {{"name", "alice"}, {T.Label, "person"}}).Option(Merge.OnCreate,new Dictionary<object,object> {{"age", 81}},Cardinality.Set), (g,p) =>g.V().Has("person","name","alice").Has("age",81), (g,p) =>g.V().Has("person","name","alice").Has("age"), (g,p) =>g.V().H [...]
                {"g_mergeV_hidden_label_key_onMatch_matched_prohibited", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.MergeV((IDictionary<object,object>) new Dictionary<object,object> {}).Option(Merge.OnMatch, (IDictionary<object,object>) p["xx1"])}}, 
                {"g_V_age_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Min<object>()}}, 
                {"g_V_foo_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Min<object>()}}, 
diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go
index bef853eefe..b052bf8db4 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -936,6 +936,9 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[
     "g_mergeVXname_markoX_optionXonMatch_age_33_singleX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property(gremlingo.Cardinality.List, "age", 29).Property(gremlingo.Cardinality.List, "age", 31).Property(gremlingo.Cardinality.List, "age", 32)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{"name": [...]
     "g_mergeVXname_markoX_optionXonMatch_name_allen_age_setX31X_singleX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property(gremlingo.Cardinality.List, "age", 29).Property(gremlingo.Cardinality.List, "age", 31).Property(gremlingo.Cardinality.List, "age", 32)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]int [...]
     "g_mergeVXname_markoX_optionXonMatch_name_allen_age_singleX31X_singleX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property(gremlingo.Cardinality.List, "age", 29).Property(gremlingo.Cardinality.List, "age", 31).Property(gremlingo.Cardinality.List, "age", 32)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}] [...]
+    "g_mergeVXname_aliceX_optionXonCreate_age_singleX81XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{"name": "alice", gremlingo.T.Label: "person"}).Option(gremlingo.Merge.OnCreate, map[interface{}]interface{}{"age": gremlingo.CardinalityValue.Single(81)})}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", "name", "alice"). [...]
+    "g_mergeVXname_aliceX_optionXonCreate_age_setX81XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{"name": "alice", gremlingo.T.Label: "person"}).Option(gremlingo.Merge.OnCreate, map[interface{}]interface{}{"age": gremlingo.CardinalityValue.Set(81)})}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", "name", "alice").Has("a [...]
+    "g_mergeVXname_aliceX_optionXonCreate_age_singleX81X_age_81_setX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{"name": "alice", gremlingo.T.Label: "person"}).Option(gremlingo.Merge.OnCreate, map[interface{}]interface{}{"age": 81}, gremlingo.Cardinality.Set)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("person", "name", "alice [...]
     "g_mergeV_hidden_label_key_onMatch_matched_prohibited": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.MergeV(map[interface{}]interface{}{}).Option(gremlingo.Merge.OnMatch, p["xx1"])}}, 
     "g_V_age_min": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Min()}}, 
     "g_V_foo_min": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("foo").Min()}}, 
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index ba94bc9794..e0072a4ffe 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -956,6 +956,9 @@ const gremlins = {
     g_mergeVXname_markoX_optionXonMatch_age_33_singleX: [function({g}) { return g.addV("person").property("name","marko").property(Cardinality.list,"age",29).property(Cardinality.list,"age",31).property(Cardinality.list,"age",32) }, function({g}) { return g.mergeV(new Map([["name","marko"]])).option(Merge.onMatch,new Map([["age",33]]),Cardinality.single) }, function({g}) { return g.V().has("person","name","marko").has("age",33) }, function({g}) { return g.V().has("person","name","marko") [...]
     g_mergeVXname_markoX_optionXonMatch_name_allen_age_setX31X_singleX: [function({g}) { return g.addV("person").property("name","marko").property(Cardinality.list,"age",29).property(Cardinality.list,"age",31).property(Cardinality.list,"age",32) }, function({g}) { return g.mergeV(new Map([["name","marko"]])).option(Merge.onMatch,new Map([["name","allen"],["age",CardinalityValue.set(31)]]),Cardinality.single) }, function({g}) { return g.V().has("person","name","marko") }, function({g}) {  [...]
     g_mergeVXname_markoX_optionXonMatch_name_allen_age_singleX31X_singleX: [function({g}) { return g.addV("person").property("name","marko").property(Cardinality.list,"age",29).property(Cardinality.list,"age",31).property(Cardinality.list,"age",32) }, function({g}) { return g.mergeV(new Map([["name","marko"]])).option(Merge.onMatch,new Map([["name","allen"],["age",CardinalityValue.single(31)]]),Cardinality.single) }, function({g}) { return g.V().has("person","name","marko") }, function({ [...]
+    g_mergeVXname_aliceX_optionXonCreate_age_singleX81XX: [function({g}) { return g.mergeV(new Map([["name","alice"],[T.label,"person"]])).option(Merge.onCreate,new Map([["age",CardinalityValue.single(81)]])) }, function({g}) { return g.V().has("person","name","alice").has("age",81) }, function({g}) { return g.V().has("person","name","alice").has("age") }, function({g}) { return g.V().has("person","name","alice").properties("age") }], 
+    g_mergeVXname_aliceX_optionXonCreate_age_setX81XX: [function({g}) { return g.mergeV(new Map([["name","alice"],[T.label,"person"]])).option(Merge.onCreate,new Map([["age",CardinalityValue.set(81)]])) }, function({g}) { return g.V().has("person","name","alice").has("age",81) }, function({g}) { return g.V().has("person","name","alice").has("age") }, function({g}) { return g.V().has("person","name","alice").properties("age") }], 
+    g_mergeVXname_aliceX_optionXonCreate_age_singleX81X_age_81_setX: [function({g}) { return g.mergeV(new Map([["name","alice"],[T.label,"person"]])).option(Merge.onCreate,new Map([["age",81]]),Cardinality.set) }, function({g}) { return g.V().has("person","name","alice").has("age",81) }, function({g}) { return g.V().has("person","name","alice").has("age") }, function({g}) { return g.V().has("person","name","alice").properties("age") }], 
     g_mergeV_hidden_label_key_onMatch_matched_prohibited: [function({g, xx1}) { return g.mergeV(new Map([])).option(Merge.onMatch,xx1) }], 
     g_V_age_min: [function({g}) { return g.V().values("age").min() }], 
     g_V_foo_min: [function({g}) { return g.V().values("foo").min() }], 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index ff19fc3506..5c156db166 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -938,6 +938,9 @@ world.gremlins = {
     'g_mergeVXname_markoX_optionXonMatch_age_33_singleX': [(lambda g:g.addV('person').property('name','marko').property(Cardinality.list_,'age',29).property(Cardinality.list_,'age',31).property(Cardinality.list_,'age',32)), (lambda g:g.merge_v({'name':'marko'}).option(Merge.on_match,{'age':33},Cardinality.single)), (lambda g:g.V().has('person','name','marko').has('age',33)), (lambda g:g.V().has('person','name','marko').has('age')), (lambda g:g.V().has('person','name','marko').properties( [...]
     'g_mergeVXname_markoX_optionXonMatch_name_allen_age_setX31X_singleX': [(lambda g:g.addV('person').property('name','marko').property(Cardinality.list_,'age',29).property(Cardinality.list_,'age',31).property(Cardinality.list_,'age',32)), (lambda g:g.merge_v({'name':'marko'}).option(Merge.on_match,{'name':'allen','age':CardinalityValue.set_(31)},Cardinality.single)), (lambda g:g.V().has('person','name','marko')), (lambda g:g.V().has('person','name','allen').has('age',31)), (lambda g:g.V [...]
     'g_mergeVXname_markoX_optionXonMatch_name_allen_age_singleX31X_singleX': [(lambda g:g.addV('person').property('name','marko').property(Cardinality.list_,'age',29).property(Cardinality.list_,'age',31).property(Cardinality.list_,'age',32)), (lambda g:g.merge_v({'name':'marko'}).option(Merge.on_match,{'name':'allen','age':CardinalityValue.single(31)},Cardinality.single)), (lambda g:g.V().has('person','name','marko')), (lambda g:g.V().has('person','name','allen').has('age',33)), (lambda  [...]
+    'g_mergeVXname_aliceX_optionXonCreate_age_singleX81XX': [(lambda g:g.merge_v({'name':'alice',T.label:'person'}).option(Merge.on_create,{'age':CardinalityValue.single(81)})), (lambda g:g.V().has('person','name','alice').has('age',81)), (lambda g:g.V().has('person','name','alice').has('age')), (lambda g:g.V().has('person','name','alice').properties('age'))], 
+    'g_mergeVXname_aliceX_optionXonCreate_age_setX81XX': [(lambda g:g.merge_v({'name':'alice',T.label:'person'}).option(Merge.on_create,{'age':CardinalityValue.set_(81)})), (lambda g:g.V().has('person','name','alice').has('age',81)), (lambda g:g.V().has('person','name','alice').has('age')), (lambda g:g.V().has('person','name','alice').properties('age'))], 
+    'g_mergeVXname_aliceX_optionXonCreate_age_singleX81X_age_81_setX': [(lambda g:g.merge_v({'name':'alice',T.label:'person'}).option(Merge.on_create,{'age':81},Cardinality.set_)), (lambda g:g.V().has('person','name','alice').has('age',81)), (lambda g:g.V().has('person','name','alice').has('age')), (lambda g:g.V().has('person','name','alice').properties('age'))], 
     'g_mergeV_hidden_label_key_onMatch_matched_prohibited': [(lambda g, xx1=None:g.merge_v({}).option(Merge.on_match,xx1))], 
     'g_V_age_min': [(lambda g:g.V().age.min_())], 
     'g_V_foo_min': [(lambda g:g.V().foo.min_())], 
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature
index 7c015e3496..af34ce42dd 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/MergeVertex.feature
@@ -926,7 +926,7 @@ Feature: Step - mergeV()
       g.addV("person").property("name", "marko").property(Cardinality.list, "age", 29).property(Cardinality.list, "age", 31).property(Cardinality.list, "age", 32)
       """
     And the traversal of
-      """
+      """e
       g.mergeV([name: "marko"]).
           option(Merge.onMatch, [name: "allen", age: Cardinality.set(31)], single)
       """
@@ -957,6 +957,48 @@ Feature: Step - mergeV()
     And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"allen\").has(\"age\")"
     And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"allen\").properties(\"age\")"
 
+  @MultiProperties
+  Scenario: g_mergeVXname_aliceX_optionXonCreate_age_singleX81XX
+    Given the empty graph
+    And the traversal of
+      """
+      g.mergeV([name: "alice", (T.label): "person"]).
+          option(Merge.onCreate, [age: Cardinality.single(81)])
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"alice\").has(\"age\", 81)"
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"alice\").has(\"age\")"
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"alice\").properties(\"age\")"
+
+  @MultiProperties
+  Scenario: g_mergeVXname_aliceX_optionXonCreate_age_setX81XX
+    Given the empty graph
+    And the traversal of
+      """
+      g.mergeV([name: "alice", (T.label): "person"]).
+          option(Merge.onCreate, [age: Cardinality.set(81)])
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"alice\").has(\"age\", 81)"
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"alice\").has(\"age\")"
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"alice\").properties(\"age\")"
+
+  @MultiProperties
+  Scenario: g_mergeVXname_aliceX_optionXonCreate_age_singleX81X_age_81_setX
+    Given the empty graph
+    And the traversal of
+      """
+      g.mergeV([name: "alice", (T.label): "person"]).
+          option(Merge.onCreate, [age: 81], Cardinality.set)
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"alice\").has(\"age\", 81)"
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"alice\").has(\"age\")"
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"alice\").properties(\"age\")"
+
   # cannot use hidden namespace for label key for onMatch
   Scenario: g_mergeV_hidden_label_key_onMatch_matched_prohibited
     Given the empty graph