[ 
https://issues.apache.org/jira/browse/TINKERPOP-2824?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17690453#comment-17690453
 ] 

ASF GitHub Bot commented on TINKERPOP-2824:
-------------------------------------------

FlorianHockmann commented on code in PR #1843:
URL: https://github.com/apache/tinkerpop/pull/1843#discussion_r1108148751


##########
gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/EdgeSerializer.cs:
##########
@@ -45,15 +46,17 @@ public EdgeSerializer() : base(DataType.Edge)
         {
             await writer.WriteAsync(value.Id, stream, 
cancellationToken).ConfigureAwait(false);
             await writer.WriteNonNullableValueAsync(value.Label, stream, 
cancellationToken).ConfigureAwait(false);
-            
+
+            // !!! await writer.WriteValueAsync(value.Label, stream, false, 
cancellationToken).ConfigureAwait(false);

Review Comment:
   ?



##########
gremlin-dotnet/src/Gremlin.Net/Structure/Edge.cs:
##########
@@ -52,6 +55,15 @@ public Edge(object? id, Vertex outV, string label, Vertex 
inV)
         /// </summary>
         public Vertex OutV { get; }
 
+        /// <summary>
+        /// Get property by key
+        /// </summary>
+        /// <returns>property or null when not found</returns>
+        public Property? Property(string key)

Review Comment:
   Any reason against implementing this in `Element` instead of here and in 
`Vertex`?



##########
gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/RemoteConnectionFactory.cs:
##########
@@ -49,19 +49,32 @@ public IRemoteConnection CreateRemoteConnection(int 
connectionPoolSize = 2)
             return CreateRemoteConnection("gmodern", connectionPoolSize);
         }
 
