前面我们大概看了tinkerpop 的代码怎么一步步变成 Traversal 和 Step,然后怎么调用和执行。我们忽略了 strategy 的相关操作。

官方介绍

在查看源码之前我们尽量对她进行了解熟悉。TraversalStrategy 分析 Traversal,如果 Traversal 符合其标准尺度,则可以相应地进行改变。 strategies 在编译期执行,是 Gremlin traversal machine 编译器的基础,分为五种类型: 1. 可以直接嵌入 traversal 逻辑的 application-level 的功能(decoration); 2. 能够更高效通过 TinkerPop3 语法表达 traversal (optimization); 3. 能够更高效在 graph 层面表达traversal(provider optimization); 4. 在执行 traversal 之前 一些必须的最终适配、清理、分析 (finalization); 5. 对于当前的程序或者存储系统来说,一些不合法的操作(verification)。

以 IdentityRemovalStrategy 为例:

public final class IdentityRemovalStrategy extends AbstractTraversalStrategy<TraversalStrategy.OptimizationStrategy> implements TraversalStrategy.OptimizationStrategy {

    private static final IdentityRemovalStrategy INSTANCE = new IdentityRemovalStrategy();

    private IdentityRemovalStrategy() {
    }

    @Override
    public void apply(Traversal.Admin<?, ?> traversal) {
        if (traversal.getSteps().size() <= 1)
            return;

        for (IdentityStep<?> identityStep : TraversalHelper.getStepsOfClass(IdentityStep.class, traversal)) {
            if (identityStep.getLabels().isEmpty() || !(identityStep.getPreviousStep() instanceof EmptyStep)) {
                TraversalHelper.copyLabels(identityStep, identityStep.getPreviousStep(), false);
                traversal.removeStep(identityStep);
            }
        }
    }

    public static IdentityRemovalStrategy instance() {
        return INSTANCE;
    }
}

可以看出继承自 AbstractTraversalStrategy, 和 TraversalStrategy.OptimizationStrategy,并有一个 apply(Traversal.Admin<?, ?> traversal) 方法。 这里是直接在 traversal 中找到 identityStep 并移除。

applyStrategies

strategies 初始化的时候就放入graph 中,我们这里看看如何嵌入到查询语句。

在我们调用 traversal.next() 之前,有个步骤就是 applyStrategies。在执行 traversal 之前大概是这样的:

this = {DefaultGraphTraversal@1359} "[GraphStep(vertex,[1]), VertexStep(OUT,[knows],edge), EdgeVertexStep(IN), PropertiesStep([name],value)]"
 lastTraverser = {EmptyTraverser@1384} 
 finalEndStep = {EmptyStep@1385} 
 stepPosition = {StepPosition@1362} "4.0.0()"
 graph = {TinkerGraph@1386} "tinkergraph[vertices:6 edges:6]"
 steps = {ArrayList@1387}  size = 4
 unmodifiableSteps = {Collections$UnmodifiableRandomAccessList@1388}  size = 4
 parent = {EmptyStep@1385} 
 sideEffects = {DefaultTraversalSideEffects@1389} "sideEffects[size:0]"
 strategies = {DefaultTraversalStrategies@1361} "strategies[ConnectiveStrategy, IncidentToAdjacentStrategy, MatchPredicateStrategy, FilterRankingStrategy, InlineFilterStrategy, AdjacentToIncidentStrategy, RepeatUnrollStrategy, PathRetractionStrategy, CountStrategy, LazyBarrierStrategy, TinkerGraphCountStrategy, TinkerGraphStepStrategy, ProfileStrategy, StandardVerificationStrategy]"
 generator = null
 requirements = null
 locked = false
 bytecode = {Bytecode@1390} "[[], [V(1), outE(knows), inV(), values(name)]]"

可以看出是 DefaultGraphTraversal 的实体类,lastTraverser finalEndStep parent sideEffects 都是刚初始化,steps unmodifiableSteps 是4个。 然后我们执行完这个方法再来看看:

