Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -256,24 +256,27 @@ public boolean containsLabelOrUserpropRelation() {
return false;
}

/**
* Returns the legacy condition value of the specified key.
*
* This method keeps the historical behavior for existing callers:
* <ul>
* <li>returns {@code null} if no top-level EQ/IN relation exists</li>
* <li>returns {@code null} if top-level EQ/IN relations resolve to empty</li>
* <li>returns the single value if only one value is resolved</li>
* <li>returns the raw IN list if there is exactly one top-level IN relation</li>
* <li>throws if multiple values remain after resolving several relations</li>
* </ul>
*
* Prefer {@link #conditionValues(Object)}, {@link #uniqueConditionValue(Object)}
* or {@link #conditionValue(Object)} for new code that needs explicit
* semantics.
*/
@Watched
public <T> T condition(Object key) {
List<Object> valuesEQ = InsertionOrderUtil.newList();
List<Object> valuesIN = InsertionOrderUtil.newList();
for (Condition c : this.conditions) {
if (c.isRelation()) {
Condition.Relation r = (Condition.Relation) c;
if (r.key().equals(key)) {
if (r.relation() == RelationType.EQ) {
valuesEQ.add(r.value());
} else if (r.relation() == RelationType.IN) {
Object value = r.value();
assert value instanceof List;
valuesIN.add(value);
}
}
}
}
this.collectConditionValues(key, valuesEQ, valuesIN);
if (valuesEQ.isEmpty() && valuesIN.isEmpty()) {
return null;
}
Expand All @@ -288,29 +291,8 @@ public <T> T condition(Object key) {
return value;
}

boolean initialized = false;
Set<Object> intersectValues = InsertionOrderUtil.newSet();
for (Object value : valuesEQ) {
List<Object> valueAsList = ImmutableList.of(value);
if (!initialized) {
intersectValues.addAll(valueAsList);
initialized = true;
} else {
CollectionUtil.intersectWithModify(intersectValues,
valueAsList);
}
}
for (Object value : valuesIN) {
@SuppressWarnings("unchecked")
List<Object> valueAsList = (List<Object>) value;
if (!initialized) {
intersectValues.addAll(valueAsList);
initialized = true;
} else {
CollectionUtil.intersectWithModify(intersectValues,
valueAsList);
}
}
Set<Object> intersectValues = this.resolveConditionValues(valuesEQ,
valuesIN);

if (intersectValues.isEmpty()) {
return null;
Expand All @@ -323,20 +305,151 @@ public <T> T condition(Object key) {
return value;
}

/**
* Returns whether there is any top-level relation for the specified key.
*/
public boolean containsCondition(Object key) {
for (Condition c : this.conditions) {
if (c.isRelation()) {
Condition.Relation r = (Condition.Relation) c;
if (r.key().equals(key)) {
return true;
}
}
}
return false;
}

/**
* Returns the resolved candidate values of the specified key from
* top-level EQ/IN relations.
*
* Use {@link #containsConditionValues(Object)} to distinguish "no EQ/IN
* condition" from "EQ/IN conditions exist but resolve to an empty
* intersection".
*/
public Set<Object> conditionValues(Object key) {
List<Object> valuesEQ = InsertionOrderUtil.newList();
List<Object> valuesIN = InsertionOrderUtil.newList();
this.collectConditionValues(key, valuesEQ, valuesIN);
if (valuesEQ.isEmpty() && valuesIN.isEmpty()) {
return InsertionOrderUtil.newSet();
}
return this.resolveConditionValues(valuesEQ, valuesIN);
}

/**
* Returns whether there is any top-level EQ/IN relation for the specified
* key.
*/
public boolean containsConditionValues(Object key) {
for (Condition c : this.conditions) {
if (c.isRelation()) {
Condition.Relation r = (Condition.Relation) c;
if (r.key().equals(key) &&
(r.relation() == RelationType.EQ ||
r.relation() == RelationType.IN)) {
return true;
}
}
}
return false;
}

/**
* Returns the unique resolved value of the specified key from top-level
* EQ/IN relations.
*
* Returns {@code null} when the resolved candidate set is empty. Throws
* if multiple values remain after resolution.
*/
public <T> T conditionValue(Object key) {
Set<Object> values = this.conditionValues(key);
if (values.isEmpty()) {
return null;
}
E.checkState(values.size() == 1,
"Illegal key '%s' with more than one value: %s",
key, values);
@SuppressWarnings("unchecked")
T value = (T) values.iterator().next();
return value;
}

/**
* Returns the unique resolved value of the specified key from top-level
* EQ/IN relations, or {@code null} if the resolved candidate set doesn't
* contain exactly one value.
*
* Use this method when callers want "single-or-null" semantics instead of
* treating multiple remaining values as an error.
*/
public <T> T uniqueConditionValue(Object key) {
Set<Object> values = this.conditionValues(key);
if (values.size() != 1) {
return null;
}
@SuppressWarnings("unchecked")
T value = (T) values.iterator().next();
return value;
}

public void unsetCondition(Object key) {
this.conditions.removeIf(c -> c.isRelation() && ((Relation) c).key().equals(key));
}

public boolean containsCondition(HugeKeys key) {
return this.containsCondition((Object) key);
}

public boolean containsConditionValues(HugeKeys key) {
return this.containsConditionValues((Object) key);
}

private void collectConditionValues(Object key, List<Object> valuesEQ,
List<Object> valuesIN) {
for (Condition c : this.conditions) {
if (c.isRelation()) {
Condition.Relation r = (Condition.Relation) c;
if (r.key().equals(key)) {
return true;
if (r.relation() == RelationType.EQ) {
valuesEQ.add(r.value());
} else if (r.relation() == RelationType.IN) {
Object value = r.value();
assert value instanceof List;
valuesIN.add(value);
}
}
}
}
return false;
}

private Set<Object> resolveConditionValues(List<Object> valuesEQ,
List<Object> valuesIN) {
boolean initialized = false;
Comment thread
contrueCT marked this conversation as resolved.
Set<Object> intersectValues = InsertionOrderUtil.newSet();
for (Object value : valuesEQ) {
List<Object> valueAsList = ImmutableList.of(value);
if (!initialized) {
intersectValues.addAll(valueAsList);
initialized = true;
} else {
CollectionUtil.intersectWithModify(intersectValues,
valueAsList);
}
}
for (Object value : valuesIN) {
@SuppressWarnings("unchecked")
List<Object> valueAsList = (List<Object>) value;
if (!initialized) {
intersectValues.addAll(valueAsList);
initialized = true;
} else {
CollectionUtil.intersectWithModify(intersectValues,
valueAsList);
}
}
return intersectValues;
}

public boolean containsCondition(Condition.RelationType type) {
Expand Down Expand Up @@ -566,6 +679,15 @@ public boolean hasNeqCondition() {
return false;
}

public boolean hasUserpropNeqCondition() {
for (Condition.Relation r : this.userpropRelations()) {
if (r.relation() == RelationType.NEQ) {
return true;
}
}
return false;
}

public boolean matchUserpropKeys(List<Id> keys) {
Set<Id> conditionKeys = this.userpropKeys();
return !keys.isEmpty() && conditionKeys.containsAll(keys);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ private Query writeQueryEdgeRangeCondition(ConditionQuery cq) {
if (direction == null) {
direction = Directions.OUT;
}
Id label = cq.condition(HugeKeys.LABEL);
Id label = (Id) this.edgeIdConditionValue(cq, HugeKeys.LABEL);

BytesBuffer start = BytesBuffer.allocate(BytesBuffer.BUF_EDGE_ID);
writePartitionedId(HugeType.EDGE, vertex, start);
Expand Down Expand Up @@ -722,7 +722,7 @@ private Query writeQueryEdgePrefixCondition(ConditionQuery cq) {
int count = 0;
BytesBuffer buffer = BytesBuffer.allocate(BytesBuffer.BUF_EDGE_ID);
for (HugeKeys key : EdgeId.KEYS) {
Object value = cq.condition(key);
Object value = this.edgeIdConditionValue(cq, key);

if (value != null) {
count++;
Expand Down Expand Up @@ -763,6 +763,17 @@ private Query writeQueryEdgePrefixCondition(ConditionQuery cq) {
return null;
}

private Object edgeIdConditionValue(ConditionQuery cq, HugeKeys key) {
if (key == HugeKeys.LABEL) {
/*
* LABEL may still be represented by multiple top-level EQ/IN
* relations before strict edge-id serialization.
*/
return cq.conditionValue(key);
}
return cq.condition(key);
}

@Override
protected Query writeQueryCondition(Query query) {
HugeType type = query.resultType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ private Query writeQueryEdgeRangeCondition(ConditionQuery cq) {
if (direction == null) {
direction = Directions.OUT;
}
Object label = cq.condition(HugeKeys.LABEL);
Object label = this.edgeIdConditionValue(cq, HugeKeys.LABEL);

List<String> start = new ArrayList<>(cq.conditionsSize());
start.add(writeEntryId((Id) vertex));
Expand Down Expand Up @@ -491,7 +491,7 @@ private Query writeQueryEdgePrefixCondition(ConditionQuery cq) {
List<String> condParts = new ArrayList<>(cq.conditionsSize());

for (HugeKeys key : EdgeId.KEYS) {
Object value = cq.condition(key);
Object value = this.edgeIdConditionValue(cq, key);
if (value == null) {
break;
}
Expand All @@ -516,6 +516,17 @@ private Query writeQueryEdgePrefixCondition(ConditionQuery cq) {
return null;
}

private Object edgeIdConditionValue(ConditionQuery cq, HugeKeys key) {
if (key == HugeKeys.LABEL) {
/*
* LABEL may still be represented by multiple top-level EQ/IN
* relations before strict edge-id serialization.
*/
return cq.conditionValue(key);
}
return cq.condition(key);
}

@Override
protected Query writeQueryCondition(Query query) {
ConditionQuery result = (ConditionQuery) query;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ public boolean matched(Query query) {
int conditionsSize = cq.conditionsSize();
Object owner = cq.condition(HugeKeys.OWNER_VERTEX);
Directions direction = cq.condition(HugeKeys.DIRECTION);
Id label = cq.condition(HugeKeys.LABEL);
Id label = cq.uniqueConditionValue(HugeKeys.LABEL);

if (direction == null && conditionsSize > 1) {
for (Condition cond : cq.conditions()) {
Expand Down Expand Up @@ -316,7 +316,7 @@ private Iterator<HugeEdge> query(ConditionQuery query) {
if (dir == null) {
dir = Directions.BOTH;
}
Id label = query.condition(HugeKeys.LABEL);
Id label = query.uniqueConditionValue(HugeKeys.LABEL);
if (label == null) {
label = IdGenerator.ZERO;
}
Expand Down
Loading
Loading