-        public IRemoteConnection CreateRemoteConnection(string 
traversalSource, int connectionPoolSize = 2)
+        public IRemoteConnection CreateRemoteConnection(string 
traversalSource, 
+            int connectionPoolSize = 2,
+            IMessageSerializer? messageSerializer = null)
         {
             var c = new DriverRemoteConnectionImpl(
-                new GremlinClient(new GremlinServer(TestHost, TestPort), 
_messageSerializer,
+                new GremlinClient(new GremlinServer(TestHost, TestPort), 

Review Comment:
   (nitpick) Why not call the newly added method `CreateClient` here?



##########
gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/client-tests.js:
##########
@@ -75,6 +75,68 @@ describe('Client', function () {
         });
     });
 
+    it('should handle properties for bytecode request', function () {
+      return client.submit(new Bytecode().addStep('V', [1]))
+        .then(function (result) {
+          assert.ok(result);
+          assert.strictEqual(result.length, 1);
+          const vertex = result.first().object;
+          assert.ok(vertex instanceof graphModule.Vertex);
+          let age, name
+          if (vertex.properties instanceof Array) {
+            age = vertex.properties[1]
+            name = vertex.properties[0]
+          } else {
+            age = vertex.properties.age[0]
+            name = vertex.properties.name[0]
+          }
+          assert.strictEqual(age.value, 29);
+          assert.strictEqual(name.value, 'marko');
+        });
+    });
+
+    it('should skip properties for bytecode request', function () {

Review Comment:
   (nitpick)
   
   ```suggestion
       it('should skip properties for bytecode request with tokens', function 
() {
   ```



##########
gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/RemoteConnectionFactory.cs:
##########
@@ -49,19 +49,32 @@ public IRemoteConnection CreateRemoteConnection(int 
connectionPoolSize = 2)
             return CreateRemoteConnection("gmodern", connectionPoolSize);
         }
 
-        public IRemoteConnection CreateRemoteConnection(string 
traversalSource, int connectionPoolSize = 2)
+        public IRemoteConnection CreateRemoteConnection(string 
traversalSource, 
+            int connectionPoolSize = 2,
+            IMessageSerializer? messageSerializer = null)
         {
             var c = new DriverRemoteConnectionImpl(
-                new GremlinClient(new GremlinServer(TestHost, TestPort), 
_messageSerializer,
+                new GremlinClient(new GremlinServer(TestHost, TestPort), 
+                    messageSerializer ?? _messageSerializer,
                     connectionPoolSettings: new ConnectionPoolSettings { 
PoolSize = connectionPoolSize }),
                 traversalSource);
-            _connections.Add(c);
+            _cleanUp.Add(c);
+            return c;
+        }
+
+        public IGremlinClient CreateClient(IMessageSerializer 
messageSerializer = null)

Review Comment:
   Please add a nullable annotation:
   
   ```suggestion
           public IGremlinClient CreateClient(IMessageSerializer? 
messageSerializer = null)
   ```



##########
gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/PropertyDeserializationTests.cs:
##########
@@ -0,0 +1,247 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#endregion
+
+using Gremlin.Net.Driver;
+using Gremlin.Net.IntegrationTest.Process.Traversal.DriverRemoteConnection;
+using Gremlin.Net.Process.Traversal;
+using Gremlin.Net.Structure;
+using Gremlin.Net.Structure.IO.GraphBinary;
+using Gremlin.Net.Structure.IO.GraphSON;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Gremlin.Net.IntegrationTest.Driver
+{
+    public class PropertyDeserializationTests
+    {
+        private readonly RemoteConnectionFactory _connectionFactory = new();
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public void 
ShouldDeserializeVertexPropertiesForBytecode(IMessageSerializer serializer)
+        {
+            var connection = 
_connectionFactory.CreateRemoteConnection("gmodern", 2, serializer);
+            var g = 
AnonymousTraversalSource.Traversal().WithRemote(connection);
+
+            var vertex = g.V(1).Next();
+
+            VerifyVertexProperties(vertex);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public void 
ShouldRespectMaterializePropertiesTokensForBytecode(IMessageSerializer 
serializer)
+        {
+            var connection = 
_connectionFactory.CreateRemoteConnection("gmodern", 2, serializer);
+            var g = 
AnonymousTraversalSource.Traversal().WithRemote(connection);
+
+            var vertex = g.With(Tokens.ArgMaterializeProperties, 
"tokens").V(1).Next();
+
+            VerifyEmptyProperties(vertex);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public void 
ShouldRespectMaterializePropertiesAllForBytecode(IMessageSerializer serializer)
+        {
+            var connection = 
_connectionFactory.CreateRemoteConnection("gmodern", 2, serializer);
+            var g = 
AnonymousTraversalSource.Traversal().WithRemote(connection);
+
+            var vertex = g.With(Tokens.ArgMaterializeProperties, 
"all").V(1).Next();
+
+            VerifyVertexProperties(vertex);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public void 
ShouldHandleEmptyVertexPropertiesForBytecode(IMessageSerializer serializer)
+        {
+            var connection = 
_connectionFactory.CreateRemoteConnection("gimmutable", 2, serializer);
+            var g = 
AnonymousTraversalSource.Traversal().WithRemote(connection);
+
+            var vertex = g.AddV("test").Next();
+
+            VerifyEmptyProperties(vertex);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public async Task 
ShouldDeserializeVertexPropertiesForGremlin(IMessageSerializer serializer)
+        {
+            var client = _connectionFactory.CreateClient(serializer);
+
+            var vertex = await 
client.SubmitWithSingleResultAsync<Vertex>("gmodern.V(1)");
+
+            VerifyVertexProperties(vertex);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public async Task 
ShouldHandleEmptyVertexPropertiesForGremlin(IMessageSerializer serializer)
+        {
+            var client = _connectionFactory.CreateClient(serializer);
+
+            var vertex = await 
client.SubmitWithSingleResultAsync<Vertex>("gimmutable.addV('test')");
+
+            VerifyEmptyProperties(vertex);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public async Task 
ShouldRespectMaterializePropertiesAllForGremlin(IMessageSerializer serializer)
+        {
+            var client = _connectionFactory.CreateClient(serializer);
+
+            var vertex = await 
client.SubmitWithSingleResultAsync<Vertex>("gmodern.with('materializeProperties',
 'all').V(1)");
+
+            VerifyVertexProperties(vertex);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public async Task 
ShouldRespectMaterializePropertiesTokensForGremlin(IMessageSerializer 
serializer)
+        {
+            var client = _connectionFactory.CreateClient(serializer);
+
+            var vertex = await 
client.SubmitWithSingleResultAsync<Vertex>("gmodern.with('materializeProperties',
 'tokens').V(1)");
+
+            VerifyEmptyProperties(vertex);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public void 
ShouldDeserializeEdgePropertiesForBytecode(IMessageSerializer serializer)
+        {
+            var connection = 
_connectionFactory.CreateRemoteConnection("gmodern", 2, serializer);
+            var g = 
AnonymousTraversalSource.Traversal().WithRemote(connection);
+
+            var edge = g.E(7).Next();
+
+            VerifyEdgeProperties(edge);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public void 
ShouldHandleEmptyEdgePropertiesForBytecode(IMessageSerializer serializer)
+        {
+            var connection = 
_connectionFactory.CreateRemoteConnection("gimmutable", 2, serializer);
+            var g = 
AnonymousTraversalSource.Traversal().WithRemote(connection);
+
+            var v1 = g.AddV("v1").Next();
+            var v2 = g.AddV("v2").Next();
+            var edge = g.AddE("test").From(v1).To(v2).Next();
+
+            VerifyEmptyProperties(edge);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public async Task 
ShouldDeserializeEdgePropertiesForGremlin(IMessageSerializer serializer)
+        {
+            var client = _connectionFactory.CreateClient(serializer);
+
+            var edge = await 
client.SubmitWithSingleResultAsync<Edge>("gmodern.E(7)");
+
+            VerifyEdgeProperties(edge);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public async Task 
ShouldHandleEmptyEdgePropertiesForGremlin(IMessageSerializer serializer)
+        {
+            var client = _connectionFactory.CreateClient(serializer);
+
+            var edge = await client.SubmitWithSingleResultAsync<Edge>(
+                
"gimmutable.addV().as('v1').addV().as('v2').addE('test').from('v1').to('v2')");
+
+            VerifyEmptyProperties(edge);
+        }
+
+        [Theory]
+        [MemberData(nameof(Serializers))]
+        public void 
ShouldHandleMultiplePropertiesWithSameNameForVertex(IMessageSerializer 
serializer)
+        {
+            var connection = 
_connectionFactory.CreateRemoteConnection("gimmutable", 2, serializer);
+            var g = 
AnonymousTraversalSource.Traversal().WithRemote(connection);
+
+            var vertex = g.AddV()
+                .Property(Cardinality.List, "test", "value1")
+                .Property(Cardinality.List, "test", "value2")
+                .Property(Cardinality.List, "test", "value3")
+                .Next()!;
+
+            vertex = g.V(vertex.Id).Next();
+
+            Assert.NotNull(vertex);
+
+            var properties = vertex.Properties!;
+            Assert.Equal(3, properties.Length);
+            var propertyValues = properties.Cast<VertexProperty>().Where(p => 
p.Key == "test").Select(p => p.Value).ToArray();
+            Assert.Equal(3, propertyValues.Length);
+            Assert.Equal(new[] { "value1", "value2", "value3" }, 
propertyValues);
+        }
+
+        private static void VerifyVertexProperties(Vertex? vertex)
+        {
+            Assert.NotNull(vertex);
+            Assert.Equal(1, vertex.Id);
+
+            Assert.Equal(2, vertex.Properties!.Length);
+            var age = vertex.Property("age");
+            Assert.NotNull(age);
+            Assert.Equal(29, age.Value);
+        }
+
+        private static void VerifyEdgeProperties(Edge? edge)
+        {
+            Assert.NotNull(edge);
+            Assert.Equal(7, edge.Id);
+
+            Assert.Single(edge.Properties!);
+            var weight = edge.Property("weight");
+            Assert.NotNull(weight);
+            Assert.Equal(0.5, weight.Value);
+        }
+
+        private static void VerifyEmptyProperties(Vertex? vertex)

Review Comment:
   Not that important but you could easily let this take an `Element` instead 
and then remove the method below.



##########
gremlin-go/driver/graphBinary.go:
##########
@@ -1052,8 +1052,8 @@ func vertexReader(data *[]byte, i *int) (interface{}, 
error) {
        return vertexReaderNullByte(data, i, true)
 }
 
-// {fully qualified id}{unqualified label}{[unused null byte]}
-func vertexReaderNullByte(data *[]byte, i *int, unusedByte bool) (interface{}, 
error) {
+// {fully qualified id}{unqualified label}{fully qualified properties}
+func vertexReaderNullByte(data *[]byte, i *int, readProperties bool) 
(interface{}, error) {

Review Comment:
   Shouldn't this now be named `vertexReaderReadingProperties` or something 
like that?



##########
gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/client-tests.js:
##########
@@ -75,6 +75,68 @@ describe('Client', function () {
         });
     });
 
+    it('should handle properties for bytecode request', function () {
+      return client.submit(new Bytecode().addStep('V', [1]))
+        .then(function (result) {
+          assert.ok(result);
+          assert.strictEqual(result.length, 1);
+          const vertex = result.first().object;
+          assert.ok(vertex instanceof graphModule.Vertex);
+          let age, name
+          if (vertex.properties instanceof Array) {
+            age = vertex.properties[1]
+            name = vertex.properties[0]
+          } else {
+            age = vertex.properties.age[0]
+            name = vertex.properties.name[0]
+          }
+          assert.strictEqual(age.value, 29);
+          assert.strictEqual(name.value, 'marko');
+        });
+    });
+
+    it('should skip properties for bytecode request', function () {
+      return client.submit(new Bytecode().addStep('V', [1]), null, 
{'materializeProperties': 'tokens'})
+        .then(function (result) {
+          assert.ok(result);
+          assert.strictEqual(result.length, 1);
+          const vertex = result.first().object;
+          assert.ok(vertex instanceof graphModule.Vertex);
+          assert.ok(vertex.properties === undefined || 
vertex.properties.length === 0);
+        });
+    });
+
+    it('should handle properties for gremlin request', function () {
+      return client.submit('g.V(1)')
+        .then(function (result) {
+          assert.ok(result);
+          assert.strictEqual(result.length, 1);
+          const vertex = result.first();
+          assert.ok(vertex instanceof graphModule.Vertex);
+          let age, name
+          if (vertex.properties instanceof Array) {
+            age = vertex.properties[1]
+            name = vertex.properties[0]
+          } else {
+            age = vertex.properties.age[0]
+            name = vertex.properties.name[0]
+          }
+          assert.strictEqual(age.value, 29);
+          assert.strictEqual(name.value, 'marko');
+        });
+    });
+
+    it('should skip properties for gremlin request', function () {

Review Comment:
   ```suggestion
       it('should skip properties for gremlin request with tokens', function () 
{
   ```



##########
gremlin-python/src/main/python/tests/structure/io/test_functionalityio.py:
##########
@@ -25,6 +25,34 @@
 from gremlin_python.statics import *
 
 
+def test_vertex(remote_connection):
+    g = Graph().traversal().withRemote(remote_connection)
+    vertex = g.V(1).next()
+    assert vertex.id == 1
+    assert len(vertex.properties) == 2
+    assert vertex.properties[0].key == 'name'
+    assert vertex.properties[0].value == 'marko'
+    assert vertex.properties[1].key == 'age'
+    assert vertex.properties[1].value == 29
+
+
+def test_vertex_without_properties(remote_connection):
+    g = Graph().traversal().withRemote(remote_connection)
+    vertex = g.with_('materializeProperties', 'tokens').V(1).next()
+    assert vertex.id == 1
+    # empty array for GraphBinary and missing field for GraphSON
+    assert vertex.properties is None or len(vertex.properties) == 0
+
+
+def test_edge(remote_connection):

Review Comment:
   (nitpick) You could also add a test for an edge with `tokens`.



##########
gremlin-server/src/test/scripts/generate-all.groovy:
##########
@@ -61,7 +61,7 @@ globals << [hook : [
 
 // add default TraversalSource instances for each graph instance
 globals << [gclassic : 
traversal().withEmbedded(classic).withStrategies(ReferenceElementStrategy)]
-globals << [gmodern : 
traversal().withEmbedded(modern).withStrategies(ReferenceElementStrategy)]
+globals << [gmodern : traversal().withEmbedded(modern)]

Review Comment:
   Just for my own understanding: You're removing this here so `gmodern` can be 
used by tests where we want to get properties back, right?



##########
gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js:
##########
@@ -131,17 +131,30 @@ Given(/^using the parameter (.+) defined as "(.+)"$/, 
function (paramName, strin
   });
 });
 
+var removeProperties = function(p) {
+  if (p === undefined) {   
+  } else if (p instanceof graphModule.Vertex || p instanceof graphModule.Edge) 
{
+    p.properties = undefined;
+  } else if (p instanceof Array) {
+    p.forEach(removeProperties)
+  } else if (p instanceof Map) {
+    removeProperties(Array.from(p.keys()))
+    removeProperties(Array.from(p.values()))
+  } else if (p instanceof graphModule.Path) {
+    removeProperties(p.objects)
+  }
+
+  return p
+}
+
 When('iterated to list', function () {
-  return this.traversal.toList().then(list => this.result = list).catch(err => 
this.result = err);
+  return this.traversal.toList().then(list => this.result = 
removeProperties(list)).catch(err => this.result = err);

Review Comment:
   Shouldn't we make sure that things like element equality is handled the same 
way across GLVs? I think JS should also treat elements as equal if their IDs 
are equal to stay consistent with Java & .NET.
   
   But that's probably out of scope for this PR. We can also create an issue 
for this if we agree that all GLVs should behave the same in this regard.





> Properties on Elements
> ----------------------
>
>                 Key: TINKERPOP-2824
>                 URL: https://issues.apache.org/jira/browse/TINKERPOP-2824
>             Project: TinkerPop
>          Issue Type: Improvement
>          Components: dotnet, driver, go, javascript, process, python
>    Affects Versions: 3.5.4
>            Reporter: Valentyn Kahamlyk
>            Assignee: Valentyn Kahamlyk
>            Priority: Major
>
> Problem: When a user writes `g.V()` they get back a Vertex object. The 
> problem is that depending on the execution context of the traversal, the 
> result could be quite different, with or without properties.
> Solution: Implement new finalization strategy DetachStrategy(detachMode, 
> properties) where mode is one of ALL, NONE or CUSTOM. `properties` is list of 
> properties name, are taken into account only for CUSTOM mode.
> Discussion thread in dev list: [Proposal to handle properties on response 
> Elements-Apache Mail 
> Archives|https://lists.apache.org/thread/l8rw7ydj7kym8vhtwk50nhbp45ng9986]
> Stephen's thread in dev list: [The Issue of Detachment-Apache Mail 
> Archives|https://lists.apache.org/thread/xltcon4zxnwq4fyw2r2126syyrqm8spy]



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to