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

commit 97c777c9848f66f6ccd573f7c7d90e8496b17347
Author: Stephen Mallette <[email protected]>
AuthorDate: Wed Jul 15 12:12:00 2026 -0400

    Preserve multi/zero-label vertices in TinkerGraphComputer result graph
    
    Fixes 'Label can not be empty' when an OLAP job (e.g. connectedComponent)
    persists a new result graph over a TinkerGraph with a multi-label vertex
    cardinality. The result graph now keeps the source cardinality and copies
    each vertex's full label set instead of a single label.
    
    Assisted-by: Claude Code:claude-opus-4-8
---
 .../process/computer/TinkerGraphComputerView.java  | 52 +++++++-----
 .../TinkerGraphComputerMultiLabelTest.java         | 96 ++++++++++++++++++++++
 2 files changed, 127 insertions(+), 21 deletions(-)

diff --git 
a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java
 
b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java
index e3d94b2cf9..41a0b74df2 100644
--- 
a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java
+++ 
b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java
@@ -18,11 +18,12 @@
  */
 package org.apache.tinkerpop.gremlin.tinkergraph.process.computer;
 
+import org.apache.commons.configuration2.BaseConfiguration;
+import org.apache.commons.configuration2.Configuration;
 import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
 import org.apache.tinkerpop.gremlin.process.computer.GraphFilter;
 import org.apache.tinkerpop.gremlin.process.computer.VertexComputeKey;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep;
