@Override
    public void apply(final Traversal.Admin<?, ?> traversal) {
        // this strategy is only applied to the root (recursively) because it uses a tiny cache which will drastically
        // speed up the ranking function in the event of encountering traversals with significant depth and branching.
        // if we let normal strategy application apply then the cache will reset since this strategy and the traversal
        // does not hold any state about that cache. tried using the marker pattern used in other strategies but that
        // didn't work so well.
        if (traversal.isRoot()) {
            // TraversalParent steps require a costly function to calculate if labels are in use in their child
            // traversals. This little cache keeps the effective data of that function which is if there is a
            // lambda in the children and the set of scope keys. note that the lambda sorta trumps the labels in
            // that if there is a lambda there's no real point to doing any sort of eval of the labels.
            //
            // this cache holds the parent and a pair. the first item in the pair is a boolean which is true if
            // lambda is present and false otherwise. the second item in the pair is a set of labels from any
            // Scoping steps
            final Map<Step, Integer> stepRanking = new HashMap<>();

            // gather the parents and their Scoping/LambdaHolder steps to build up the cache. since the traversal is
            // processed in depth first manner, the entries gathered to m are deepest child first and held in order,
            // so that the cache can be constructed with parent's knowing their children were processed first
            // Note: We should avoid using streams with groupingBy collectors for this (e.g., with code of the form
            // TraversalHelper.getStepsOfAssignableClassRecursivelyFromDepth(traversal, TraversalParent.class).stream().
            // collect(Collectors.groupingBy(step -> ((Step) step).getTraversal().getParent(), LinkedHashMap::new, Collectors.toList())) )
            // since it would mean that we do hash computation multiple times for the traversal parent (which can be a
            // complex step with many children).
            // Also note that IdentityHashMap is not an option for above Collectors.groupingBy call since it would not
            // retain the required order (deepest traversal parents first).
            final List<Pair<TraversalParent, List<Step<?,?>>>> traversalParentsStepsCollection =
                    collectStepsOfAssignableClassRecursivelyFromDepthGroupedByParent(traversal);

            // create cache
            final Map<TraversalParent, Pair<Boolean, Set<String>>> traversalParentCache =
                    getTraversalParentCache(traversalParentsStepsCollection);

            TraversalHelper.applyTraversalRecursively(t -> {
                boolean modified = true;
                while (modified) {
                    modified = false;
                    final List<Step> steps = t.getSteps();
                    for (int i = 0; i < steps.size() - 1; i++) {
                        final Step<?, ?> step = steps.get(i);
                        final Set<String> labels = step.getLabels();
                        final Step<?, ?> nextStep = step.getNextStep();
                        if (!usesLabels(nextStep, labels, traversalParentCache)) {
                            final int nextRank = stepRanking.computeIfAbsent(nextStep, FilterRankingStrategy::getStepRank);
                            if (nextRank != 0) {
                                if (!step.getLabels().isEmpty()) {
                                    TraversalHelper.copyLabels(step, nextStep, true);
                                    modified = true;
                                }
                                if (stepRanking.computeIfAbsent(step, FilterRankingStrategy::getStepRank) > nextRank) {
                                    t.removeStep(nextStep);
                                    t.addStep(i, nextStep);
                                    modified = true;
                                }
                            }
                        }
                    }
                }
            }, traversal);
        }
    }
{@code FilterRankingStrategy} reorders filter- and order-steps according to their rank. Step ranks are defined within
the strategy and indicate when it is reasonable for a step to move in front of another. It will also do its best to
push step labels as far "right" as possible in order to keep traversers as small and bulkable as possible prior to
the absolute need for path-labeling.

@author Daniel Kuppitz (http://gremlin.guru)
@author Marko A. Rodriguez (http://markorodriguez.com)
@example <pre>
__.order().dedup()                        // is replaced by __.dedup().order()
__.dedup().filter(out()).has("value", 0)  // is replaced by __.has("value", 0).filter(out()).dedup()
</pre>
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/FilterRankingStrategy.java