// Copyright 2017 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.janusgraph.graphdb.database.StandardJanusGraph;
import org.janusgraph.graphdb.query.QueryUtil;
import org.janusgraph.graphdb.transaction.StandardJanusGraphTx;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import org.apache.tinkerpop.gremlin.structure.Graph;
import java.util.Collections;
import java.util.Set;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
* @author Matthias Broecheler (http://matthiasb.com)
*/
public class JanusGraphLocalQueryOptimizerStrategy extends AbstractTraversalStrategy<TraversalStrategy.ProviderOptimizationStrategy> implements TraversalStrategy.ProviderOptimizationStrategy {
private static final JanusGraphLocalQueryOptimizerStrategy INSTANCE = new JanusGraphLocalQueryOptimizerStrategy();
private JanusGraphLocalQueryOptimizerStrategy() {
}
@Override
public void apply(final Traversal.Admin<?, ?> traversal) {
if (!traversal.getGraph().isPresent())
return;
Graph graph = traversal.getGraph().get();
//If this is a compute graph then we can't apply local traversal optimisation at this stage.
StandardJanusGraph janusGraph = graph instanceof StandardJanusGraphTx ? ((StandardJanusGraphTx) graph).getGraph() : (StandardJanusGraph) graph;
final boolean useMultiQuery = !TraversalHelper.onGraphComputer(traversal) && janusGraph.getConfiguration().useMultiQuery();
/*
====== VERTEX STEP ======
*/
TraversalHelper.getStepsOfClass(VertexStep.class, traversal).forEach(originalStep -> {
JanusGraphVertexStep vstep = new JanusGraphVertexStep(originalStep);
TraversalHelper.replaceStep(originalStep, vstep, traversal);
if (JanusGraphTraversalUtil.isEdgeReturnStep(vstep)) {
HasStepFolder.foldInHasContainer(vstep, traversal);
//We cannot fold in orders or ranges since they are not local
}
assert JanusGraphTraversalUtil.isEdgeReturnStep(vstep) || JanusGraphTraversalUtil.isVertexReturnStep(vstep);
Step nextStep = JanusGraphTraversalUtil.getNextNonIdentityStep(vstep);
if (nextStep instanceof RangeGlobalStep) {
int limit = QueryUtil.convertLimit(((RangeGlobalStep) nextStep).getHighRange());
vstep.setLimit(QueryUtil.mergeLimits(limit, vstep.getLimit()));
}
if (useMultiQuery) {
vstep.setUseMultiQuery(true);
}
});
/*
====== PROPERTIES STEP ======
*/
TraversalHelper.getStepsOfClass(PropertiesStep.class, traversal).forEach(originalStep -> {
JanusGraphPropertiesStep vstep = new JanusGraphPropertiesStep(originalStep);
TraversalHelper.replaceStep(originalStep, vstep, traversal);
if (vstep.getReturnType().forProperties()) {
HasStepFolder.foldInHasContainer(vstep, traversal);
//We cannot fold in orders or ranges since they are not local
}
if (useMultiQuery) {
vstep.setUseMultiQuery(true);
}
});
/*
====== EITHER INSIDE LOCAL ======
*/
TraversalHelper.getStepsOfClass(LocalStep.class, traversal).forEach(localStep -> {
Traversal.Admin localTraversal = ((LocalStep<?, ?>) localStep).getLocalChildren().get(0);
Step localStart = localTraversal.getStartStep();
if (localStart instanceof VertexStep) {
JanusGraphVertexStep vstep = new JanusGraphVertexStep((VertexStep) localStart);
TraversalHelper.replaceStep(localStart, vstep, localTraversal);
if (JanusGraphTraversalUtil.isEdgeReturnStep(vstep)) {
HasStepFolder.foldInHasContainer(vstep, localTraversal);
HasStepFolder.foldInOrder(vstep, localTraversal, traversal, false);
}
HasStepFolder.foldInRange(vstep, localTraversal);
unfoldLocalTraversal(traversal,localStep,localTraversal,vstep,useMultiQuery);
}
if (localStart instanceof PropertiesStep) {
JanusGraphPropertiesStep vstep = new JanusGraphPropertiesStep((PropertiesStep) localStart);
TraversalHelper.replaceStep(localStart, vstep, localTraversal);
if (vstep.getReturnType().forProperties()) {
HasStepFolder.foldInHasContainer(vstep, localTraversal);
HasStepFolder.foldInOrder(vstep, localTraversal, traversal, false);
}
HasStepFolder.foldInRange(vstep, localTraversal);
unfoldLocalTraversal(traversal,localStep,localTraversal,vstep,useMultiQuery);
}
});
}
private static void unfoldLocalTraversal(final Traversal.Admin<?, ?> traversal,
LocalStep<?,?> localStep, Traversal.Admin localTraversal,
MultiQueriable vstep, boolean useMultiQuery) {
assert localTraversal.asAdmin().getSteps().size() > 0;
if (localTraversal.asAdmin().getSteps().size() == 1) {
//Can replace the entire localStep by the vertex step in the outer traversal
assert localTraversal.getStartStep() == vstep;
vstep.setTraversal(traversal);
TraversalHelper.replaceStep(localStep, vstep, traversal);
if (useMultiQuery) {
vstep.setUseMultiQuery(true);
}
}
}
private static final Set<Class<? extends ProviderOptimizationStrategy>> PRIORS = Collections.singleton(AdjacentVertexFilterOptimizerStrategy.class);
@Override
public Set<Class<? extends ProviderOptimizationStrategy>> applyPrior() {
return PRIORS;
}
public static JanusGraphLocalQueryOptimizerStrategy instance() {
return INSTANCE;
}
}