-import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Graph;
@@ -171,16 +172,8 @@ public final class TinkerGraphComputerView {
                 this.addPropertiesToOriginalGraph();
                 return this.graph;
             } else {
-                final TinkerGraph newGraph = TinkerGraph.open();
-                this.graph.vertices().forEachRemaining(vertex -> {
-                    final Vertex newVertex = newGraph.addVertex(T.id, 
vertex.id(), T.label, vertex.label());
-                    vertex.properties().forEachRemaining(vertexProperty -> {
-                        final VertexProperty<?> newVertexProperty = 
newVertex.property(VertexProperty.Cardinality.list, vertexProperty.key(), 
vertexProperty.value(), T.id, vertexProperty.id());
-                        vertexProperty.properties().forEachRemaining(property 
-> {
-                            newVertexProperty.property(property.key(), 
property.value());
-                        });
-                    });
-                });
+                final TinkerGraph newGraph = createResultGraph();
+                this.graph.vertices().forEachRemaining(vertex -> 
copyVertexToResultGraph(newGraph, vertex));
                 return newGraph;
             }
         } else {  // Persist.EDGES
@@ -188,16 +181,8 @@ public final class TinkerGraphComputerView {
                 this.addPropertiesToOriginalGraph();
                 return this.graph;
             } else {
-                final TinkerGraph newGraph = TinkerGraph.open();
-                this.graph.vertices().forEachRemaining(vertex -> {
-                    final Vertex newVertex = newGraph.addVertex(T.id, 
vertex.id(), T.label, vertex.label());
-                    vertex.properties().forEachRemaining(vertexProperty -> {
-                        final VertexProperty<?> newVertexProperty = 
newVertex.property(VertexProperty.Cardinality.list, vertexProperty.key(), 
vertexProperty.value(), T.id, vertexProperty.id());
-                        vertexProperty.properties().forEachRemaining(property 
-> {
-                            newVertexProperty.property(property.key(), 
property.value());
-                        });
-                    });
-                });
+                final TinkerGraph newGraph = createResultGraph();
+                this.graph.vertices().forEachRemaining(vertex -> 
copyVertexToResultGraph(newGraph, vertex));
                 this.graph.edges().forEachRemaining(edge -> {
                     final Vertex outVertex = 
newGraph.vertices(edge.outVertex().id()).next();
                     final Vertex inVertex = 
newGraph.vertices(edge.inVertex().id()).next();
@@ -209,6 +194,31 @@ public final class TinkerGraphComputerView {
         }
     }
 
+    /**
+     * Opens a new result graph that preserves the source graph's vertex 
{@code LabelCardinality} so that
+     * multi-label and zero-label vertices can be persisted without violating 
the default cardinality.
+     */
+    private TinkerGraph createResultGraph() {
+        final Configuration conf = new BaseConfiguration();
+        
conf.setProperty(AbstractTinkerGraph.GREMLIN_TINKERGRAPH_VERTEX_LABEL_CARDINALITY,
+                this.graph.features().vertex().getLabelCardinality().name());
+        return TinkerGraph.open(conf);
+    }
+
+    /**
+     * Copies a vertex and its (meta-)properties into the result graph, 
carrying over all of its labels so
+     * that vertices with zero or multiple labels round-trip correctly.
+     */
+    private static void copyVertexToResultGraph(final TinkerGraph newGraph, 
final Vertex vertex) {
+        final Vertex newVertex = newGraph.addVertex(T.id, vertex.id(), 
T.label, vertex.labels());
+        vertex.properties().forEachRemaining(vertexProperty -> {
+            final VertexProperty<?> newVertexProperty = 
newVertex.property(VertexProperty.Cardinality.list, vertexProperty.key(), 
vertexProperty.value(), T.id, vertexProperty.id());
+            vertexProperty.properties().forEachRemaining(property -> {
+                newVertexProperty.property(property.key(), property.value());
+            });
+        });
+    }
+
     private void addPropertiesToOriginalGraph() {
         TinkerHelper.dropGraphComputerView(this.graph);
         this.computeProperties.forEach((element, properties) -> {
diff --git 
a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerMultiLabelTest.java
 
b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerMultiLabelTest.java
new file mode 100644
index 0000000000..dd835089c2
--- /dev/null
+++ 
b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerMultiLabelTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+package org.apache.tinkerpop.gremlin.tinkergraph.process.computer;
+
+import org.apache.commons.configuration2.BaseConfiguration;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.tinkerpop.gremlin.process.computer.ComputerResult;
+import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
+import 
org.apache.tinkerpop.gremlin.process.computer.clustering.connected.ConnectedComponentVertexProgram;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.tinkergraph.structure.AbstractTinkerGraph;
+import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasSize;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Verifies that a {@code ResultGraph.NEW} produced by {@link 
org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComputer}
+ * preserves multi-label and zero-label vertices rather than collapsing them 
to a single label (which previously
+ * threw {@code "Label can not be empty"} for zero-label vertices).
+ */
+public class TinkerGraphComputerMultiLabelTest {
+
+    private static TinkerGraph openMultiLabelGraph() {
+        final Configuration config = new BaseConfiguration();
+        
config.setProperty(AbstractTinkerGraph.GREMLIN_TINKERGRAPH_VERTEX_LABEL_CARDINALITY,
 "ZERO_OR_MORE");
+        return TinkerGraph.open(config);
+    }
+
+    private static Graph computeNewGraph(final TinkerGraph graph, final 
GraphComputer.Persist persist) throws Exception {
+        final ComputerResult result = graph.compute()
+                .program(ConnectedComponentVertexProgram.build().create(graph))
+                .result(GraphComputer.ResultGraph.NEW)
+                .persist(persist)
+                .submit().get();
+        return result.graph();
+    }
+
+    @Test
+    public void shouldPreserveVertexLabelsInNewResultGraphWithPersistEdges() 
throws Exception {
+        try (final TinkerGraph graph = openMultiLabelGraph()) {
+            final Vertex a = graph.addVertex(T.id, "a", T.label, 
Arrays.asList("person", "employee"));
+            final Vertex b = graph.addVertex(T.id, "b", T.label, "person");
+            graph.addVertex(T.id, "c", T.label, Collections.emptyList());
+            a.addEdge("knows", b);
+
+            final Graph result = computeNewGraph(graph, 
GraphComputer.Persist.EDGES);
+
+            assertThat(result.vertices("a").next().labels(), 
containsInAnyOrder("person", "employee"));
+            assertThat(result.vertices("b").next().labels(), 
containsInAnyOrder("person"));
+            assertThat(result.vertices("c").next().labels(), hasSize(0));
+            assertEquals(1L, 
result.traversal().E().count().next().longValue());
+        }
+    }
+
+    @Test
+    public void 
shouldPreserveVertexLabelsInNewResultGraphWithPersistVertexProperties() throws 
Exception {
+        try (final TinkerGraph graph = openMultiLabelGraph()) {
+            final Vertex a = graph.addVertex(T.id, "a", T.label, 
Arrays.asList("person", "employee"));
+            final Vertex b = graph.addVertex(T.id, "b", T.label, "person");
+            graph.addVertex(T.id, "c", T.label, Collections.emptyList());
+            a.addEdge("knows", b);
+
+            final Graph result = computeNewGraph(graph, 
GraphComputer.Persist.VERTEX_PROPERTIES);
+
+            assertThat(result.vertices("a").next().labels(), 
containsInAnyOrder("person", "employee"));
+            assertThat(result.vertices("b").next().labels(), 
containsInAnyOrder("person"));
+            assertThat(result.vertices("c").next().labels(), hasSize(0));
+            assertEquals(0L, 
result.traversal().E().count().next().longValue());
+        }
+    }
+}

Reply via email to