// 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.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.NoOpBarrierStep; import org.apache.tinkerpop.gremlin.process.traversal.util.AndP; import org.janusgraph.core.Cardinality; import org.janusgraph.core.PropertyKey; import org.janusgraph.core.JanusGraphTransaction; import org.janusgraph.graphdb.query.QueryUtil; import org.janusgraph.graphdb.query.JanusGraphPredicate; import org.apache.tinkerpop.gremlin.process.traversal.Order; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder; import org.apache.tinkerpop.gremlin.process.traversal.step.Ranging; 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.OrderGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IdentityStep; import org.apache.tinkerpop.gremlin.process.traversal.step.util.ElementValueComparator; import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; import org.javatuples.Pair; import java.util.Arrays; import java.util.Comparator; import java.util.List; /** * @author Matthias Broecheler (me@matthiasb.com) */ public interface HasStepFolder<S, E> extends Step<S, E> { void addAll(Iterable<HasContainer> hasContainers); void orderBy(String key, Order order); void setLimit(int limit); int getLimit(); static boolean validJanusGraphHas(HasContainer has) { if (has.getPredicate() instanceof AndP) { final List<? extends P<?>> predicates = ((AndP<?>) has.getPredicate()).getPredicates(); return !predicates.stream().filter(p->!validJanusGraphHas(new HasContainer(has.getKey(), p))).findAny().isPresent(); } else { return JanusGraphPredicate.Converter.supports(has.getBiPredicate()); } } static boolean validJanusGraphHas(Iterable<HasContainer> has) { for (HasContainer h : has) { if (!validJanusGraphHas(h)) return false; } return true; } static boolean validJanusGraphOrder(OrderGlobalStep ostep, Traversal rootTraversal, boolean isVertexOrder) { for (Pair<Traversal.Admin<Object, Comparable>, Comparator<Comparable>> comp : (List<Pair<Traversal.Admin<Object, Comparable>, Comparator<Comparable>>>) ostep.getComparators()) { if (!(comp.getValue1() instanceof ElementValueComparator)) return false; ElementValueComparator evc = (ElementValueComparator) comp.getValue1(); if (!(evc.getValueComparator() instanceof Order)) return false; JanusGraphTransaction tx = JanusGraphTraversalUtil.getTx(rootTraversal.asAdmin()); String key = evc.getPropertyKey(); PropertyKey pkey = tx.getPropertyKey(key); if (pkey == null || !(Comparable.class.isAssignableFrom(pkey.dataType()))) return false; if (isVertexOrder && pkey.cardinality() != Cardinality.SINGLE) return false; } return true; } static void foldInIds(final HasStepFolder janusgraphStep, final Traversal.Admin<?, ?> traversal) { Step<?, ?> currentStep = janusgraphStep.getNextStep(); while (true) { if (currentStep instanceof HasContainerHolder) { boolean removeStep = false; for (final HasContainer hasContainer : ((HasContainerHolder) currentStep).getHasContainers()) { if (GraphStep.processHasContainerIds((GraphStep) janusgraphStep, hasContainer)) { currentStep.getLabels().forEach(janusgraphStep::addLabel); removeStep = true; } } if (removeStep) traversal.removeStep(currentStep); } else if (currentStep instanceof IdentityStep) { // do nothing, has no impact } else if (currentStep instanceof NoOpBarrierStep) { // do nothing, has no impact } else { break; } currentStep = currentStep.getNextStep(); } } static void foldInHasContainer(final HasStepFolder janusgraphStep, final Traversal.Admin<?, ?> traversal) { Step<?, ?> currentStep = janusgraphStep.getNextStep(); while (true) { if (currentStep instanceof HasContainerHolder) { Iterable<HasContainer> containers = ((HasContainerHolder) currentStep).getHasContainers(); if (validJanusGraphHas(containers)) { janusgraphStep.addAll(containers); currentStep.getLabels().forEach(janusgraphStep::addLabel); traversal.removeStep(currentStep); } } else if (currentStep instanceof IdentityStep) { // do nothing, has no impact } else if (currentStep instanceof NoOpBarrierStep) { // do nothing, has no impact } else { break; } currentStep = currentStep.getNextStep(); } } // public static boolean addLabeledStepAsIdentity(Step<?,?> currentStep, final Traversal.Admin<?, ?> traversal) { // if (currentStep.getLabel().isPresent()) { // final IdentityStep identityStep = new IdentityStep<>(traversal); // identityStep.setLabel(currentStep.getLabel().get()); // TraversalHelper.insertAfterStep(identityStep, currentStep, traversal); // return true; // } else return false; // } static void foldInOrder(final HasStepFolder janusgraphStep, final Traversal.Admin<?, ?> traversal, final Traversal<?, ?> rootTraversal, boolean isVertexOrder) { Step<?, ?> currentStep = janusgraphStep.getNextStep(); OrderGlobalStep<?, ?> lastOrder = null; while (true) { if (currentStep instanceof OrderGlobalStep) { if (lastOrder != null) { //Previous orders are rendered irrelevant by next order (since re-ordered) lastOrder.getLabels().forEach(janusgraphStep::addLabel); traversal.removeStep(lastOrder); } lastOrder = (OrderGlobalStep) currentStep; } else if (currentStep instanceof IdentityStep) { // do nothing, can be skipped } else if (currentStep instanceof HasStep) { // do nothing, can be skipped } else if (currentStep instanceof NoOpBarrierStep) { // do nothing, can be skipped } else { break; } currentStep = currentStep.getNextStep(); } if (lastOrder != null && lastOrder instanceof OrderGlobalStep) { if (validJanusGraphOrder(lastOrder, rootTraversal, isVertexOrder)) { //Add orders to HasStepFolder for (Pair<Traversal.Admin<Object, Comparable>, Comparator<Comparable>> comp : (List<Pair<Traversal.Admin<Object, Comparable>, Comparator<Comparable>>>) ((OrderGlobalStep) lastOrder).getComparators()) { ElementValueComparator evc = (ElementValueComparator) comp.getValue1(); janusgraphStep.orderBy(evc.getPropertyKey(), (Order) evc.getValueComparator()); } lastOrder.getLabels().forEach(janusgraphStep::addLabel); traversal.removeStep(lastOrder); } } } static void splitAndP(final List<HasContainer> hasContainers, final Iterable<HasContainer> has) { has.forEach(hasContainer -> { if (hasContainer.getPredicate() instanceof AndP) { for (final P<?> predicate : ((AndP<?>) hasContainer.getPredicate()).getPredicates()) { hasContainers.add(new HasContainer(hasContainer.getKey(), predicate)); } } else hasContainers.add(hasContainer); }); } class OrderEntry { public final String key; public final Order order; public OrderEntry(String key, Order order) { this.key = key; this.order = order; } } static <E extends Ranging> void foldInRange(final HasStepFolder janusgraphStep, final Traversal.Admin<?, ?> traversal) { Step<?, ?> nextStep = JanusGraphTraversalUtil.getNextNonIdentityStep(janusgraphStep); if (nextStep instanceof RangeGlobalStep) { RangeGlobalStep range = (RangeGlobalStep) nextStep; int limit = QueryUtil.convertLimit(range.getHighRange()); janusgraphStep.setLimit(QueryUtil.mergeLimits(limit, janusgraphStep.getLimit())); if (range.getLowRange() == 0) { //Range can be removed since there is no offset nextStep.getLabels().forEach(janusgraphStep::addLabel); traversal.removeStep(nextStep); } } } }