This is an automated email from the ASF dual-hosted git repository.
imbajin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hugegraph.git
The following commit(s) were added to refs/heads/master by this push:
new 7f0a44adc fix(server): handle match() in no index case (#3039)
7f0a44adc is described below
commit 7f0a44adc0bd8f7445a4ac357633e967955c845c
Author: legendpei <[email protected]>
AuthorDate: Thu Jun 4 17:50:07 2026 +0800
fix(server): handle match() in no index case (#3039)
---
.../traversal/optimize/TraversalUtil.java | 227 +++++++++++++++++
.../hugegraph/core/CountStrategyCoreTest.java | 283 +++++++++++++++++++++
.../optimize/TraversalUtilOptimizeTest.java | 24 ++
3 files changed, 534 insertions(+)
diff --git
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtil.java
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtil.java
index 8f4f7eb86..7b68f7177 100644
---
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtil.java
+++
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtil.java
@@ -42,6 +42,7 @@ import org.apache.hugegraph.backend.query.Query;
import org.apache.hugegraph.exception.NotFoundException;
import org.apache.hugegraph.exception.NotSupportException;
import org.apache.hugegraph.iterator.FilterIterator;
+import org.apache.hugegraph.schema.IndexLabel;
import org.apache.hugegraph.schema.PropertyKey;
import org.apache.hugegraph.schema.SchemaLabel;
import org.apache.hugegraph.structure.HugeElement;
@@ -68,6 +69,7 @@ import
org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep;
import
org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.CountGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MaxGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MinGlobalStep;
@@ -172,6 +174,27 @@ public final class TraversalUtil {
Step<?, ?> nextStep = step.getNextStep();
if (step instanceof HasStep) {
HasContainerHolder holder = (HasContainerHolder) step;
+ /*
+ * Range/neq predicates before match() may trigger a no-index
+ * query after MatchStep reorders filters. Keep known-indexed
+ * boolean predicates pushed down, and leave the rest for
+ * TinkerPop to evaluate.
+ */
+ if (followedByMatchStep(step) &&
+ hasUnusableMatchPredicate(newStep, holder)) {
+ List<HasContainer> extracted =
+ extractUsableHasContainers(newStep, holder);
+ for (HasContainer has : extracted) {
+ holder.removeHasContainer(has);
+ }
+ if (holder.getHasContainers().isEmpty()) {
+ TraversalHelper.copyLabels(step,
step.getPreviousStep(),
+ false);
+ traversal.removeStep(step);
+ }
+ step = nextStep;
+ continue;
+ }
if (extractHasContainers(newStep, holder)) {
TraversalHelper.copyLabels(step, step.getPreviousStep(),
false);
traversal.removeStep(step);
@@ -181,6 +204,203 @@ public final class TraversalUtil {
}
}
+ private static boolean followedByMatchStep(Step<?, ?> step) {
+ Step<?, ?> next = step.getNextStep();
+ while (next instanceof HasStep ||
+ next instanceof NoOpBarrierStep ||
+ next instanceof IdentityStep) {
+ next = next.getNextStep();
+ }
+ return next instanceof MatchStep;
+ }
+
+ private static boolean hasUnusableMatchPredicate(HugeGraphStep<?, ?> step,
+ HasContainerHolder
holder) {
+ HugeGraph graph = tryGetGraph(step);
+ for (HasContainer has : holder.getHasContainers()) {
+ if (!hasMatchIndexSensitivePredicate(has)) {
+ continue;
+ }
+ if (graph == null || !hasUsableMatchIndex(graph, step, has)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static List<HasContainer> extractUsableHasContainers(
+ HugeGraphStep<?, ?> step, HasContainerHolder holder) {
+ List<HasContainer> extracted = new ArrayList<>();
+ HugeGraph graph = tryGetGraph(step);
+ for (HasContainer has : holder.getHasContainers()) {
+ if (hasMatchIndexSensitivePredicate(has) &&
+ (graph == null || !hasUsableMatchIndex(graph, step, has))) {
+ continue;
+ }
+ if (!canExtractHasContainer(graph, has)) {
+ continue;
+ }
+ if (!GraphStep.processHasContainerIds(step, has)) {
+ step.addHasContainer(has);
+ }
+ extracted.add(has);
+ }
+ return extracted;
+ }
+
+ private static boolean hasMatchIndexSensitivePredicate(HasContainer has) {
+ if (hasNullPredicate(has)) {
+ return true;
+ }
+ List<P<Object>> predicates = new ArrayList<>();
+ collectPredicates(predicates, ImmutableList.of(has.getPredicate()));
+ for (P<Object> pred : predicates) {
+ BiPredicate<?, ?> bp = pred.getBiPredicate();
+ if (bp == Compare.neq ||
+ bp == Compare.gt || bp == Compare.gte ||
+ bp == Compare.lt || bp == Compare.lte) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasUsableMatchIndex(HugeGraph graph,
+ HugeGraphStep<?, ?> step,
+ HasContainer has) {
+ if (isSysProp(has.getKey())) {
+ return false;
+ }
+
+ PropertyKey pkey;
+ try {
+ pkey = graph.propertyKey(has.getKey());
+ } catch (NotFoundException e) {
+ return false;
+ }
+ if (!hasOnlyUsableNeqPredicates(pkey, has)) {
+ return false;
+ }
+ if (!canExtractHasContainer(graph, has)) {
+ return false;
+ }
+
+ Collection<? extends SchemaLabel> schemaLabels = step.returnsVertex() ?
+ graph.vertexLabels() :
+ graph.edgeLabels();
+ boolean seen = false;
+ for (SchemaLabel schemaLabel : schemaLabels) {
+ if (!schemaLabel.properties().contains(pkey.id())) {
+ continue;
+ }
+ seen = true;
+ if (pkey.dataType() == DataType.BOOLEAN &&
+ !hasBooleanIndex(graph, schemaLabel, pkey)) {
+ return false;
+ }
+ if (pkey.dataType().isNumber() &&
+ (!hasOnlyRangePredicates(has) ||
+ !hasRangeIndex(graph, schemaLabel, pkey))) {
+ return false;
+ }
+ if (pkey.dataType() != DataType.BOOLEAN &&
+ !pkey.dataType().isNumber()) {
+ return false;
+ }
+ }
+ return seen;
+ }
+
+ private static boolean hasOnlyUsableNeqPredicates(PropertyKey pkey,
+ HasContainer has) {
+ if (hasNullPredicate(has)) {
+ return false;
+ }
+ List<P<Object>> predicates = new ArrayList<>();
+ collectPredicates(predicates, ImmutableList.of(has.getPredicate()));
+ for (P<Object> pred : predicates) {
+ if (pred.getBiPredicate() != Compare.neq) {
+ continue;
+ }
+ if (pkey.dataType() == DataType.BOOLEAN &&
+ pred.getValue() instanceof Boolean) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean hasNullPredicate(HasContainer has) {
+ List<P<Object>> predicates = new ArrayList<>();
+ collectPredicates(predicates, ImmutableList.of(has.getPredicate()));
+ for (P<Object> pred : predicates) {
+ if (pred.getValue() == null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasBooleanIndex(HugeGraph graph,
+ SchemaLabel schemaLabel,
+ PropertyKey pkey) {
+ for (Id id : schemaLabel.indexLabels()) {
+ IndexLabel indexLabel = indexLabelOrNull(graph, id);
+ if (indexLabel == null ||
+ !matchSingleFieldIndex(indexLabel, pkey)) {
+ continue;
+ }
+ if (indexLabel.indexType().isSecondary()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasRangeIndex(HugeGraph graph,
+ SchemaLabel schemaLabel,
+ PropertyKey pkey) {
+ for (Id id : schemaLabel.indexLabels()) {
+ IndexLabel indexLabel = indexLabelOrNull(graph, id);
+ if (indexLabel == null ||
+ !matchSingleFieldIndex(indexLabel, pkey)) {
+ continue;
+ }
+ if (indexLabel.indexType().isRange()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static IndexLabel indexLabelOrNull(HugeGraph graph, Id id) {
+ try {
+ return graph.indexLabel(id);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ private static boolean matchSingleFieldIndex(IndexLabel indexLabel,
+ PropertyKey pkey) {
+ return indexLabel.indexFields().size() == 1 &&
+ indexLabel.indexField().equals(pkey.id());
+ }
+
+ private static boolean hasOnlyRangePredicates(HasContainer has) {
+ List<P<Object>> predicates = new ArrayList<>();
+ collectPredicates(predicates, ImmutableList.of(has.getPredicate()));
+ for (P<Object> pred : predicates) {
+ BiPredicate<?, ?> bp = pred.getBiPredicate();
+ if (bp != Compare.gt && bp != Compare.gte &&
+ bp != Compare.lt && bp != Compare.lte) {
+ return false;
+ }
+ }
+ return true;
+ }
+
public static void extractHasContainer(HugeVertexStep<?> newStep,
Traversal.Admin<?, ?> traversal) {
Step<?, ?> step = newStep;
@@ -248,6 +468,9 @@ public final class TraversalUtil {
} catch (NotFoundException e) {
return false;
}
+ if (hasNullPredicate(has)) {
+ return false;
+ }
if (!pkey.dataType().isText()) {
return true;
}
@@ -775,6 +998,10 @@ public final class TraversalUtil {
List<P<Object>> leafPredicates = new ArrayList<>();
collectPredicates(leafPredicates, ImmutableList.of(predicate));
for (P<Object> pred : leafPredicates) {
+ if (pred.getBiPredicate() == Compare.neq &&
+ pred.getValue() == null) {
+ continue;
+ }
Object value = validPropertyValue(pred.getValue(), pkey);
pred.setValue(value);
}
diff --git
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CountStrategyCoreTest.java
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CountStrategyCoreTest.java
index 212c2bc04..58a0d992f 100644
---
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CountStrategyCoreTest.java
+++
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CountStrategyCoreTest.java
@@ -19,8 +19,15 @@ package org.apache.hugegraph.core;
import org.apache.hugegraph.schema.SchemaManager;
import org.apache.hugegraph.testutil.Assert;
+import org.apache.hugegraph.traversal.optimize.HugeGraphStep;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
+import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.junit.Test;
@@ -48,6 +55,57 @@ public class CountStrategyCoreTest extends BaseCoreTest {
commitTx();
}
+ private void initMatchNoIndexSchema() {
+ SchemaManager schema = graph().schema();
+ schema.propertyKey("vp2").asBoolean().create();
+ schema.propertyKey("vp3").asLong().create();
+ schema.propertyKey("vp4").asText().create();
+ schema.propertyKey("ep2").asBoolean().create();
+ schema.vertexLabel("vl1").properties("vp2", "vp4")
+ .nullableKeys("vp2", "vp4").create();
+ schema.vertexLabel("vl0").properties("vp3")
+ .nullableKeys("vp3").create();
+ schema.edgeLabel("el1").link("vl1", "vl0")
+ .properties("ep2").nullableKeys("ep2").create();
+ }
+
+ private void initMatchNoIndexGraph() {
+ Vertex v1 = graph().addVertex(T.label, "vl1", "vp2", true,
+ "vp4", "foo");
+ Vertex v2 = graph().addVertex(T.label, "vl1", "vp2", false,
+ "vp4", "J2O");
+ Vertex v3 = graph().addVertex(T.label, "vl0",
+ "vp3", 4592737712018141719L);
+ Vertex v4 = graph().addVertex(T.label, "vl0",
+ "vp3", 4592737712018141717L);
+
+ v1.addEdge("el1", v3, "ep2", true);
+ v2.addEdge("el1", v4, "ep2", false);
+ commitTx();
+ }
+
+ private static HugeGraphStep<?, ?> applyAndGetGraphStep(
+ GraphTraversal<?, ?> traversal) {
+ traversal.asAdmin().applyStrategies();
+ return (HugeGraphStep<?, ?>) traversal.asAdmin().getStartStep();
+ }
+
+ private static boolean hasRemainingHasStep(GraphTraversal<?, ?> traversal,
+ String key) {
+ for (Step<?, ?> step : traversal.asAdmin().getSteps()) {
+ if (!(step instanceof HasStep)) {
+ continue;
+ }
+ HasContainerHolder holder = (HasContainerHolder) step;
+ for (HasContainer has : holder.getHasContainers()) {
+ if (key.equals(has.getKey())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private void initTextRangeSchema(boolean withEdge) {
SchemaManager schema = graph().schema();
schema.propertyKey("vp4").asText().create();
@@ -227,4 +285,229 @@ public class CountStrategyCoreTest extends BaseCoreTest {
Assert.assertEquals(direct, viaMatch);
}
+ @Test
+ public void testMatchWithNoIndexConditionMatchesDirectTraversal() {
+ this.initMatchNoIndexSchema();
+ this.initMatchNoIndexGraph();
+
+ long direct = graph().traversal().V()
+ .has("vp4", P.neq("J2O"))
+ .has("vl1", "vp2", P.gte(false))
+ .has("vp2")
+ .has("vl0", "vp3", P.gt(4592737712018141718L))
+ .out("el1")
+ .count().next();
+ long viaMatch = graph().traversal().V()
+ .has("vp4", P.neq("J2O"))
+ .has("vl1", "vp2", P.gte(false))
+ .match(__.<Vertex>as("start0")
+ .has("vp2")
+ .has("vl0", "vp3",
+ P.gt(4592737712018141718L))
+ .repeat(__.out("el1"))
+ .times(1)
+ .as("m0"))
+ .<Vertex>select("m0").count().next();
+
+ Assert.assertEquals(0L, direct);
+ Assert.assertEquals(direct, viaMatch);
+ }
+
+ @Test
+ public void testMatchWithIndexedRangeConditionStillExtractsHas() {
+ this.initMatchNoIndexSchema();
+ graph().schema().indexLabel("vl1ByVp2").onV("vl1")
+ .by("vp2").secondary().create();
+ this.initMatchNoIndexGraph();
+
+ GraphTraversal<Vertex, Long> traversal = graph().traversal().V()
+ .has("vp2", P.lt(true))
+
.match(__.<Vertex>as("s")
+ .has("vp2")
+ .as("m"))
+ .<Vertex>select("m")
+ .count();
+
+ HugeGraphStep<?, ?> graphStep = applyAndGetGraphStep(traversal);
+ Assert.assertEquals(1, graphStep.getHasContainers().size());
+ Assert.assertEquals("vp2",
graphStep.getHasContainers().get(0).getKey());
+ Assert.assertEquals(1L, traversal.next());
+ }
+
+ @Test
+ public void testMatchWithNoIndexConditionKeepsExtractingNextHas() {
+ this.initMatchNoIndexSchema();
+ graph().schema().indexLabel("vl1ByVp2").onV("vl1")
+ .by("vp2").secondary().create();
+ this.initMatchNoIndexGraph();
+
+ GraphTraversal<Vertex, Long> traversal = graph().traversal().V()
+ .has("vp4",
P.neq("J2O"))
+ .has("vp2", true)
+
.match(__.<Vertex>as("s")
+ .has("vp2")
+ .as("m"))
+ .<Vertex>select("m")
+ .count();
+
+ HugeGraphStep<?, ?> graphStep = applyAndGetGraphStep(traversal);
+ Assert.assertEquals(1, graphStep.getHasContainers().size());
+ Assert.assertEquals("vp2",
graphStep.getHasContainers().get(0).getKey());
+ Assert.assertTrue(hasRemainingHasStep(traversal, "vp4"));
+ Assert.assertFalse(hasRemainingHasStep(traversal, "vp2"));
+ Assert.assertEquals(1L, traversal.next());
+ }
+
+ @Test
+ public void testMatchWithBooleanExistsConditionKeepsHas() {
+ this.initMatchNoIndexSchema();
+ graph().schema().indexLabel("vl1ByVp2").onV("vl1")
+ .by("vp2").secondary().create();
+ this.initMatchNoIndexGraph();
+
+ GraphTraversal<Vertex, Long> traversal = graph().traversal().V()
+ .has("vp2",
P.neq(null))
+
.match(__.<Vertex>as("s")
+ .has("vp2",
true)
+ .as("m"))
+ .<Vertex>select("m")
+ .count();
+
+ HugeGraphStep<?, ?> graphStep = applyAndGetGraphStep(traversal);
+ Assert.assertEquals(0, graphStep.getHasContainers().size());
+ Assert.assertEquals(1L, traversal.next());
+ }
+
+ @Test
+ public void testMatchWithIdentityKeepsNoIndexConditionLocal() {
+ this.initMatchNoIndexSchema();
+ this.initMatchNoIndexGraph();
+
+ GraphTraversal<Vertex, Long> traversal = graph().traversal().V()
+ .has("vp4",
P.neq("J2O"))
+ .identity()
+
.match(__.<Vertex>as("s")
+ .has("vp2")
+ .as("m"))
+ .<Vertex>select("m")
+ .count();
+
+ HugeGraphStep<?, ?> graphStep = applyAndGetGraphStep(traversal);
+ Assert.assertEquals(0, graphStep.getHasContainers().size());
+ Assert.assertTrue(hasRemainingHasStep(traversal, "vp4"));
+ Assert.assertEquals(1L, traversal.next());
+ }
+
+ @Test
+ public void testMatchWithIndexedEdgeRangeConditionStillExtractsHas() {
+ this.initMatchNoIndexSchema();
+ graph().schema().indexLabel("el1ByEp2").onE("el1")
+ .by("ep2").secondary().create();
+ this.initMatchNoIndexGraph();
+
+ GraphTraversal<Edge, Long> traversal = graph().traversal().E()
+ .has("ep2", P.lt(true))
+ .match(__.<Edge>as("s")
+ .has("ep2")
+ .as("m"))
+ .<Edge>select("m")
+ .count();
+
+ HugeGraphStep<?, ?> graphStep = applyAndGetGraphStep(traversal);
+ Assert.assertEquals(1, graphStep.getHasContainers().size());
+ Assert.assertEquals("ep2",
graphStep.getHasContainers().get(0).getKey());
+ Assert.assertEquals(1L, traversal.next());
+ }
+
+ @Test
+ public void testMatchWithIndexedNumericRangeConditionStillExtractsHas() {
+ this.initMatchNoIndexSchema();
+ graph().schema().indexLabel("vl0ByVp3").onV("vl0")
+ .by("vp3").range().create();
+ this.initMatchNoIndexGraph();
+
+ GraphTraversal<Vertex, Long> traversal = graph().traversal().V()
+ .has("vp3",
+
P.gt(4592737712018141718L))
+
.match(__.<Vertex>as("s")
+ .has("vp3")
+ .as("m"))
+ .<Vertex>select("m")
+ .count();
+
+ HugeGraphStep<?, ?> graphStep = applyAndGetGraphStep(traversal);
+ Assert.assertEquals(1, graphStep.getHasContainers().size());
+ Assert.assertEquals("vp3",
graphStep.getHasContainers().get(0).getKey());
+ Assert.assertFalse(hasRemainingHasStep(traversal, "vp3"));
+ Assert.assertEquals(1L, traversal.next());
+ }
+
+ @Test
+ public void testMatchWithIndexedNumericNeqConditionKeepsHas() {
+ this.initMatchNoIndexSchema();
+ graph().schema().indexLabel("vl0ByVp3").onV("vl0")
+ .by("vp3").range().create();
+ graph().schema().indexLabel("vl1ByVp2").onV("vl1")
+ .by("vp2").secondary().create();
+ this.initMatchNoIndexGraph();
+
+ GraphTraversal<Vertex, Long> traversal = graph().traversal().V()
+ .has("vp3",
+
P.neq(4592737712018141719L))
+ .has("vp2", true)
+
.match(__.<Vertex>as("s")
+ .has("vp2")
+ .as("m"))
+ .<Vertex>select("m")
+ .count();
+
+ HugeGraphStep<?, ?> graphStep = applyAndGetGraphStep(traversal);
+ Assert.assertEquals(1, graphStep.getHasContainers().size());
+ Assert.assertEquals("vp2",
graphStep.getHasContainers().get(0).getKey());
+ Assert.assertTrue(hasRemainingHasStep(traversal, "vp3"));
+ Assert.assertEquals(0L, traversal.next());
+ }
+
+ @Test
+ public void testMatchWithSystemRangeConditionMatchesDirectTraversal() {
+ this.initMatchNoIndexSchema();
+ this.initMatchNoIndexGraph();
+
+ long direct = graph().traversal().V()
+ .hasLabel("vl1")
+ .has("vp2")
+ .count().next();
+ GraphTraversal<Vertex, Long> traversal = graph().traversal().V()
+ .hasLabel(P.neq("vl0"))
+
.match(__.<Vertex>as("s")
+ .has("vp2")
+ .as("m"))
+ .<Vertex>select("m")
+ .count();
+
+ HugeGraphStep<?, ?> graphStep = applyAndGetGraphStep(traversal);
+ Assert.assertTrue(hasRemainingHasStep(traversal,
T.label.getAccessor()));
+ Assert.assertEquals(direct, traversal.next().longValue());
+ }
+
+ @Test
+ public void testMatchWithUniqueBooleanRangeConditionKeepsHas() {
+ this.initMatchNoIndexSchema();
+ graph().schema().indexLabel("vl1ByUniqueVp2").onV("vl1")
+ .by("vp2").unique().create();
+ this.initMatchNoIndexGraph();
+
+ GraphTraversal<Vertex, Long> traversal = graph().traversal().V()
+ .has("vp2", P.lt(true))
+
.match(__.<Vertex>as("s")
+ .has("vp2")
+ .as("m"))
+ .<Vertex>select("m")
+ .count();
+
+ HugeGraphStep<?, ?> graphStep = applyAndGetGraphStep(traversal);
+ Assert.assertEquals(0, graphStep.getHasContainers().size());
+ Assert.assertTrue(hasRemainingHasStep(traversal, "vp2"));
+ Assert.assertEquals(1L, traversal.next());
+ }
}
diff --git
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtilOptimizeTest.java
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtilOptimizeTest.java
index cdf6addfc..c3208a0b9 100644
---
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtilOptimizeTest.java
+++
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtilOptimizeTest.java
@@ -60,6 +60,16 @@ public class TraversalUtilOptimizeTest {
graph, new HasContainer("missing", P.eq("marko"))));
}
+ @Test
+ public void testIndexLabelOrNullWithMissingIndexLabel() {
+ HugeGraph graph = Mockito.mock(HugeGraph.class);
+ Id id = IdGenerator.of(1L);
+ Mockito.when(graph.indexLabel(id))
+ .thenThrow(new IllegalArgumentException("missing"));
+
+ Assert.assertNull(TraversalUtil.indexLabelOrNull(graph, id));
+ }
+
@Test
public void testCanExtractHasContainerWithNonTextProperty() {
HugeGraph graph = Mockito.mock(HugeGraph.class);
@@ -116,6 +126,20 @@ public class TraversalUtilOptimizeTest {
Assert.assertTrue(hasStepExists(traversal));
}
+ @Test
+ public void testExtractHasContainerKeepsMatchRangeWithoutGraph() {
+ Traversal.Admin<?, ?> traversal = __.V()
+ .has("age", P.gt(18))
+
.match(__.as("v").identity().as("m"))
+ .asAdmin();
+ HugeGraphStep<?, ?> newStep = replaceGraphStep(traversal);
+
+ TraversalUtil.extractHasContainer(newStep, traversal);
+
+ Assert.assertTrue(newStep.getHasContainers().isEmpty());
+ Assert.assertTrue(hasStepExists(traversal));
+ }
+
@Test
public void testExtractHasContainerKeepsTextBetweenGraphHasStep() {
HugeGraph graph = Mockito.mock(HugeGraph.class);