// 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.core.JanusGraphVertex; import org.janusgraph.graphdb.types.system.ImplicitKey; import org.apache.tinkerpop.gremlin.process.traversal.Compare; import org.apache.tinkerpop.gremlin.process.traversal.P; 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.filter.HasStep; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.IsStep; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TraversalFilterStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeOtherVertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeVertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IdentityStep; import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Vertex; import java.util.List; /** * @author Matthias Broecheler (me@matthiasb.com) */ public class AdjacentVertexFilterOptimizerStrategy extends AbstractTraversalStrategy<TraversalStrategy.ProviderOptimizationStrategy> implements TraversalStrategy.ProviderOptimizationStrategy { private static final AdjacentVertexFilterOptimizerStrategy INSTANCE = new AdjacentVertexFilterOptimizerStrategy(); private AdjacentVertexFilterOptimizerStrategy() { } public static AdjacentVertexFilterOptimizerStrategy instance() { return INSTANCE; } @Override public void apply(final Traversal.Admin<?, ?> traversal) { TraversalHelper.getStepsOfClass(TraversalFilterStep.class, traversal).forEach(originalStep -> { // Check if this filter traversal matches the pattern: _.inV/outV/otherV.is(x) Traversal.Admin<?, ?> filterTraversal = (Traversal.Admin<?, ?>) originalStep.getLocalChildren().get(0); List<Step> steps = filterTraversal.getSteps(); if (steps.size() == 2 && (steps.get(0) instanceof EdgeVertexStep || steps.get(0) instanceof EdgeOtherVertexStep) && (steps.get(1) instanceof IsStep)) { //Get the direction in which we filter on the adjacent vertex (or null if not a valid adjacency filter) Direction direction = null; if (steps.get(0) instanceof EdgeVertexStep) { EdgeVertexStep evs = (EdgeVertexStep) steps.get(0); if (evs.getDirection() != Direction.BOTH) direction = evs.getDirection(); } else { assert steps.get(0) instanceof EdgeOtherVertexStep; direction = Direction.BOTH; } P predicate = ((IsStep) steps.get(1)).getPredicate(); //Check that we have a valid direction and a valid vertex filter predicate if (direction != null && predicate.getBiPredicate() == Compare.eq && predicate.getValue() instanceof Vertex) { JanusGraphVertex vertex = JanusGraphTraversalUtil.getJanusGraphVertex((Vertex) predicate.getValue()); //Now, check that this step is preceeded by VertexStep that returns edges Step<?, ?> currentStep = originalStep.getPreviousStep(); while (true) { if (currentStep instanceof HasStep || currentStep instanceof IdentityStep) { //We can jump over those steps as we move backward } else break; } if (currentStep instanceof VertexStep) { VertexStep vstep = (VertexStep) currentStep; if (vstep.returnsEdge() && (direction == Direction.BOTH || direction.equals(vstep.getDirection().opposite()))) { //Now replace the step with a has condition TraversalHelper.replaceStep(originalStep, new HasStep(traversal, HasContainer.makeHasContainers(ImplicitKey.ADJACENT_ID.name(), P.eq(vertex))), traversal); } } } } }); } }