Skip to content

fix(query): handle conflicting edge label conditions safely#2990

Open
contrueCT wants to merge 3 commits intoapache:masterfrom
contrueCT:task/fix-2933-label-condition
Open

fix(query): handle conflicting edge label conditions safely#2990
contrueCT wants to merge 3 commits intoapache:masterfrom
contrueCT:task/fix-2933-label-condition

Conversation

@contrueCT
Copy link
Copy Markdown
Contributor

Purpose of the PR

HugeGraph may read LABEL from ConditionQuery before the query is flattened when optimizing edge traversals. For contradictory label conditions such as:

g.V().inE("created").hasLabel("created", "look").hasLabel("authored")

the previous intersection logic used an empty set to mean both "not initialized yet" and "already intersected to empty". As a result, a later IN condition could repopulate the candidate labels and trigger:

Illegal key 'LABEL' with more than one value

instead of returning an empty result.

Main Changes

Update ConditionQuery.condition(Object key) to track whether the intersection has been initialized independently.

This keeps an empty intersection empty, so conflicting label predicates now fall back safely and produce no matches, while the existing protection for true multi-value results is preserved.

Verifying these changes

Added regression coverage for both the low-level query behavior and the traversal scenario from the issue:

QueryTest#testConditionWithEqAndIn
QueryTest#testConditionWithConflictingEqAndIn
EdgeCoreTest#testQueryInEdgesOfVertexByConflictingLabels
The edge traversal test also compares the direct traversal with an equivalent match()-based query and verifies that both return 0 instead of throwing an exception.

  • Trivial rework / code cleanup without any test coverage. (No Need)
  • Already covered by existing tests, such as (please modify tests here).
  • Need tests and can be verified as follows:
    • mvn -pl hugegraph-server/hugegraph-test -am -P core-test,memory -DfailIfNoTests=false -Dtest='QueryTest#testConditionWithEqAndIn+testConditionWithConflictingEqAndIn' test
    • mvn -pl hugegraph-server/hugegraph-test -am -P core-test,memory -DfailIfNoTests=false -Dtest='EdgeCoreTest#testQueryInEdgesOfVertexByConflictingLabels' test

Does this PR potentially affect the following parts?

Documentation Status

  • Doc - TODO
  • Doc - Done
  • Doc - No Need

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 apache#2933, including a match()-based equivalent query to assert consistent zero-count results.
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.
@dosubot dosubot bot added size:M This PR changes 30-99 lines, ignoring generated files. bug Something isn't working tests Add or improve test cases labels Apr 12, 2026
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 apache#2933 fix only changes the empty-intersection case and does not silently relax true multi-value results.
@imbajin
Copy link
Copy Markdown
Member

imbajin commented Apr 12, 2026

当前修复我认为是合理的。

1. 当前问题

这次问题的根因比较明确:ConditionQuery.condition() 在求交集时,把下面两种状态都复用了 isEmpty() 来判断:

1. 交集尚未开始计算
2. 交集已经计算完成,但结果为空

这会导致空交集在后续遇到 IN 条件时被重新填充,最终错误地触发多值异常,而不是返回空结果。

这次通过额外状态位把“未初始化”和“已为空”区分开,能够直接修正这条错误链路,而且改动范围比较小,不会在这个 bugfix 里额外引入太多变量。从修复当前 issue 的角度看,这个方案是合适的。

如果你希望进一步提升这段代码的可维护性,也可以考虑用更直观的状态表达方式,例如把“尚未初始化”直接编码到变量本身:

Set<Object> intersectValues = null;

if (intersectValues == null) {
    intersectValues = InsertionOrderUtil.newSet();
    intersectValues.addAll(valueAsList);
} else {
    CollectionUtil.intersectWithModify(intersectValues, valueAsList);
}

这样是否比 boolean initialized 更清晰,可以根据你对可读性的偏好自行选择;但这不影响当前修复思路本身是成立的。

2. condition() 的语义混杂,建议单独记录 issue 处理

这次修复同时也暴露出 ConditionQuery.condition() 这个 API 本身存在语义混杂的问题,建议单独开一个 issue 记录,后续再独立处理。

一个比较直观的例子是,下面两种情况:

ConditionQuery q1 = new ConditionQuery(HugeType.EDGE);
// 没有任何 LABEL 条件

ConditionQuery q2 = new ConditionQuery(HugeType.EDGE);
q2.eq(HugeKeys.LABEL, label1);
q2.eq(HugeKeys.LABEL, label2);
// LABEL 条件冲突,交集为空

当前这两种调用:

q1.condition(HugeKeys.LABEL)
q2.condition(HugeKeys.LABEL)

都可能返回:

null

但它们的含义并不相同:

q1: 没有这个条件
q2: 有这个条件,但交集为空

同一个 API 现在还可能出现其他几种结果:

// 返回唯一值
query.eq(HugeKeys.LABEL, label1);

// 返回整个 IN 列表
query.query(Condition.in(HugeKeys.LABEL, ImmutableList.of(label1, label2)));

// 在交集后仍有多个候选值时抛异常
throw new IllegalStateException(...);

也就是说,condition() 目前同时承担了这些不同语义:

- 条件不存在
- 唯一值提取
- 空交集表示
- 多值冲突表示

对于这次修复涉及的调用链来说,这种设计暂时还能工作,因为调用方只关心“能不能拿到唯一 label 并继续做优化”;但从 API 设计上看,语义边界并不清晰,后续维护时仍然有潜在风险。

@contrueCT
Copy link
Copy Markdown
Contributor Author

@imbajin
好的,我一会再确认一下,然后开一个新的issue描述记录这个问题

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:M This PR changes 30-99 lines, ignoring generated files. tests Add or improve test cases

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Unexpected "Illegal key 'LABEL'..." happens

2 participants