From 050d24c721d521a4fe83e739582bb6dd2d707e28 Mon Sep 17 00:00:00 2001 From: contrueCT Date: Sun, 12 Apr 2026 00:33:54 +0800 Subject: [PATCH 1/3] fix(query): handle conflicting edge label conditions safely HugeGraph may read LABEL from a ConditionQuery before the query is flattened when optimizing edge traversals. In contradictory label combinations such as inE('created').hasLabel('created', 'look').hasLabel('authored'), the previous intersection logic reused an empty set to mean both 'not initialized yet' and 'already intersected to empty'. That allowed later IN conditions to repopulate the candidate set and raised an Illegal key 'LABEL' with more than one value error instead of returning an empty result. Track whether the intersection has been initialized independently so an empty intersection remains empty. This keeps the existing protection for true multi-value results, while allowing conflicting label predicates to fall back safely and produce no matches. Add regression coverage for the low-level ConditionQuery behavior and for the edge traversal scenario from issue #2933, including a match()-based equivalent query to assert consistent zero-count results. --- .../backend/query/ConditionQuery.java | 7 +++-- .../apache/hugegraph/core/EdgeCoreTest.java | 24 ++++++++++++++ .../apache/hugegraph/unit/core/QueryTest.java | 31 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/ConditionQuery.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/ConditionQuery.java index 8a5706a774..063d23aa6d 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/ConditionQuery.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/ConditionQuery.java @@ -288,11 +288,13 @@ public T condition(Object key) { return value; } + boolean initialized = false; Set intersectValues = InsertionOrderUtil.newSet(); for (Object value : valuesEQ) { List valueAsList = ImmutableList.of(value); - if (intersectValues.isEmpty()) { + if (!initialized) { intersectValues.addAll(valueAsList); + initialized = true; } else { CollectionUtil.intersectWithModify(intersectValues, valueAsList); @@ -301,8 +303,9 @@ public T condition(Object key) { for (Object value : valuesIN) { @SuppressWarnings("unchecked") List valueAsList = (List) value; - if (intersectValues.isEmpty()) { + if (!initialized) { intersectValues.addAll(valueAsList); + initialized = true; } else { CollectionUtil.intersectWithModify(intersectValues, valueAsList); diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java index 265d408742..122ee04393 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java @@ -4646,6 +4646,30 @@ public void testQueryInEdgesOfVertexByLabels() { Assert.assertEquals(3L, size); } + @Test + public void testQueryInEdgesOfVertexByConflictingLabels() { + HugeGraph graph = graph(); + init18Edges(); + + long direct = graph.traversal().V().inE("created") + .hasLabel("created", "look") + .hasLabel("authored") + .count().next(); + Assert.assertEquals(0L, direct); + + long matched = graph.traversal().V() + .match(__.as("start1") + .repeat(__.inE("created")) + .times(1) + .as("m1")) + .select("m1") + .hasLabel("created", "look") + .hasLabel("authored") + .count().next(); + Assert.assertEquals(0L, matched); + Assert.assertEquals(matched, direct); + } + @Test public void testQueryInEdgesOfVertexBySortkey() { HugeGraph graph = graph(); diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/QueryTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/QueryTest.java index 88b161d32a..70f06b8230 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/QueryTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/QueryTest.java @@ -17,8 +17,10 @@ package org.apache.hugegraph.unit.core; +import org.apache.hugegraph.backend.id.Id; import org.apache.hugegraph.backend.id.IdGenerator; import org.apache.hugegraph.backend.query.Aggregate.AggregateFunc; +import org.apache.hugegraph.backend.query.Condition; import org.apache.hugegraph.backend.query.ConditionQuery; import org.apache.hugegraph.backend.query.IdPrefixQuery; import org.apache.hugegraph.backend.query.IdQuery; @@ -30,6 +32,7 @@ import org.apache.hugegraph.type.define.HugeKeys; import org.junit.Test; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -45,6 +48,34 @@ public void testOrderBy() { query.orders()); } + @Test + public void testConditionWithEqAndIn() { + Id label1 = IdGenerator.of(1); + Id label2 = IdGenerator.of(2); + + ConditionQuery query = new ConditionQuery(HugeType.EDGE); + query.eq(HugeKeys.LABEL, label1); + query.query(Condition.in(HugeKeys.LABEL, + ImmutableList.of(label1, label2))); + + Assert.assertEquals(label1, query.condition(HugeKeys.LABEL)); + } + + @Test + public void testConditionWithConflictingEqAndIn() { + Id label1 = IdGenerator.of(1); + Id label2 = IdGenerator.of(2); + Id label3 = IdGenerator.of(3); + + ConditionQuery query = new ConditionQuery(HugeType.EDGE); + query.eq(HugeKeys.LABEL, label1); + query.eq(HugeKeys.LABEL, label2); + query.query(Condition.in(HugeKeys.LABEL, + ImmutableList.of(label1, label3))); + + Assert.assertNull(query.condition(HugeKeys.LABEL)); + } + @Test public void testToString() { Query query = new Query(HugeType.VERTEX); From 0c97f0077fabe94f3ecca7246752ebb0f798f552 Mon Sep 17 00:00:00 2001 From: contrueCT Date: Sun, 12 Apr 2026 15:19:12 +0800 Subject: [PATCH 2/3] test(query): simplify #2933 match traversal regression Replace the match() control query in EdgeCoreTest with a direct inE() traversal instead of repeat(...).times(1). The revised form keeps the regression focused on comparing the direct traversal with an equivalent match()-based query while avoiding the generic mismatch that prevented the test from compiling. --- .../src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java index 122ee04393..6bec6dc06f 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java @@ -4659,8 +4659,7 @@ public void testQueryInEdgesOfVertexByConflictingLabels() { long matched = graph.traversal().V() .match(__.as("start1") - .repeat(__.inE("created")) - .times(1) + .inE("created") .as("m1")) .select("m1") .hasLabel("created", "look") From 33e7b3b2dc4f45ed7e73495bc65c5249da156f50 Mon Sep 17 00:00:00 2001 From: contrueCT Date: Sun, 12 Apr 2026 16:14:21 +0800 Subject: [PATCH 3/3] test(query): cover multi-value label intersections Add a low-level regression test for ConditionQuery.condition(HugeKeys.LABEL) when multiple IN predicates intersect to more than one candidate label. This locks in the existing Illegal key 'LABEL' guard so the #2933 fix only changes the empty-intersection case and does not silently relax true multi-value results. --- .../apache/hugegraph/unit/core/QueryTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/QueryTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/QueryTest.java index 70f06b8230..7d48084dbf 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/QueryTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/QueryTest.java @@ -76,6 +76,25 @@ public void testConditionWithConflictingEqAndIn() { Assert.assertNull(query.condition(HugeKeys.LABEL)); } + @Test + public void testConditionWithMultipleMatchedInValues() { + Id label1 = IdGenerator.of(1); + Id label2 = IdGenerator.of(2); + Id label3 = IdGenerator.of(3); + Id label4 = IdGenerator.of(4); + + ConditionQuery query = new ConditionQuery(HugeType.EDGE); + query.query(Condition.in(HugeKeys.LABEL, + ImmutableList.of(label1, label2, label3))); + query.query(Condition.in(HugeKeys.LABEL, + ImmutableList.of(label1, label2, label4))); + + Assert.assertThrows(IllegalStateException.class, + () -> query.condition(HugeKeys.LABEL), + e -> Assert.assertContains("Illegal key 'LABEL'", + e.getMessage())); + } + @Test public void testToString() { Query query = new Query(HugeType.VERTEX);