| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,14 +22,15 @@ | |
| import org.apache.tinkerpop.gremlin.process.traversal.step.Profiling; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalInterruptedException; | ||
| import org.apache.tinkerpop.gremlin.structure.Element; | ||
| import org.apache.tinkerpop.gremlin.structure.Property; | ||
| import org.apache.tinkerpop.gremlin.structure.Vertex; | ||
| import org.apache.tinkerpop.gremlin.structure.util.StringFactory; | ||
| import org.apache.tinkerpop.gremlin.structure.util.wrapped.WrappedVertex; | ||
| import org.janusgraph.core.BaseVertexQuery; | ||
| import org.janusgraph.core.JanusGraphException; | ||
| import org.janusgraph.core.JanusGraphMultiVertexQuery; | ||
| import org.janusgraph.core.JanusGraphProperty; | ||
| import org.janusgraph.core.JanusGraphVertex; | ||
|
|
@@ -43,34 +44,48 @@ | |
| import org.janusgraph.graphdb.tinkerpop.profile.TP3ProfileWrapper; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.HashSet; | ||
| import java.util.Iterator; | ||
| import java.util.LinkedList; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
|
|
||
| /** | ||
| * @author Matthias Broecheler (me@matthiasb.com) | ||
| */ | ||
| public class JanusGraphPropertiesStep<E> extends PropertiesStep<E> implements HasStepFolder<Element, E>, Profiling, MultiQueriable<Element,E> { | ||
|
|
||
| private boolean useMultiQuery = false; | ||
| private Set<Vertex> verticesToPrefetch = new HashSet<>(); | ||
| private Map<JanusGraphVertex, Iterable<? extends JanusGraphProperty>> multiQueryResults = null; | ||
| private QueryProfiler queryProfiler = QueryProfiler.NO_OP; | ||
|
|
||
| public JanusGraphPropertiesStep(PropertiesStep<E> originalStep) { | ||
| super(originalStep.getTraversal(), originalStep.getReturnType(), originalStep.getPropertyKeys()); | ||
| originalStep.getLabels().forEach(this::addLabel); | ||
|
|
||
| if (originalStep instanceof JanusGraphPropertiesStep) { | ||
rngcntr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| JanusGraphPropertiesStep originalJanusGraphPropertiesStep = (JanusGraphPropertiesStep) originalStep; | ||
| this.useMultiQuery = originalJanusGraphPropertiesStep.useMultiQuery; | ||
| this.hasContainers = originalJanusGraphPropertiesStep.hasContainers; | ||
| this.limit = originalJanusGraphPropertiesStep.limit; | ||
| } else { | ||
| this.hasContainers = new ArrayList<>(); | ||
| this.limit = Query.NO_LIMIT; | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void setUseMultiQuery(boolean useMultiQuery) { | ||
| this.useMultiQuery = useMultiQuery; | ||
| } | ||
|
|
||
| @Override | ||
| public void registerFutureVertexForPrefetching(Vertex futureVertex) { | ||
| verticesToPrefetch.add(futureVertex); | ||
| } | ||
|
|
||
| private <Q extends BaseVertexQuery> Q makeQuery(Q query) { | ||
| final String[] keys = getPropertyKeys(); | ||
| query.keys(keys); | ||
|
|
@@ -91,55 +106,30 @@ private Iterator<E> convertIterator(Iterable<? extends JanusGraphProperty> itera | |
| return (Iterator<E>) Iterators.transform(iterable.iterator(), Property::value); | ||
| } | ||
|
|
||
| /** | ||
| * This initialisation method is called when an attempt to retrieve a vertex from the cached multiQuery results | ||
| * doesn't find an entry. | ||
| */ | ||
| private void prefetchNextBatch() { | ||
| final JanusGraphMultiVertexQuery multiQuery = JanusGraphTraversalUtil.getTx(getTraversal()).multiQuery(); | ||
| multiQuery.addAllVertices(verticesToPrefetch); | ||
| verticesToPrefetch.clear(); | ||
| makeQuery(multiQuery); | ||
|
|
||
| try { | ||
| multiQueryResults = multiQuery.properties(); | ||
| } catch (JanusGraphException janusGraphException) { | ||
|
There was a problem hiding this comment. Why is this needed? The signature of JanusGraphMultiVertexQuery::properties does not say it throws exceptions. There was a problem hiding this comment. I explicitly added that There was a problem hiding this comment. Can you share which test fails? In that case why the old code does not suffer this problem? if (Thread.interrupted()) throw new TraversalInterruptedException()otherwise it does not seem to be semantically correct There was a problem hiding this comment. This is the test that fails if no It creates a I think what happened before this PR is that the There was a problem hiding this comment. Got it. Now it looks good to me. I am just confused why CodeCov complains line 123 is not covered. There was a problem hiding this comment. Somehow Codecov is still reporting that line 123 is not covered by tests. If you set a debug point on line 123 and run the test locally, do you see it get executed? There was a problem hiding this comment. Yes, I did exactly that when I was debugging the exception handling and trying to figure out which Exception has to be caught/thrown. I don't see why Codecov complains here, since the breakpoint definitely triggers. There was a problem hiding this comment. It's probably not a codecov problem. I downloaded the jacoco report from this GitHub Actions run and loaded into Intellij. It shows that JanusGraphPropertiesStep has 0% coverage. There was a problem hiding this comment. I used this one for debugging. It executes a number of TinkerPop tests, including the one you mentioned. Executing this test class leads to the breakpoint being triggered. There was a problem hiding this comment. Thanks, indeed. I also verified that test indeed was executed in CI. No idea why jacoco does not include |
||
| if (janusGraphException.isCausedBy(InterruptedException.class)) { | ||
| throw new TraversalInterruptedException(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| protected Iterator<E> flatMap(final Traverser.Admin<Element> traverser) { | ||
| if (useMultiQuery && traverser.get() instanceof Vertex) { | ||
rngcntr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (multiQueryResults == null || !multiQueryResults.containsKey(traverser.get())) { | ||
| prefetchNextBatch(); | ||
| } | ||
| return convertIterator(multiQueryResults.get(traverser.get())); | ||
| } else if (traverser.get() instanceof JanusGraphVertex || traverser.get() instanceof WrappedVertex) { | ||
|
|
@@ -172,19 +162,6 @@ protected Iterator<E> flatMap(final Traverser.Admin<Element> traverser) { | |
| } | ||
| } | ||
|
|
||
| /* | ||
| ===== HOLDER ===== | ||
| */ | ||
|
|
@@ -211,7 +188,7 @@ public void orderBy(String key, Order order) { | |
|
|
||
| @Override | ||
| public void localOrderBy(List<HasContainer> hasContainers, String key, Order order) { | ||
| throw new UnsupportedOperationException("LocalOrderBy is not supported for properties step."); | ||
| } | ||
|
|
||
| @Override | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| // Copyright 2021 JanusGraph Authors | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package org.janusgraph.graphdb.tinkerpop.optimize.strategy; | ||
|
|
||
| import org.apache.tinkerpop.gremlin.process.traversal.Step; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DropStep; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.step.map.NoOpBarrierStep; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; | ||
| import org.janusgraph.graphdb.database.StandardJanusGraph; | ||
| import org.janusgraph.graphdb.tinkerpop.optimize.JanusGraphTraversalUtil; | ||
| import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphMultiQueryStep; | ||
| import org.janusgraph.graphdb.tinkerpop.optimize.step.MultiQueriable; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.HashSet; | ||
| import java.util.List; | ||
| import java.util.Optional; | ||
| import java.util.Set; | ||
|
|
||
| /** | ||
| * @author Marko A. Rodriguez (https://markorodriguez.com) | ||
| * @author Matthias Broecheler (http://matthiasb.com) | ||
| */ | ||
| public class JanusGraphMultiQueryStrategy extends AbstractTraversalStrategy<TraversalStrategy.ProviderOptimizationStrategy> implements TraversalStrategy.ProviderOptimizationStrategy { | ||
|
|
||
| private static final Set<Class<? extends ProviderOptimizationStrategy>> PRIORS = new HashSet<>(Arrays.asList(JanusGraphLocalQueryOptimizerStrategy.class, JanusGraphStepStrategy.class)); | ||
li-boxuan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| private static final JanusGraphMultiQueryStrategy INSTANCE = new JanusGraphMultiQueryStrategy(); | ||
|
|
||
| private JanusGraphMultiQueryStrategy() { | ||
| } | ||
|
|
||
| @Override | ||
| public void apply(final Admin<?, ?> traversal) { | ||
| if (!traversal.getGraph().isPresent() | ||
| || TraversalHelper.onGraphComputer(traversal) | ||
| // The LazyBarrierStrategy is not allowed to run on traversals which use drop(). As a precaution, | ||
| // this strategy should not run on those traversals either, because it can also insert barrier(). | ||
| || !TraversalHelper.getStepsOfAssignableClassRecursively(DropStep.class, traversal).isEmpty()) { | ||
| return; | ||
| } | ||
|
|
||
| final StandardJanusGraph janusGraph = JanusGraphTraversalUtil.getJanusGraph(traversal); | ||
| if (janusGraph == null || !janusGraph.getConfiguration().useMultiQuery()) { | ||
| return; | ||
| } | ||
|
|
||
| insertMultiQuerySteps(traversal, janusGraph.getConfiguration().limitBatchSize()); | ||
| configureMultiQueriables(traversal); | ||
| } | ||
|
|
||
| /** | ||
| * Insert JanusGraphMultiQuerySteps everywhere in the current traversal where MultiQueriable steps could benefit | ||
| * | ||
| * @param traversal The local traversal layer. | ||
| */ | ||
| private void insertMultiQuerySteps(final Admin<?, ?> traversal, boolean limitBatchSize) { | ||
| JanusGraphTraversalUtil.getSteps(JanusGraphTraversalUtil::isMultiQueryCompatibleStep, traversal).forEach(step -> { | ||
| Optional<Step> multiQueryPosition = JanusGraphTraversalUtil.getLocalMultiQueryPositionForStep(step); | ||
| if (multiQueryPosition.isPresent() && JanusGraphTraversalUtil.isLegalMultiQueryPosition(multiQueryPosition.get())) { | ||
| Step pos = multiQueryPosition.get(); | ||
| if (limitBatchSize && !(multiQueryPosition.get() instanceof NoOpBarrierStep)) { | ||
| NoOpBarrierStep barrier = new NoOpBarrierStep(traversal); | ||
| TraversalHelper.insertBeforeStep(barrier, pos, traversal); | ||
| pos = barrier; | ||
li-boxuan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| JanusGraphMultiQueryStep multiQueryStep = new JanusGraphMultiQueryStep(traversal, limitBatchSize); | ||
| TraversalHelper.insertBeforeStep(multiQueryStep, pos, traversal); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Looks for MultiQueriables in within the traversal and registers them as clients of their respective | ||
| * JanusGraphMultiQuerySteps | ||
| * | ||
| * @param traversal The local traversal layer. | ||
| */ | ||
| private void configureMultiQueriables(final Admin<?, ?> traversal) { | ||
| TraversalHelper.getStepsOfAssignableClass(MultiQueriable.class, traversal).forEach(multiQueriable -> { | ||
| final List<Step> mqPositions = JanusGraphTraversalUtil.getAllMultiQueryPositionsForMultiQueriable(multiQueriable); | ||
|
|
||
| // If one position is not legal, this means that the entire step can not use the multiQuery feature. | ||
| for (Step mqPos : mqPositions) { | ||
| if (!JanusGraphTraversalUtil.isLegalMultiQueryPosition(mqPos)) { | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // MultiQuery is applicable | ||
| multiQueriable.setUseMultiQuery(true); | ||
| for (Step mqPos : mqPositions) { | ||
| final Optional<JanusGraphMultiQueryStep> multiQueryStep = | ||
| JanusGraphTraversalUtil.getPreviousStepOfClass(JanusGraphMultiQueryStep.class, mqPos); | ||
| multiQueryStep.ifPresent(mqs -> mqs.attachClient(multiQueriable)); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| @Override | ||
| public Set<Class<? extends ProviderOptimizationStrategy>> applyPrior() { | ||
| return PRIORS; | ||
| } | ||
|
|
||
| public static JanusGraphMultiQueryStrategy instance() { | ||
| return INSTANCE; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| // Copyright 2021 JanusGraph Authors | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package org.janusgraph.graphdb.tinkerpop.optimize; | ||
|
|
||
| import org.apache.tinkerpop.gremlin.process.traversal.Traversal; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet; | ||
| import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphMultiQueryStep; | ||
| import org.janusgraph.graphdb.tinkerpop.optimize.step.MultiQueriable; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.Arguments; | ||
| import org.junit.jupiter.params.provider.MethodSource; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.Collection; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.stream.Stream; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
| import static org.junit.jupiter.params.provider.Arguments.arguments; | ||
| import static org.mockito.Mockito.mock; | ||
| import static org.mockito.Mockito.when; | ||
|
|
||
| public class JanusGraphMultiQueryStepTest { | ||
|
|
||
| @ParameterizedTest | ||
| @MethodSource("generateTestParameters") | ||
| public void testClone(Traversal.Admin traversal, boolean limitBatchSize, Collection<MultiQueriable> clients) { | ||
| JanusGraphMultiQueryStep originalStep = new JanusGraphMultiQueryStep(traversal, limitBatchSize); | ||
| clients.forEach(originalStep::attachClient); | ||
|
|
||
| JanusGraphMultiQueryStep clone = originalStep.clone(); | ||
|
|
||
| assertEquals(limitBatchSize, clone.isLimitBatchSize()); | ||
| assertEquals(originalStep.getClientSteps().size(), clone.getClientSteps().size()); | ||
| assertTrue(clone.getClientSteps().containsAll(originalStep.getClientSteps())); | ||
| assertTrue(originalStep.getClientSteps().containsAll(clone.getClientSteps())); | ||
| } | ||
|
|
||
| @ParameterizedTest | ||
| @MethodSource("generateTestParameters") | ||
| public void testReset(Traversal.Admin traversal, boolean limitBatchSize, Collection<MultiQueriable> clients) { | ||
| JanusGraphMultiQueryStep originalStep = new JanusGraphMultiQueryStep(traversal, limitBatchSize); | ||
| clients.forEach(originalStep::attachClient); | ||
|
|
||
| originalStep.reset(); | ||
|
|
||
| assertEquals(limitBatchSize, originalStep.isLimitBatchSize()); | ||
| assertEquals(originalStep.getClientSteps().size(), clients.size()); | ||
| assertTrue(clients.containsAll(originalStep.getClientSteps())); | ||
| assertTrue(originalStep.getClientSteps().containsAll(clients)); | ||
| } | ||
|
|
||
| private static Stream<Arguments> generateTestParameters() { | ||
li-boxuan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Traversal.Admin mockedTraversal = mock(Traversal.Admin.class); | ||
| when(mockedTraversal.getTraverserSetSupplier()).thenReturn(TraverserSet::new); | ||
|
|
||
| MultiQueriable mqA = mock(MultiQueriable.class); | ||
| MultiQueriable mqB = mock(MultiQueriable.class); | ||
|
|
||
| List<MultiQueriable> emptyClientList = Collections.emptyList(); | ||
| List<MultiQueriable> singleClientList = Collections.singletonList(mqA); | ||
| List<MultiQueriable> multiClientList = Arrays.asList(mqA, mqB); | ||
|
|
||
| return Arrays.stream(new Arguments[]{ | ||
| arguments(mockedTraversal, true, emptyClientList), | ||
| arguments(mockedTraversal, false, emptyClientList), | ||
| arguments(mockedTraversal, true, singleClientList), | ||
| arguments(mockedTraversal, false, singleClientList), | ||
| arguments(mockedTraversal, true, multiClientList), | ||
| arguments(mockedTraversal, false, multiClientList) | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| // Copyright 2020 JanusGraph Authors | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package org.janusgraph.graphdb.tinkerpop.optimize; | ||
|
|
||
| import org.apache.tinkerpop.gremlin.process.traversal.P; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.Traversal; | ||
| 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.branch.ChooseStep; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalStep; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.step.branch.OptionalStep; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.step.branch.RepeatStep; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionStep; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TraversalFilterStep; | ||
| import org.apache.tinkerpop.gremlin.process.traversal.step.map.NoOpBarrierStep; | ||
| import org.apache.tinkerpop.gremlin.structure.Edge; | ||
| import org.janusgraph.graphdb.query.profile.QueryProfiler; | ||
| import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphMultiQueryStep; | ||
| import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphPropertiesStep; | ||
| import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphStep; | ||
| import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphVertexStep; | ||
| import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphLocalQueryOptimizerStrategy; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import static org.apache.tinkerpop.gremlin.process.traversal.Order.desc; | ||
| import static org.janusgraph.graphdb.JanusGraphBaseTest.option; | ||
| import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.LIMIT_BATCH_SIZE; | ||
| import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.USE_MULTIQUERY; | ||
| import static org.janusgraph.testutil.JanusGraphAssert.assertNumStep; | ||
| import static org.janusgraph.testutil.JanusGraphAssert.assertCount; | ||
| import static org.janusgraph.testutil.JanusGraphAssert.queryProfilerAnnotationIsPresent; | ||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| public class JanusGraphMultiQueryStrategyTest extends OptimizerStrategyTest { | ||
|
|
||
| @Test | ||
| public void testQueryIsExecutableIfJanusGraphLocalQueryOptimizerStrategyIsEnabled() { | ||
| clopen(option(USE_MULTIQUERY), true); | ||
| makeSampleGraph(); | ||
|
|
||
| final List<Edge> normalResults = g.V(sv[0]).outE().inV().choose(__.inE("knows").has("weight", 0), __.inE("knows").has("weight", 1), __.inE("knows").has("weight", 2)).toList(); | ||
| final List<Edge> resultsWithDisabledStrategy = g.withoutStrategies(JanusGraphLocalQueryOptimizerStrategy.class).V(sv[0]).outE().inV().choose(__.inE("knows").has("weight", 0), __.inE("knows").has("weight", 1), __.inE("knows").has("weight", 2)).toList(); | ||
|
|
||
| assertEquals(normalResults, resultsWithDisabledStrategy); | ||
| } | ||
|
|
||
| @Test | ||
| public void testNoMultiQueryStepsInsertedIfPathQuery() { | ||
| clopen(option(USE_MULTIQUERY), true); | ||
| makeSampleGraph(); | ||
|
|
||
| final GraphTraversal<?,?> traversalWithoutPath = g.V(sv[0]).outE().inV(); | ||
| assertNumStep(numV, 1, traversalWithoutPath, JanusGraphMultiQueryStep.class); | ||
|
|
||
| final GraphTraversal<?,?> traversalWithPath = g.V(sv[0]).outE().inV().path(); | ||
| assertNumStep(numV, 0, traversalWithPath, JanusGraphMultiQueryStep.class); | ||
|
|
||
| final GraphTraversal<?,?> traversalWithNestedPath = g.V(sv[0]).outE().inV().where(__.path()); | ||
| assertNumStep(numV, 0, traversalWithNestedPath, JanusGraphMultiQueryStep.class); | ||
| } | ||
|
|
||
| @Test | ||
| public void testNoOpBarrierStepInsertedIfNotPresentAndLimitBatchSize() { | ||
| clopen(option(USE_MULTIQUERY), true, option(LIMIT_BATCH_SIZE), true); | ||
| makeSampleGraph(); | ||
|
|
||
| final GraphTraversal<?,?> traversalWithoutExplicitBarrier = g.V(sv[0]).outE().inV(); | ||
| assertNumStep(numV, 1, traversalWithoutExplicitBarrier, NoOpBarrierStep.class); | ||
|
|
||
| final GraphTraversal<?,?> traversalWithExplicitBarrier = g.V(sv[0]).barrier(1).outE().inV(); | ||
| assertNumStep(numV, 1, traversalWithExplicitBarrier, NoOpBarrierStep.class); | ||
| } | ||
|
|
||
| @Test | ||
| public void testNoOpBarrierStepNotInsertedLimitBatchSizeDisabled() { | ||
| clopen(option(USE_MULTIQUERY), true, option(LIMIT_BATCH_SIZE), false); | ||
| makeSampleGraph(); | ||
|
|
||
| final GraphTraversal<?,?> traversalWithoutExplicitBarrier = g.V(sv[0]).outE().inV(); | ||
| assertNumStep(numV, 0, traversalWithoutExplicitBarrier, NoOpBarrierStep.class); | ||
|
|
||
| final GraphTraversal<?,?> traversalWithExplicitBarrier = g.V(sv[0]).barrier(1).outE().inV(); | ||
| assertNumStep(numV, 1, traversalWithExplicitBarrier, NoOpBarrierStep.class); | ||
| } | ||
|
|
||
| @Test | ||
| public void testMultiQuery() { | ||
| clopen(option(USE_MULTIQUERY), true); | ||
| makeSampleGraph(); | ||
|
|
||
| Traversal t = g.V(sv[0]).outE().inV().choose(__.inE("knows").has("weight", 0),__.inE("knows").has("weight", 1), __.inE("knows").has("weight", 2)).profile("~metrics"); | ||
| assertNumStep(numV * 2, 2, (GraphTraversal)t, ChooseStep.class, JanusGraphVertexStep.class); | ||
| assertTrue(queryProfilerAnnotationIsPresent(t, QueryProfiler.MULTIQUERY_ANNOTATION)); | ||
|
|
||
| t = g.V(sv[0]).outE().inV().union(__.inE("knows").has("weight", 0),__.inE("knows").has("weight", 1),__.inE("knows").has("weight", 2)).profile("~metrics"); | ||
| assertNumStep(numV * 6, 2, (GraphTraversal)t, UnionStep.class, JanusGraphVertexStep.class); | ||
| assertTrue(queryProfilerAnnotationIsPresent(t, QueryProfiler.MULTIQUERY_ANNOTATION)); | ||
|
|
||
| int[] loop = {0}; // repeat starts from vertex with id 0 and goes in to the sv[0] vertex then loops back out to the vertex with the next id | ||
| t = g.V(vs[0], vs[1], vs[2]) | ||
| .repeat(__.inE("knows") | ||
| .outV() | ||
| .hasId(sv[0].id()) | ||
| .out("knows") // TINKERPOP-2342 | ||
| .sideEffect(e -> loop[0] = e.loops()) | ||
| .has("id", loop[0])) | ||
| .times(numV) | ||
| .profile("~metrics"); | ||
| assertNumStep(3, 1, (GraphTraversal)t, RepeatStep.class); | ||
| assertEquals(numV - 1, loop[0]); | ||
| assertTrue(queryProfilerAnnotationIsPresent(t, QueryProfiler.MULTIQUERY_ANNOTATION)); | ||
|
|
||
| t = g.V(vs[0],vs[1],vs[2]).optional(__.inE("knows").has("weight", 0)).profile("~metrics"); | ||
| assertNumStep(12, 1, (GraphTraversal)t, OptionalStep.class); | ||
| assertTrue(queryProfilerAnnotationIsPresent(t, QueryProfiler.MULTIQUERY_ANNOTATION)); | ||
|
|
||
| t = g.V(vs[0],vs[1],vs[2]).filter(__.inE("knows").has("weight", 0)).profile("~metrics"); | ||
| assertNumStep(1, 1, (GraphTraversal)t, TraversalFilterStep.class); | ||
| assertTrue(queryProfilerAnnotationIsPresent(t, QueryProfiler.MULTIQUERY_ANNOTATION)); | ||
|
|
||
| assertNumStep(superV * (numV / 5), 2, g.V().has("id", sid).outE("knows").has("weight", 1), JanusGraphStep.class, JanusGraphVertexStep.class); | ||
| assertNumStep(superV * (numV / 5 * 2), 2, g.V().has("id", sid).outE("knows").has("weight", P.between(1, 3)), JanusGraphStep.class, JanusGraphVertexStep.class); | ||
| assertNumStep(superV * 10, 2, g.V().has("id", sid).local(__.outE("knows").has("weight", P.gte(1)).has("weight", P.lt(3)).limit(10)), JanusGraphStep.class, JanusGraphVertexStep.class); | ||
| assertNumStep(superV * 10, 1, g.V().has("id", sid).local(__.outE("knows").has("weight", P.between(1, 3)).order().by("weight", desc).limit(10)), JanusGraphStep.class); | ||
| assertNumStep(superV * 10, 0, g.V().has("id", sid).local(__.outE("knows").has("weight", P.between(1, 3)).order().by("weight", desc).limit(10)), LocalStep.class); | ||
| assertNumStep(superV * numV, 2, g.V().has("id", sid).values("names"), JanusGraphStep.class, JanusGraphPropertiesStep.class); | ||
|
|
||
| //Verify traversal metrics when all reads are from cache (i.e. no backend queries) | ||
| t = g.V().has("id", sid).local(__.outE("knows").has("weight", P.between(1, 3)).order().by("weight", desc).limit(10)).profile("~metrics"); | ||
| assertCount(superV * 10, t); | ||
| assertTrue(queryProfilerAnnotationIsPresent(t, QueryProfiler.MULTIQUERY_ANNOTATION)); | ||
|
|
||
| //Verify that properties also use multi query | ||
| t = g.V().has("id", sid).values("names").profile("~metrics"); | ||
| assertCount(superV * numV, t); | ||
| assertTrue(queryProfilerAnnotationIsPresent(t, QueryProfiler.MULTIQUERY_ANNOTATION)); | ||
| } | ||
| } |