Skip to content

Commit 2e82f83

Browse files
committed
improve(query): consolidate unique label resolution
1 parent 5f15699 commit 2e82f83

10 files changed

Lines changed: 158 additions & 79 deletions

File tree

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

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,9 @@ public boolean containsLabelOrUserpropRelation() {
268268
* <li>throws if multiple values remain after resolving several relations</li>
269269
* </ul>
270270
*
271-
* Prefer {@link #conditionValues(Object)} or {@link #conditionValue(Object)}
272-
* for new code that needs explicit semantics.
271+
* Prefer {@link #conditionValues(Object)}, {@link #uniqueConditionValue(Object)}
272+
* or {@link #conditionValue(Object)} for new code that needs explicit
273+
* semantics.
273274
*/
274275
@Watched
275276
public <T> T condition(Object key) {
@@ -290,29 +291,8 @@ public <T> T condition(Object key) {
290291
return value;
291292
}
292293

293-
boolean initialized = false;
294-
Set<Object> intersectValues = InsertionOrderUtil.newSet();
295-
for (Object value : valuesEQ) {
296-
List<Object> valueAsList = ImmutableList.of(value);
297-
if (!initialized) {
298-
intersectValues.addAll(valueAsList);
299-
initialized = true;
300-
} else {
301-
CollectionUtil.intersectWithModify(intersectValues,
302-
valueAsList);
303-
}
304-
}
305-
for (Object value : valuesIN) {
306-
@SuppressWarnings("unchecked")
307-
List<Object> valueAsList = (List<Object>) value;
308-
if (!initialized) {
309-
intersectValues.addAll(valueAsList);
310-
initialized = true;
311-
} else {
312-
CollectionUtil.intersectWithModify(intersectValues,
313-
valueAsList);
314-
}
315-
}
294+
Set<Object> intersectValues = this.resolveConditionValues(valuesEQ,
295+
valuesIN);
316296

317297
if (intersectValues.isEmpty()) {
318298
return null;
@@ -344,8 +324,9 @@ public boolean containsCondition(Object key) {
344324
* Returns the resolved candidate values of the specified key from
345325
* top-level EQ/IN relations.
346326
*
347-
* Use {@link #containsCondition(Object)} to distinguish "no condition"
348-
* from "conditions exist but resolve to an empty intersection".
327+
* Use {@link #containsConditionValues(Object)} to distinguish "no EQ/IN
328+
* condition" from "EQ/IN conditions exist but resolve to an empty
329+
* intersection".
349330
*/
350331
public Set<Object> conditionValues(Object key) {
351332
List<Object> valuesEQ = InsertionOrderUtil.newList();
@@ -357,6 +338,24 @@ public Set<Object> conditionValues(Object key) {
357338
return this.resolveConditionValues(valuesEQ, valuesIN);
358339
}
359340

341+
/**
342+
* Returns whether there is any top-level EQ/IN relation for the specified
343+
* key.
344+
*/
345+
public boolean containsConditionValues(Object key) {
346+
for (Condition c : this.conditions) {
347+
if (c.isRelation()) {
348+
Condition.Relation r = (Condition.Relation) c;
349+
if (r.key().equals(key) &&
350+
(r.relation() == RelationType.EQ ||
351+
r.relation() == RelationType.IN)) {
352+
return true;
353+
}
354+
}
355+
}
356+
return false;
357+
}
358+
360359
/**
361360
* Returns the unique resolved value of the specified key from top-level
362361
* EQ/IN relations.
@@ -377,6 +376,24 @@ public <T> T conditionValue(Object key) {
377376
return value;
378377
}
379378

379+
/**
380+
* Returns the unique resolved value of the specified key from top-level
381+
* EQ/IN relations, or {@code null} if the resolved candidate set doesn't
382+
* contain exactly one value.
383+
*
384+
* Use this method when callers want "single-or-null" semantics instead of
385+
* treating multiple remaining values as an error.
386+
*/
387+
public <T> T uniqueConditionValue(Object key) {
388+
Set<Object> values = this.conditionValues(key);
389+
if (values.size() != 1) {
390+
return null;
391+
}
392+
@SuppressWarnings("unchecked")
393+
T value = (T) values.iterator().next();
394+
return value;
395+
}
396+
380397
public void unsetCondition(Object key) {
381398
this.conditions.removeIf(c -> c.isRelation() && ((Relation) c).key().equals(key));
382399
}
@@ -385,6 +402,10 @@ public boolean containsCondition(HugeKeys key) {
385402
return this.containsCondition((Object) key);
386403
}
387404

405+
public boolean containsConditionValues(HugeKeys key) {
406+
return this.containsConditionValues((Object) key);
407+
}
408+
388409
private void collectConditionValues(Object key, List<Object> valuesEQ,
389410
List<Object> valuesIN) {
390411
for (Condition c : this.conditions) {
@@ -658,6 +679,15 @@ public boolean hasNeqCondition() {
658679
return false;
659680
}
660681

682+
public boolean hasUserpropNeqCondition() {
683+
for (Condition.Relation r : this.userpropRelations()) {
684+
if (r.relation() == RelationType.NEQ) {
685+
return true;
686+
}
687+
}
688+
return false;
689+
}
690+
661691
public boolean matchUserpropKeys(List<Id> keys) {
662692
Set<Id> conditionKeys = this.userpropKeys();
663693
return !keys.isEmpty() && conditionKeys.containsAll(keys);

hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/serializer/BinarySerializer.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ private Query writeQueryEdgeRangeCondition(ConditionQuery cq) {
674674
if (direction == null) {
675675
direction = Directions.OUT;
676676
}
677-
Id label = cq.conditionValue(HugeKeys.LABEL);
677+
Id label = (Id) this.edgeIdConditionValue(cq, HugeKeys.LABEL);
678678

679679
BytesBuffer start = BytesBuffer.allocate(BytesBuffer.BUF_EDGE_ID);
680680
writePartitionedId(HugeType.EDGE, vertex, start);
@@ -722,8 +722,7 @@ private Query writeQueryEdgePrefixCondition(ConditionQuery cq) {
722722
int count = 0;
723723
BytesBuffer buffer = BytesBuffer.allocate(BytesBuffer.BUF_EDGE_ID);
724724
for (HugeKeys key : EdgeId.KEYS) {
725-
Object value = key == HugeKeys.LABEL ?
726-
cq.conditionValue(key) : cq.condition(key);
725+
Object value = this.edgeIdConditionValue(cq, key);
727726

728727
if (value != null) {
729728
count++;
@@ -764,6 +763,17 @@ private Query writeQueryEdgePrefixCondition(ConditionQuery cq) {
764763
return null;
765764
}
766765

766+
private Object edgeIdConditionValue(ConditionQuery cq, HugeKeys key) {
767+
if (key == HugeKeys.LABEL) {
768+
/*
769+
* LABEL may still be represented by multiple top-level EQ/IN
770+
* relations before strict edge-id serialization.
771+
*/
772+
return cq.conditionValue(key);
773+
}
774+
return cq.condition(key);
775+
}
776+
767777
@Override
768778
protected Query writeQueryCondition(Query query) {
769779
HugeType type = query.resultType();

hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/serializer/TextSerializer.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ private Query writeQueryEdgeRangeCondition(ConditionQuery cq) {
457457
if (direction == null) {
458458
direction = Directions.OUT;
459459
}
460-
Object label = cq.conditionValue(HugeKeys.LABEL);
460+
Object label = this.edgeIdConditionValue(cq, HugeKeys.LABEL);
461461

462462
List<String> start = new ArrayList<>(cq.conditionsSize());
463463
start.add(writeEntryId((Id) vertex));
@@ -491,8 +491,7 @@ private Query writeQueryEdgePrefixCondition(ConditionQuery cq) {
491491
List<String> condParts = new ArrayList<>(cq.conditionsSize());
492492

493493
for (HugeKeys key : EdgeId.KEYS) {
494-
Object value = key == HugeKeys.LABEL ?
495-
cq.conditionValue(key) : cq.condition(key);
494+
Object value = this.edgeIdConditionValue(cq, key);
496495
if (value == null) {
497496
break;
498497
}
@@ -517,6 +516,17 @@ private Query writeQueryEdgePrefixCondition(ConditionQuery cq) {
517516
return null;
518517
}
519518

519+
private Object edgeIdConditionValue(ConditionQuery cq, HugeKeys key) {
520+
if (key == HugeKeys.LABEL) {
521+
/*
522+
* LABEL may still be represented by multiple top-level EQ/IN
523+
* relations before strict edge-id serialization.
524+
*/
525+
return cq.conditionValue(key);
526+
}
527+
return cq.condition(key);
528+
}
529+
520530
@Override
521531
protected Query writeQueryCondition(Query query) {
522532
ConditionQuery result = (ConditionQuery) query;

hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/store/ram/RamTable.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public boolean matched(Query query) {
269269
int conditionsSize = cq.conditionsSize();
270270
Object owner = cq.condition(HugeKeys.OWNER_VERTEX);
271271
Directions direction = cq.condition(HugeKeys.DIRECTION);
272-
Id label = uniqueLabel(cq);
272+
Id label = cq.uniqueConditionValue(HugeKeys.LABEL);
273273

274274
if (direction == null && conditionsSize > 1) {
275275
for (Condition cond : cq.conditions()) {
@@ -316,7 +316,7 @@ private Iterator<HugeEdge> query(ConditionQuery query) {
316316
if (dir == null) {
317317
dir = Directions.BOTH;
318318
}
319-
Id label = uniqueLabel(query);
319+
Id label = query.uniqueConditionValue(HugeKeys.LABEL);
320320
if (label == null) {
321321
label = IdGenerator.ZERO;
322322
}
@@ -377,14 +377,6 @@ private static void ensureNumberId(Id id) {
377377
}
378378
}
379379

380-
private static Id uniqueLabel(ConditionQuery query) {
381-
java.util.Set<Object> labels = query.conditionValues(HugeKeys.LABEL);
382-
if (labels.size() != 1) {
383-
return null;
384-
}
385-
return (Id) labels.iterator().next();
386-
}
387-
388380
private static long encode(long target, Directions direction, int label) {
389381
// TODO: support property
390382
assert (label & 0x0fffffff) == label;

hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,8 @@ private IdHolderList queryByLabel(ConditionQuery query) {
415415
HugeType queryType = query.resultType();
416416
IndexLabel il = IndexLabel.label(queryType);
417417
validateIndexLabel(il);
418+
// Query-by-label builds a label index entry and requires one
419+
// deterministically resolved label instead of best-effort fallback.
418420
Id label = query.conditionValue(HugeKeys.LABEL);
419421
E.checkState(label != null, "Expect one label value for query: %s",
420422
query);
@@ -483,7 +485,7 @@ private IdHolderList queryByUserprop(ConditionQuery query) {
483485
}
484486
Set<MatchedIndex> indexes = this.collectMatchedIndexes(query);
485487
if (indexes.isEmpty()) {
486-
Id label = uniqueLabel(query);
488+
Id label = query.uniqueConditionValue(HugeKeys.LABEL);
487489
throw noIndexException(this.graph(), query, label);
488490
}
489491

@@ -757,11 +759,12 @@ private PageIds doIndexQueryOnce(IndexLabel indexLabel,
757759
@Watched(prefix = "index")
758760
private Set<MatchedIndex> collectMatchedIndexes(ConditionQuery query) {
759761
ISchemaTransaction schema = this.params().schemaTransaction();
760-
boolean hasLabel = query.containsCondition(HugeKeys.LABEL);
762+
boolean hasLabelValues = query.containsConditionValues(HugeKeys.LABEL);
761763
Set<Object> labels = query.conditionValues(HugeKeys.LABEL);
762764

763765
List<? extends SchemaLabel> schemaLabels;
764-
if (hasLabel && labels.isEmpty()) {
766+
if (hasLabelValues && labels.isEmpty()) {
767+
// LABEL EQ/IN conditions resolve to an empty intersection.
765768
return Collections.emptySet();
766769
}
767770
if (labels.size() == 1) {
@@ -952,7 +955,7 @@ private void removeExpiredIndexIfNeeded(HugeIndex index,
952955
private static Set<IndexLabel> matchSingleOrCompositeIndex(
953956
ConditionQuery query,
954957
Set<IndexLabel> indexLabels) {
955-
if (query.hasNeqCondition()) {
958+
if (query.hasUserpropNeqCondition()) {
956959
return ImmutableSet.of();
957960
}
958961
boolean requireRange = query.hasRangeCondition();
@@ -993,7 +996,7 @@ private static Set<IndexLabel> matchSingleOrCompositeIndex(
993996
private static Set<IndexLabel> matchJointIndexes(
994997
ConditionQuery query,
995998
Set<IndexLabel> indexLabels) {
996-
if (query.hasNeqCondition()) {
999+
if (query.hasUserpropNeqCondition()) {
9971000
return ImmutableSet.of();
9981001
}
9991002
Set<Id> queryPropKeys = query.userpropKeys();
@@ -1788,7 +1791,7 @@ protected long removeIndexLeft(ConditionQuery query,
17881791
}
17891792

17901793
// Check label is matched
1791-
Id label = uniqueLabel(query);
1794+
Id label = query.uniqueConditionValue(HugeKeys.LABEL);
17921795
// NOTE: original condition query may not have label condition,
17931796
// which means possibly label == null.
17941797
if (label != null && !element.schemaLabel().id().equals(label)) {
@@ -1988,12 +1991,4 @@ public Long reduce(Long t1, Long t2) {
19881991
return t1 + t2;
19891992
}
19901993
}
1991-
1992-
private static Id uniqueLabel(ConditionQuery query) {
1993-
Set<Object> labels = query.conditionValues(HugeKeys.LABEL);
1994-
if (labels.size() != 1) {
1995-
return null;
1996-
}
1997-
return (Id) labels.iterator().next();
1998-
}
19991994
}

hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,7 @@ protected Iterator<HugeEdge> queryEdgesFromBackend(Query query) {
10571057
ConditionQueryFlatten.flatten((ConditionQuery) query, supportIn).stream();
10581058

10591059
Stream<Iterator<HugeEdge>> edgeIterators = flattenedQueries.map(cq -> {
1060-
Id label = uniqueLabel(cq);
1060+
Id label = cq.uniqueConditionValue(HugeKeys.LABEL);
10611061
if (this.storeFeatures().supportsFatherAndSubEdgeLabel() &&
10621062
label != null &&
10631063
graph().edgeLabel(label).isFather() &&
@@ -1387,7 +1387,7 @@ private static boolean matchEdgeSortKeys(ConditionQuery query,
13871387
boolean matchAll,
13881388
HugeGraph graph) {
13891389
assert query.resultType().isEdge();
1390-
Id label = uniqueLabel(query);
1390+
Id label = query.uniqueConditionValue(HugeKeys.LABEL);
13911391
if (label == null) {
13921392
return false;
13931393
}
@@ -1520,7 +1520,7 @@ private Query optimizeQuery(ConditionQuery query) {
15201520
throw new HugeException("Not supported querying by id and conditions: %s", query);
15211521
}
15221522

1523-
Id label = uniqueLabel(query);
1523+
Id label = query.uniqueConditionValue(HugeKeys.LABEL);
15241524

15251525
// Optimize vertex query
15261526
if (label != null && query.resultType().isVertex()) {
@@ -1912,7 +1912,8 @@ private boolean rightResultFromIndexQuery(Query query, HugeElement elem) {
19121912
}
19131913

19141914
ConditionQuery cq = (ConditionQuery) query;
1915-
if (uniqueLabel(cq) != null && cq.resultType().isEdge()) {
1915+
if (cq.uniqueConditionValue(HugeKeys.LABEL) != null &&
1916+
cq.resultType().isEdge()) {
19161917
if (cq.conditions().size() == 1) {
19171918
// g.E().hasLabel(xxx)
19181919
return true;
@@ -1964,14 +1965,6 @@ private boolean rightResultFromIndexQuery(Query query, HugeElement elem) {
19641965
return false;
19651966
}
19661967

1967-
private static Id uniqueLabel(ConditionQuery query) {
1968-
Set<Object> labels = query.conditionValues(HugeKeys.LABEL);
1969-
if (labels.size() != 1) {
1970-
return null;
1971-
}
1972-
return (Id) labels.iterator().next();
1973-
}
1974-
19751968
private <T extends HugeElement> Iterator<T> filterExpiredResultFromBackend(
19761969
Query query, Iterator<T> results) {
19771970
if (this.store().features().supportsTtl() || query.showExpired()) {

hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/algorithm/HugeTraverser.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,8 @@ private void fillFilterBySortKeys(Query query, Id[] edgeLabels,
582582

583583
ConditionQuery condQuery = (ConditionQuery) query;
584584
if (!GraphTransaction.matchFullEdgeSortKeys(condQuery, this.graph())) {
585+
// Sort-key validation needs one concrete edge label so that the
586+
// error message points to the exact schema label in use.
585587
Id label = condQuery.conditionValue(HugeKeys.LABEL);
586588
E.checkArgument(false, "The properties %s does not match " +
587589
"sort keys of edge label '%s'",

0 commit comments

Comments
 (0)