白乔原创:理解Neo4j的Cypher执行引擎

org.neo4j.cypher.internal.ExecutionEngine

其中execute()

      val preParsedQuery = preParser.preParseQuery(query, profile)
      val executableQuery = getOrCompile(context, preParsedQuery, queryTracer, params)
      if (preParsedQuery.executionMode.name != "explain") {
        checkParameters(executableQuery.paramNames, params, executableQuery.extractedParams)
      }
      val combinedParams = params.updatedWith(executableQuery.extractedParams)
      context.executingQuery().compilationCompleted(executableQuery.compilerInfo, supplier(executableQuery.planDescription()))
      executableQuery.execute(context, preParsedQuery, combinedParams)

preParsedQuery–>executableQuery
白乔原创:理解Neo4j的Cypher执行引擎_第1张图片
举个例子,查询语句

match (n)-[dad]->(m) where m.age>35 return n.name

翻译成executableQuery
白乔原创:理解Neo4j的Cypher执行引擎_第2张图片
executableQuery.logicalPlan为:

ProduceResult(Vector(n.name)) {
  LHS -> Projection(Map(n.name -> Property(Variable(n),PropertyKeyName(name)))) {
    LHS -> Expand(m, INCOMING, List(), n, dad, ExpandAll) {
      LHS -> Selection(Ands(Set(GreaterThan(Property(Variable(m),PropertyKeyName(age)),Parameter(  AUTOINT0,Integer))))) {
        LHS -> AllNodesScan(m, Set()) {}
      }
    }
  }
}

其中的Property是org.neo4j.cypher.internal.v3_5.expressions.Property,定义如下:

case class Property(map: Expression, propertyKey: PropertyKeyName)(val position: InputPosition) extends LogicalProperty {
  override def asCanonicalStringVal = s"${map.asCanonicalStringVal}.${propertyKey.asCanonicalStringVal}"
}

PropertyKeyName定义如下:

case class PropertyKeyName(name: String)(val position: InputPosition) extends SymbolicName

executableQuery.executionPlan.resultBuilderFactory.pipe为:

ProduceResultsPipe(ProjectionPipe(ExpandAllPipe(FilterPipe(AllNodesScanPipe(m),m.age > {  AUTOINT0}),m,dad,n,INCOMING,org.neo4j.cypher.internal.runtime.interpreted.pipes.LazyTypes@1c395774),InterpretedCommandProjection(Map(n.name -> n.name))),Vector(n.name))

其中的FilterPipe内容如下:
白乔原创:理解Neo4j的Cypher执行引擎_第3张图片
可以看出,其中每个操作被包装为org.neo4j.cypher.internal.runtime.interpreted.pipes.Pipe,每个Pipe子类必须实现如下方法:

  def createResults(state: QueryState) : Iterator[ExecutionContext]

如FilterPipe,它的实现如下:

  protected def internalCreateResults(input: Iterator[ExecutionContext], state: QueryState): Iterator[ExecutionContext] =
    input.filter(ctx => predicate(ctx, state) eq Values.TRUE)

再如AllNodesScanPipe,它的实现如下:

  protected def internalCreateResults(state: QueryState): Iterator[ExecutionContext] = {
   val baseContext = state.newExecutionContext(executionContextFactory)
   state.query.nodeOps.all.map(n => executionContextFactory.copyWith(baseContext, ident, n))
 }

org.neo4j.cypher.internal.compatibility.v3_5.runtime.PipeExecutionPlanBuilder.build()负责将logicalPlan翻译成Pipe:

class PipeExecutionPlanBuilder(pipeBuilderFactory: PipeBuilderFactory,
                               expressionConverters: ExpressionConverters) {
  def build(plan: LogicalPlan)
           (implicit context: PipeExecutionBuilderContext, tokenContext: TokenContext): Pipe = {

    buildPipe(plan)
  }

  private def buildPipe(plan: LogicalPlan)(implicit context: PipeExecutionBuilderContext, tokenContext: TokenContext): Pipe = {
    val pipeBuilder = pipeBuilderFactory(recurse = p => buildPipe(p),
                                         readOnly = context.readOnly,
                                         expressionConverters = expressionConverters)
    LogicalPlans.map(plan, pipeBuilder)
  }
}

LogicalPlans.map()的遍历顺序如下:

  /**
    * Traverses the logical plan tree structure and maps the tree in a bottom but fashion.
    *
    * Given a logical plan such as:
    *
    *         a
    *        / \
    *       b   c
    *      /   / \
    *     d   e   f
    *
    * the mapper will be called in the following sequence:
    *
    *   F = mapLeaf(f)
    *   E = mapLeaf(e)
    *   C = mapTwoChildPlan(c, E, F)
    *   D = mapLeaf(d)
    *   B = mapOneChildPlan(b, D)
    *   A = mapTwoChildPlan(a, B, C)
    */

org.neo4j.cypher.internal.runtime.interpreted.InterpretedPipeBuilder提供了mapXXX(),负责将logicalPlan节点翻译成Pipe,例如:

  def onOneChildPlan(plan: LogicalPlan, source: Pipe): Pipe = {
    plan match {
      case AllNodesScan(ident, _) =>
        AllNodesScanPipe(ident)(id = id)

      case Selection(predicate, _) =>
        val predicateExpression =
          if (predicate.exprs.size == 1) buildExpression(predicate.exprs.head) else buildExpression(predicate)
        FilterPipe(source, predicateExpression)(id = id)
    ...
  }

也可以使用explain命令来查看执行计划,需要结合neo4j-browser:
假设具有如下数据:

create (m:man {name: 'bluejoe', age: 40}), (n:man {name: 'alan', age: 39}), (x:kid {name: 'alex', age: 10}), (x)-[:dad]->(m), (m)-[:brother]->(n)

执行如下查询:

explain match (m:man)-[dad]->(x:kid)-[brother]-(n) where m.age<18 and n.age>30 return n.name, m.name, x

neo4j-browser将会输出:
白乔原创:理解Neo4j的Cypher执行引擎_第4张图片

你可能感兴趣的:(源码故事)