this = {DefaultGraphTraversal@1359} "[TinkerGraphStep(vertex,[1]), VertexStep(OUT,[knows],vertex), PropertiesStep([name],value)]"
 lastTraverser = {EmptyTraverser@1384} 
 finalEndStep = {PropertiesStep@1432} "PropertiesStep([name],value)"
 stepPosition = {StepPosition@1362} "6.0.0()"
 graph = {TinkerGraph@1386} "tinkergraph[vertices:6 edges:6]"
 steps = {ArrayList@1387}  size = 3
 unmodifiableSteps = {Collections$UnmodifiableRandomAccessList@1388}  size = 3
 parent = {EmptyStep@1385} 
 sideEffects = {DefaultTraversalSideEffects@1389} "sideEffects[size:0]"
 strategies = {DefaultTraversalStrategies@1361} "strategies[ConnectiveStrategy, IncidentToAdjacentStrategy, MatchPredicateStrategy, FilterRankingStrategy, InlineFilterStrategy, AdjacentToIncidentStrategy, RepeatUnrollStrategy, PathRetractionStrategy, CountStrategy, LazyBarrierStrategy, TinkerGraphCountStrategy, TinkerGraphStepStrategy, ProfileStrategy, StandardVerificationStrategy]"
 generator = null
 requirements = {Collections$UnmodifiableSet@1431}  size = 1
 locked = true
 bytecode = {Bytecode@1390} "[[], [V(1), outE(knows), inV(), values(name)]]"

finalEndStep 变了,stepPosition 变了,steps unmodifiableSteps 都少了一步,requirements 多了一个。然后我们还是具体看看方法执行步骤:

public void applyStrategies() throws IllegalStateException {
    if (this.locked) throw Traversal.Exceptions.traversalIsLocked(); // 判断是否执行过。
    TraversalHelper.reIdSteps(this.stepPosition, this);
    this.strategies.applyStrategies(this); // 这一步循环调用 strategy 的 apply 方法。
    {
        for (final TraversalStrategy<?> traversalStrategy : this.traversalStrategies) {
            traversalStrategy.apply(traversal);
        }
    }
    boolean hasGraph = null != this.graph;
    
    // 判断 TraversalParent 的所有 globalChild 也要进行 applyStrategies
    for (int i = 0, j = this.steps.size(); i < j; i++) { // "foreach" can lead to ConcurrentModificationExceptions
        final Step step = this.steps.get(i);
        if (step instanceof TraversalParent) {
            for (final Traversal.Admin<?, ?> globalChild : ((TraversalParent) step).getGlobalChildren()) {
                globalChild.setStrategies(this.strategies);
                globalChild.setSideEffects(this.sideEffects);
                if (hasGraph) globalChild.setGraph(this.graph);
                globalChild.applyStrategies();
            }
            for (final Traversal.Admin<?, ?> localChild : ((TraversalParent) step).getLocalChildren()) {
                localChild.setStrategies(this.strategies);
                localChild.setSideEffects(this.sideEffects);
                if (hasGraph) localChild.setGraph(this.graph);
                localChild.applyStrategies();
            }
        }
    }
    // 得到 finalEndStep 
    this.finalEndStep = this.getEndStep();
    // finalize requirements
    if (this.getParent() instanceof EmptyStep) {
        this.requirements = null;
        this.getTraverserRequirements();
    }
    this.locked = true;
}

整段代码并不复杂,复杂的是每个 traversalStrategy 具体都做了什么。

我们知道 tinkerpop 是一个独立的项目,里面是不带任何和 janus 相关的内容,如何会有 JanusGraphStep 呢, 其实在很多地方(例如 fill:175, Traversal (org.apache.tinkerpop.gremlin.process.traversal))会调用 this.asAdmin().applyStrategies(); 会循环调用 traversalStrategy.apply(traversal)。在 apply 方法中得到了所有的 GraphStep。然后 new JanusGraphStep ,并且替换掉原有的 step。