fix(server): handle match() in no index case (#3039)
This commit is contained in:
parent
d6e6216222
commit
7f0a44adc0
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue