Skip to content

Commit 4704354

Browse files
authored
fix(query): handle conflicting edge label conditions safely (#2990)
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.
1 parent 28c39b6 commit 4704354

File tree

3 files changed

+78
-2
lines changed

3 files changed

+78
-2
lines changed

hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/ConditionQuery.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,13 @@ public <T> T condition(Object key) {
288288
return value;
289289
}
290290

291+
boolean initialized = false;
291292
Set<Object> intersectValues = InsertionOrderUtil.newSet();
292293
for (Object value : valuesEQ) {
293294
List<Object> valueAsList = ImmutableList.of(value);
294-
if (intersectValues.isEmpty()) {
295+
if (!initialized) {
295296
intersectValues.addAll(valueAsList);
297+
initialized = true;
296298
} else {
297299
CollectionUtil.intersectWithModify(intersectValues,
298300
valueAsList);
@@ -301,8 +303,9 @@ public <T> T condition(Object key) {
301303
for (Object value : valuesIN) {
302304
@SuppressWarnings("unchecked")
303305
List<Object> valueAsList = (List<Object>) value;
304-
if (intersectValues.isEmpty()) {
306+
if (!initialized) {
305307
intersectValues.addAll(valueAsList);
308+
initialized = true;
306309
} else {
307310
CollectionUtil.intersectWithModify(intersectValues,
308311
valueAsList);

hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4646,6 +4646,29 @@ public void testQueryInEdgesOfVertexByLabels() {
46464646
Assert.assertEquals(3L, size);
46474647
}
46484648

4649+
@Test
4650+
public void testQueryInEdgesOfVertexByConflictingLabels() {
4651+
HugeGraph graph = graph();
4652+
init18Edges();
4653+
4654+
long direct = graph.traversal().V().inE("created")
4655+
.hasLabel("created", "look")
4656+
.hasLabel("authored")
4657+
.count().next();
4658+
Assert.assertEquals(0L, direct);
4659+
4660+
long matched = graph.traversal().V()
4661+
.match(__.as("start1")
4662+
.inE("created")
4663+
.as("m1"))
4664+
.select("m1")
4665+
.hasLabel("created", "look")
4666+
.hasLabel("authored")
4667+
.count().next();
4668+
Assert.assertEquals(0L, matched);
4669+
Assert.assertEquals(matched, direct);
4670+
}
4671+
46494672
@Test
46504673
public void testQueryInEdgesOfVertexBySortkey() {
46514674
HugeGraph graph = graph();

hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/QueryTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717

1818
package org.apache.hugegraph.unit.core;
1919

20+
import org.apache.hugegraph.backend.id.Id;
2021
import org.apache.hugegraph.backend.id.IdGenerator;
2122
import org.apache.hugegraph.backend.query.Aggregate.AggregateFunc;
23+
import org.apache.hugegraph.backend.query.Condition;
2224
import org.apache.hugegraph.backend.query.ConditionQuery;
2325
import org.apache.hugegraph.backend.query.IdPrefixQuery;
2426
import org.apache.hugegraph.backend.query.IdQuery;
@@ -30,6 +32,7 @@
3032
import org.apache.hugegraph.type.define.HugeKeys;
3133
import org.junit.Test;
3234

35+
import com.google.common.collect.ImmutableList;
3336
import com.google.common.collect.ImmutableMap;
3437
import com.google.common.collect.ImmutableSet;
3538

@@ -45,6 +48,53 @@ public void testOrderBy() {
4548
query.orders());
4649
}
4750

51+
@Test
52+
public void testConditionWithEqAndIn() {
53+
Id label1 = IdGenerator.of(1);
54+
Id label2 = IdGenerator.of(2);
55+
56+
ConditionQuery query = new ConditionQuery(HugeType.EDGE);
57+
query.eq(HugeKeys.LABEL, label1);
58+
query.query(Condition.in(HugeKeys.LABEL,
59+
ImmutableList.of(label1, label2)));
60+
61+
Assert.assertEquals(label1, query.condition(HugeKeys.LABEL));
62+
}
63+
64+
@Test
65+
public void testConditionWithConflictingEqAndIn() {
66+
Id label1 = IdGenerator.of(1);
67+
Id label2 = IdGenerator.of(2);
68+
Id label3 = IdGenerator.of(3);
69+
70+
ConditionQuery query = new ConditionQuery(HugeType.EDGE);
71+
query.eq(HugeKeys.LABEL, label1);
72+
query.eq(HugeKeys.LABEL, label2);
73+
query.query(Condition.in(HugeKeys.LABEL,
74+
ImmutableList.of(label1, label3)));
75+
76+
Assert.assertNull(query.condition(HugeKeys.LABEL));
77+
}
78+
79+
@Test
80+
public void testConditionWithMultipleMatchedInValues() {
81+
Id label1 = IdGenerator.of(1);
82+
Id label2 = IdGenerator.of(2);
83+
Id label3 = IdGenerator.of(3);
84+
Id label4 = IdGenerator.of(4);
85+
86+
ConditionQuery query = new ConditionQuery(HugeType.EDGE);
87+
query.query(Condition.in(HugeKeys.LABEL,
88+
ImmutableList.of(label1, label2, label3)));
89+
query.query(Condition.in(HugeKeys.LABEL,
90+
ImmutableList.of(label1, label2, label4)));
91+
92+
Assert.assertThrows(IllegalStateException.class,
93+
() -> query.condition(HugeKeys.LABEL),
94+
e -> Assert.assertContains("Illegal key 'LABEL'",
95+
e.getMessage()));
96+
}
97+
4898
@Test
4999
public void testToString() {
50100
Query query = new Query(HugeType.VERTEX);

0 commit comments

Comments
 (0)