Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

2.1 Label scan support for new planner [DONT MERGE, Waiting for fix on master] #2096

Merged
merged 2 commits into from

2 participants

@boggle
Collaborator

No description provided.

boggle added some commits
@boggle boggle Introduce Selections to the QueryGraph and SQGBuilder
o SQGB: For now only works with HasLabels() in MATCH
o Renamed ProjectionPipe to ProjectionNewPipe
o QueryGraph: Projections are now a Map[String, Expression]
35c58ba
@boggle boggle Prototype simple label scan support for new planner 0bb92ac
@jakub- jakub- merged commit f813add into from
@boggle boggle deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 5, 2014
  1. @boggle

    Introduce Selections to the QueryGraph and SQGBuilder

    boggle authored Jakub Wieczorek committed
    o SQGB: For now only works with HasLabels() in MATCH
    o Renamed ProjectionPipe to ProjectionNewPipe
    o QueryGraph: Projections are now a Map[String, Expression]
  2. @boggle

    Prototype simple label scan support for new planner

    boggle authored Jakub Wieczorek committed
This page is out of date. Refresh to see the latest.
Showing with 257 additions and 74 deletions.
  1. +2 −1  ...her/cypher-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/pipes/AllNodesScanPipe.scala
  2. +46 −0 ...r/cypher-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/pipes/LabelNodesScanPipe.scala
  3. +2 −2 ...n/scala/org/neo4j/cypher/internal/compiler/v2_1/pipes/{ProjectionPipe.scala → ProjectionNewPipe.scala}
  4. +1 −1  ...pher-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/CardinalityEstimator.scala
  5. +4 −1 ...ity/cypher/cypher-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/Planner.scala
  6. +17 −1 .../cypher/cypher-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/QueryGraph.scala
  7. +15 −8 ...r-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/SimpleQueryGraphBuilder.scala
  8. +6 −8 ...1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/execution/SimpleExecutionPlanBuilder.scala
  9. +31 −0 ...er-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/LabelNodesScan.scala
  10. +2 −2 ...cypher-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/Projection.scala
  11. +5 −5 ...compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/ProjectionPlanner.scala
  12. +21 −9 ...piler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/SimpleLogicalPlanner.scala
  13. +27 −10 ...mpiler-2.1/src/test/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/SimpleQueryGraphBuilderTest.scala
  14. +23 −7 ...c/test/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/execution/SimpleExecutionPlanBuilderTest.scala
  15. +9 −9 ...iler-2.1/src/test/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/ProjectionPlannerTest.scala
  16. +46 −10 ...r-2.1/src/test/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/SimpleLogicalPlannerTest.scala
View
3  .../cypher-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/pipes/AllNodesScanPipe.scala
@@ -23,11 +23,12 @@ import org.neo4j.cypher.internal.compiler.v2_1.{PlanDescriptionImpl, symbols, Ex
import symbols._
case class AllNodesScanPipe(id: String) extends Pipe {
+
override protected def internalCreateResults(state: QueryState): Iterator[ExecutionContext] = {
state.query.nodeOps.all.map(n => ExecutionContext.from(id -> n))
}
- override def exists(pred: (Pipe) => Boolean): Boolean = pred(this)
+ override def exists(predicate: Pipe => Boolean): Boolean = predicate(this)
override def executionPlanDescription = new PlanDescriptionImpl(this, "AllNodesScan", Seq.empty, Seq("identifier" -> id))
View
46 ...ypher-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/pipes/LabelNodesScanPipe.scala
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2002-2014 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.neo4j.cypher.internal.compiler.v2_1.pipes
+
+import org.neo4j.cypher.internal.compiler.v2_1.{LabelId, symbols, PlanDescription, ExecutionContext}
+import symbols._
+
+case class LabelNodesScanPipe(id: String, label: Either[String, LabelId]) extends Pipe {
+
+ override protected def internalCreateResults(state: QueryState): Iterator[ExecutionContext] = {
+ val optLabelId = label match {
+ case Left(str) => state.query.getOptLabelId(str).map(LabelId)
+ case Right(labelId) => Some(labelId)
+ }
+
+ optLabelId match {
+ case Some(labelId) =>
+ state.query.getNodesByLabel(labelId.id).map(n => ExecutionContext.from(id -> n))
+ case None =>
+ Iterator.empty
+ }
+ }
+
+ override def exists(predicate: Pipe => Boolean): Boolean = predicate(this)
+
+ override def executionPlanDescription: PlanDescription = ???
+
+ override def symbols: SymbolTable = new SymbolTable(Map(id -> CTNode))
+}
View
4 ...internal/compiler/v2_1/pipes/ProjectionPipe.scala → ...ernal/compiler/v2_1/pipes/ProjectionNewPipe.scala
@@ -24,7 +24,7 @@ import org.neo4j.cypher.internal.compiler.v2_1.symbols.SymbolTable
import org.neo4j.cypher.internal.compiler.v2_1.ExecutionContext
import org.neo4j.cypher.internal.compiler.v2_1.data.SimpleVal
-case class ProjectionPipe(source: Pipe, expressions: Map[String, Expression]) extends PipeWithSource(source) {
+case class ProjectionNewPipe(source: Pipe, expressions: Map[String, Expression]) extends PipeWithSource(source) {
val symbols: SymbolTable = {
val newIdentifiers = expressions.map {
case (name, expression) => name -> expression.getType(source.symbols)
@@ -50,4 +50,4 @@ case class ProjectionPipe(source: Pipe, expressions: Map[String, Expression]) ex
.andThen(this, "Projection",
"symKeys" -> SimpleVal.fromIterable(source.symbols.keys),
"exprKeys" -> SimpleVal.fromIterable(expressions.keys))
-}
+}
View
2  ...r-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/CardinalityEstimator.scala
@@ -27,7 +27,7 @@ This class is responsible for answering questions about cardinality. It does thi
information is available, or guessing when that's not possible.
*/
trait CardinalityEstimator {
- def estimateLabelScan(labelId: LabelId): Int
+ def estimateLabelScan(labelId: Option[LabelId]): Int
def estimateAllNodes(): Int
View
5 .../cypher/cypher-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/Planner.scala
@@ -32,7 +32,10 @@ case class Planner() {
val estimator = new CardinalityEstimator {
def estimateExpandRelationship(labelIds: Seq[LabelId], relationshipType: Seq[RelTypeId], dir: Direction) = 20
- def estimateLabelScan(labelId: LabelId) = 100
+ def estimateLabelScan(labelId: Option[LabelId]) = labelId match {
+ case Some(id) => 100
+ case None => 0
+ }
def estimateAllNodes() = 1000
}
View
18 ...pher/cypher-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/QueryGraph.scala
@@ -21,9 +21,25 @@ package org.neo4j.cypher.internal.compiler.v2_1.planner
import org.neo4j.cypher.internal.compiler.v2_1.ast
import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.Id
+import org.neo4j.cypher.internal.compiler.v2_1.ast.{LabelName, Identifier, HasLabels, Where}
/*
An abstract representation of the query graph being solved at the current step
*/
-case class QueryGraph(projection: Seq[(String, ast.Expression)], identifiers: Set[Id])
+case class QueryGraph(projections: Map[String, ast.Expression],
+ selections: Selections,
+ identifiers: Set[Id])
+object SelectionPredicates {
+ def fromWhere(where: Where): Seq[(Set[Id], ast.Expression)] = where.expression match {
+ case expr@HasLabels(identifier@Identifier(name), labels) =>
+ labels.map( (label: LabelName) => Set(Id(name)) -> HasLabels(identifier, Seq(label))(expr.position) )
+ case _ =>
+ throw new CantHandleQueryException
+ }
+}
+
+case class Selections(predicates: Seq[(Set[Id], ast.Expression)] = Seq.empty) {
+ def apply(availableIds: Set[Id]): Seq[ast.Expression] =
+ predicates.collect { case (k, v) if k.subsetOf(availableIds) => v }
+}
View
23 ...ompiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/SimpleQueryGraphBuilder.scala
@@ -25,23 +25,30 @@ import org.neo4j.cypher.internal.compiler.v2_1.ast.convert.ExpressionConverters.
class SimpleQueryGraphBuilder extends QueryGraphBuilder {
override def produce(ast: Query): QueryGraph = {
- val (projection, identifiers: Set[Id]) = ast match {
+ val (projections: Seq[(String, Expression)], selections, identifiers: Set[Id]) = ast match {
case Query(None, SingleQuery(Seq(Return(false, ListedReturnItems(expressions), None, None, None)))) =>
- (expressions.map(e => e.name -> e.expression), Set.empty)
+ val projections: Seq[(String, Expression)] = expressions.map(e => e.name -> e.expression)
+ val selections = Selections()
+ val identifiers = Set.empty
+ (projections, selections, identifiers)
case Query(None, SingleQuery(Seq(
- Match(false, Pattern(Seq(EveryPath(NodePattern(Some(Identifier(s)), Seq(), None, _)))), Seq(), None),
+ Match(false, Pattern(Seq(EveryPath(NodePattern(Some(Identifier(s)), Seq(), None, _)))), Seq(), optWhere),
Return(false, ListedReturnItems(expressions), None, None, None)
))) =>
- (expressions.map(e => e.name -> e.expression), Set(Id(s)))
+ val projections: Seq[(String, Expression)] = expressions.map(e => e.name -> e.expression)
+ val selections = Selections(optWhere.map(SelectionPredicates.fromWhere).getOrElse(Seq.empty))
+ val identifiers = Set(Id(s))
+ (projections, selections, identifiers)
- case _ => throw new CantHandleQueryException
+ case _ =>
+ throw new CantHandleQueryException
}
- if (projection.exists {
+ if (projections.exists {
case (_,e) => e.asCommandExpression.containsAggregate
}) throw new CantHandleQueryException
- QueryGraph(projection, identifiers)
+ QueryGraph(projections.toMap, selections, identifiers)
}
-}
+}
View
14 ...rc/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/execution/SimpleExecutionPlanBuilder.scala
@@ -40,9 +40,10 @@ class SimpleExecutionPlanBuilder extends ExecutionPlanBuilder {
val right = plan.rhs.map(buildPipe)
plan match {
- case Projection(_, expressions) => ProjectionPipe(left.get, toLegacyExpressions(expressions))
- case SingleRow() => NullPipe()
- case AllNodesScan(Id(id), _) => AllNodesScanPipe(id)
+ case Projection(_, expressions) => ProjectionNewPipe(left.get, toLegacyExpressions(expressions))
+ case SingleRow() => NullPipe()
+ case AllNodesScan(Id(id), _) => AllNodesScanPipe(id)
+ case LabelNodesScan(Id(id), label, _) => LabelNodesScanPipe(id, label)
}
}
@@ -51,8 +52,5 @@ class SimpleExecutionPlanBuilder extends ExecutionPlanBuilder {
PipeInfo(topLevelPipe, updating, None)
}
- def toLegacyExpressions(expressions: Seq[(String, Expression)]): Map[String, legacy.Expression] =
- expressions.map {
- case (k: String, v: Expression) => (k, v.asCommandExpression)
- }.toMap
-}
+ def toLegacyExpressions(expressions: Map[String, Expression]) = expressions.mapValues(_.asCommandExpression)
+}
View
31 ...compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/LabelNodesScan.scala
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2002-2014 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.neo4j.cypher.internal.compiler.v2_1.planner.logical
+
+import org.neo4j.cypher.internal.compiler.v2_1.LabelId
+
+case class LabelNodesScan(id: Id, label: Either[String, LabelId], cardinality: Int) extends LogicalPlan {
+ def coveredIds: Set[Id] = Set(id)
+
+ def cost: Int = ???
+
+ def rhs: Option[LogicalPlan] = None
+ def lhs: Option[LogicalPlan] = None
+}
View
4 ...her-compiler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/Projection.scala
@@ -21,7 +21,7 @@ package org.neo4j.cypher.internal.compiler.v2_1.planner.logical
import org.neo4j.cypher.internal.compiler.v2_1.ast.Expression
-case class Projection(left: LogicalPlan, expressions: Seq[(String, Expression)]) extends LogicalPlan {
+case class Projection(left: LogicalPlan, expressions: Map[String, Expression]) extends LogicalPlan {
def coveredIds: Set[Id] = left.coveredIds
def cardinality: Int = left.cardinality
@@ -31,4 +31,4 @@ case class Projection(left: LogicalPlan, expressions: Seq[(String, Expression)])
def rhs = None
def lhs: Option[LogicalPlan] = Some(left)
-}
+}
View
10 ...piler-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/ProjectionPlanner.scala
@@ -24,14 +24,14 @@ import org.neo4j.cypher.internal.compiler.v2_1.ast.{Expression, Identifier}
class ProjectionPlanner {
def amendPlan(in: QueryGraph, plan: LogicalPlan): LogicalPlan = {
- val ids: Seq[(String, Expression)] = plan.coveredIds.toSeq.map {
+ val ids: Map[String, Expression] = plan.coveredIds.map {
case Id(id) => id -> Identifier(id)(null)
- }
+ }.toMap
- if (ids != in.projection)
- Projection(plan, in.projection)
+ if (ids != in.projections)
+ Projection(plan, in.projections)
else
plan
}
-}
+}
View
30 ...er-2.1/src/main/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/SimpleLogicalPlanner.scala
@@ -20,22 +20,34 @@
package org.neo4j.cypher.internal.compiler.v2_1.planner.logical
import org.neo4j.cypher.internal.compiler.v2_1.planner.{CantHandleQueryException, CardinalityEstimator, QueryGraph}
+import org.neo4j.cypher.internal.compiler.v2_1.ast.{Identifier, HasLabels}
case class SimpleLogicalPlanner(estimator: CardinalityEstimator) extends LogicalPlanner {
val projectionPlanner = new ProjectionPlanner
- override def plan(in: QueryGraph): LogicalPlan = {
- var planTable: Map[Set[Id], LogicalPlan] = Map.empty
- in.identifiers.foreach {
- id =>
- planTable = planTable + (Set(id) -> AllNodesScan(id, estimator.estimateAllNodes()))
- }
+ override def plan(qg: QueryGraph): LogicalPlan = {
+ val planTableBuilder = Map.newBuilder[Set[Id], LogicalPlan]
+ qg.identifiers.foreach( planTableBuilder += identifierSource(_, qg) )
+ val planTable = planTableBuilder.result()
+
while (planTable.size > 1) {
throw new CantHandleQueryException
}
- val input = if (planTable.size == 0) SingleRow() else planTable.values.head
- projectionPlanner.amendPlan(in, input)
+ val logicalPlan = if (planTable.size == 0) SingleRow() else planTable.values.head
+
+ projectionPlanner.amendPlan(qg, logicalPlan)
+ }
+
+ def identifierSource(id: Id, qg: QueryGraph) = {
+ val idSet = Set(id)
+ val predicates = qg.selections.apply(idSet)
+ val source = predicates.collectFirst({
+ case HasLabels(Identifier(id.name), label :: Nil) =>
+ val labelId = label.id
+ LabelNodesScan(id, labelId.toRight(label.name), estimator.estimateLabelScan(labelId))
+ }).getOrElse(AllNodesScan(id, estimator.estimateAllNodes()))
+ idSet -> source
}
-}
+}
View
37 ...ler-2.1/src/test/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/SimpleQueryGraphBuilderTest.scala
@@ -17,8 +17,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.neo4j.cypher.internal.compiler.v2_1.planner
-
+import org.neo4j.cypher.internal.compiler.v2_1.planner.{Selections, SimpleQueryGraphBuilder}
+import org.neo4j.cypher.internal.compiler.v2_1.{InputPosition, DummyPosition}
import org.neo4j.cypher.internal.commons.CypherFunSuite
import org.neo4j.cypher.internal.compiler.v2_1.ast._
import org.neo4j.cypher.internal.compiler.v2_1.parser.CypherParser
@@ -28,21 +28,22 @@ import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.Id
class SimpleQueryGraphBuilderTest extends CypherFunSuite {
val parser = new CypherParser()
+ val pos = DummyPosition(0)
test("projection only query") {
val ast = parse("RETURN 42")
val builder = new SimpleQueryGraphBuilder
val qg = builder.produce(ast)
- qg.projection should equal(Seq("42" -> SignedIntegerLiteral("42")(DummyPosition(0))))
+ qg.projections should equal(Map("42" -> SignedIntegerLiteral("42")(pos)))
}
test("multiple projection query") {
val ast = parse("RETURN 42, 'foo'")
val builder = new SimpleQueryGraphBuilder
val qg = builder.produce(ast)
- qg.projection should equal(Seq(
- "42" -> SignedIntegerLiteral("42")(DummyPosition(0)),
- "'foo'" -> StringLiteral("foo")(DummyPosition(0))
+ qg.projections should equal(Map(
+ "42" -> SignedIntegerLiteral("42")(pos),
+ "'foo'" -> StringLiteral("foo")(pos)
))
}
@@ -51,14 +52,30 @@ class SimpleQueryGraphBuilderTest extends CypherFunSuite {
val builder = new SimpleQueryGraphBuilder
val qg = builder.produce(ast)
- qg.projection should equal(Seq(
- "n" -> Identifier("n")(DummyPosition(0))
+ qg.projections should equal(Map(
+ "n" -> Identifier("n")(pos)
))
qg.identifiers should equal(Set(Id("n")))
}
+ test("match n where n:Awesome return n") {
+ val ast = parse("MATCH n WHERE n:Awesome:Foo RETURN n")
+ val builder = new SimpleQueryGraphBuilder
+ val qg = builder.produce(ast)
+
+ qg.projections should equal(Map(
+ "n" -> Identifier("n")(pos)
+ ))
+
+ qg.selections should equal(Selections(Seq(
+ Set(Id("n")) -> HasLabels(Identifier("n")(pos), Seq(LabelName("Awesome")()(pos)))(pos),
+ Set(Id("n")) -> HasLabels(Identifier("n")(pos), Seq(LabelName("Foo")()(pos)))(pos)
+ )))
+
+ qg.identifiers should equal(Set(Id("n")))
+ }
+
def parse(s: String): Query =
parser.parse(s).asInstanceOf[Query]
-
-}
+}
View
30 ...est/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/execution/SimpleExecutionPlanBuilderTest.scala
@@ -20,23 +20,30 @@
package org.neo4j.cypher.internal.compiler.v2_1.planner.execution
import org.neo4j.cypher.internal.commons.CypherFunSuite
-import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.{Id, AllNodesScan, SingleRow, Projection}
-import org.neo4j.cypher.internal.compiler.v2_1.ast.SignedIntegerLiteral
-import org.neo4j.cypher.internal.compiler.v2_1.DummyPosition
-import org.neo4j.cypher.internal.compiler.v2_1.pipes.{ProjectionPipe, AllNodesScanPipe, NullPipe}
+import org.neo4j.cypher.internal.compiler.v2_1.{LabelId, DummyPosition}
+import org.neo4j.cypher.internal.compiler.v2_1.pipes._
import org.neo4j.cypher.internal.compiler.v2_1.commands.{expressions => legacy}
+import org.neo4j.cypher.internal.compiler.v2_1.pipes.NullPipe
+import org.neo4j.cypher.internal.compiler.v2_1.pipes.AllNodesScanPipe
+import org.neo4j.cypher.internal.compiler.v2_1.pipes.ProjectionNewPipe
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.SingleRow
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.Id
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.LabelNodesScan
+import org.neo4j.cypher.internal.compiler.v2_1.ast.SignedIntegerLiteral
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.AllNodesScan
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.Projection
class SimpleExecutionPlanBuilderTest extends CypherFunSuite {
val planner = new SimpleExecutionPlanBuilder
test("projection only query") {
- val logicalPlan = Projection(SingleRow(), Seq("42" -> SignedIntegerLiteral("42")(DummyPosition(0))))
+ val logicalPlan = Projection(SingleRow(), Map("42" -> SignedIntegerLiteral("42")(DummyPosition(0))))
val pipeInfo = planner.build(logicalPlan)
pipeInfo should not be 'updating
pipeInfo.periodicCommit should equal(None)
- pipeInfo.pipe should equal(ProjectionPipe(NullPipe(), Map("42" -> legacy.Literal(42))))
+ pipeInfo.pipe should equal(ProjectionNewPipe(NullPipe(), Map("42" -> legacy.Literal(42))))
}
test("simple pattern query") {
@@ -47,4 +54,13 @@ class SimpleExecutionPlanBuilderTest extends CypherFunSuite {
pipeInfo.periodicCommit should equal(None)
pipeInfo.pipe should equal(AllNodesScanPipe("n"))
}
-}
+
+ test("simple label scan query") {
+ val logicalPlan = LabelNodesScan(Id("n"), Right(LabelId(12)), 1000)
+ val pipeInfo = planner.build(logicalPlan)
+
+ pipeInfo should not be 'updating
+ pipeInfo.periodicCommit should equal(None)
+ pipeInfo.pipe should equal(LabelNodesScanPipe("n", Right(LabelId(12))))
+ }
+}
View
18 ...r-2.1/src/test/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/ProjectionPlannerTest.scala
@@ -20,7 +20,7 @@
package org.neo4j.cypher.internal.compiler.v2_1.planner.logical
import org.neo4j.cypher.internal.commons.CypherFunSuite
-import org.neo4j.cypher.internal.compiler.v2_1.planner.QueryGraph
+import org.neo4j.cypher.internal.compiler.v2_1.planner.{Selections, QueryGraph}
import org.neo4j.cypher.internal.compiler.v2_1.ast.{Identifier, SignedIntegerLiteral}
import org.neo4j.cypher.internal.compiler.v2_1.DummyPosition
import org.mockito.Mockito._
@@ -28,9 +28,9 @@ import org.scalatest.mock.MockitoSugar
class ProjectionPlannerTest extends CypherFunSuite with MockitoSugar {
test("should add projection for expressions not already covered") {
- val input = FakePlan(Set(Id("n")))
- val projections = Seq("42" -> SignedIntegerLiteral("42")(DummyPosition(0)))
- val qg = QueryGraph(projections, identifiers = Set.empty)
+ val input = fakePlan(Set(Id("n")))
+ val projections = Map("42" -> SignedIntegerLiteral("42")(DummyPosition(0)))
+ val qg = QueryGraph(projections, Selections(), identifiers = Set.empty)
val planner = new ProjectionPlanner
val result = planner.amendPlan(qg, input)
@@ -39,9 +39,9 @@ class ProjectionPlannerTest extends CypherFunSuite with MockitoSugar {
}
test("does not add projection when not needed") {
- val input = FakePlan(Set(Id("n")))
- val projections = Seq("n" -> Identifier("n")(DummyPosition(0)))
- val qg = QueryGraph(projections, identifiers = Set.empty)
+ val input = fakePlan(Set(Id("n")))
+ val projections = Map("n" -> Identifier("n")(DummyPosition(0)))
+ val qg = QueryGraph(projections, Selections(), identifiers = Set.empty)
val planner = new ProjectionPlanner
val result = planner.amendPlan(qg, input)
@@ -49,7 +49,7 @@ class ProjectionPlannerTest extends CypherFunSuite with MockitoSugar {
result should equal(input)
}
- def FakePlan(coveredIds: Set[Id]): LogicalPlan = {
+ def fakePlan(coveredIds: Set[Id]): LogicalPlan = {
val plan = mock[LogicalPlan]
when(plan.coveredIds).thenReturn(coveredIds)
plan
@@ -66,4 +66,4 @@ object FakePlan {
def rhs: Option[LogicalPlan] = ???
def lhs: Option[LogicalPlan] = ???
-}
+}
View
56 ....1/src/test/scala/org/neo4j/cypher/internal/compiler/v2_1/planner/logical/SimpleLogicalPlannerTest.scala
@@ -17,12 +17,18 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.neo4j.cypher.internal.compiler.v2_1.planner.logical
-
import org.neo4j.cypher.internal.commons.CypherFunSuite
-import org.neo4j.cypher.internal.compiler.v2_1.planner.{CardinalityEstimator, QueryGraph}
-import org.neo4j.cypher.internal.compiler.v2_1.ast.{Identifier, SignedIntegerLiteral}
-import org.neo4j.cypher.internal.compiler.v2_1.DummyPosition
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical._
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.AllNodesScan
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.Id
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.Projection
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.SimpleLogicalPlanner
+import org.neo4j.cypher.internal.compiler.v2_1.planner._
+import org.neo4j.cypher.internal.compiler.v2_1.ast.{LabelName, HasLabels, Identifier, SignedIntegerLiteral}
+import org.neo4j.cypher.internal.compiler.v2_1.planner.logical.SingleRow
+import org.neo4j.cypher.internal.compiler.v2_1.planner.QueryGraph
+import org.neo4j.cypher.internal.compiler.v2_1.planner.Selections
+import org.neo4j.cypher.internal.compiler.v2_1.{LabelId, DummyPosition}
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._
@@ -30,11 +36,12 @@ class SimpleLogicalPlannerTest extends CypherFunSuite with MockitoSugar {
val estimator = mock[CardinalityEstimator]
val planner = new SimpleLogicalPlanner(estimator)
+ val pos = DummyPosition(0)
test("projection only query") {
// given
- val expressions = Seq("42" -> SignedIntegerLiteral("42")(DummyPosition(0)))
- val qg = QueryGraph(expressions, Set.empty)
+ val expressions = Map("42" -> SignedIntegerLiteral("42")(pos))
+ val qg = QueryGraph(expressions, Selections(), Set.empty)
// when
val resultPlan = planner.plan(qg)
@@ -45,8 +52,8 @@ class SimpleLogicalPlannerTest extends CypherFunSuite with MockitoSugar {
test("simple pattern query") {
// given
- val expressions = Seq("n" -> Identifier("n")(DummyPosition(0)))
- val qg = QueryGraph(expressions, Set(Id("n")))
+ val expressions = Map("n" -> Identifier("n")(pos))
+ val qg = QueryGraph(expressions, Selections(), Set(Id("n")))
// when
when(estimator.estimateAllNodes()).thenReturn(1000)
@@ -55,4 +62,33 @@ class SimpleLogicalPlannerTest extends CypherFunSuite with MockitoSugar {
// then
resultPlan should equal(AllNodesScan(Id("n"), 1000))
}
-}
+
+ test("simple label scan without compile-time label ID") {
+ // given
+ val expressions = Map("n" -> Identifier("n")(pos))
+ val hasLabels = HasLabels(Identifier("n")(pos), Seq(LabelName("Awesome")()(pos)))(pos)
+ val qg = QueryGraph(expressions, Selections(Seq(Set(Id("n")) -> hasLabels)), Set(Id("n")))
+
+ // when
+ when(estimator.estimateLabelScan(None)).thenReturn(0)
+ val resultPlan = planner.plan(qg)
+
+ // then
+ resultPlan should equal(LabelNodesScan(Id("n"), Left("Awesome"), 0))
+ }
+
+ test("simple label scan with a compile-time label ID") {
+ // given
+ val expressions = Map("n" -> Identifier("n")(pos))
+ val labelId = LabelId(12)
+ val hasLabels = HasLabels(Identifier("n")(pos), Seq(LabelName("Awesome")(Some(labelId))(pos)))(pos)
+ val qg = QueryGraph(expressions, Selections(Seq(Set(Id("n")) -> hasLabels)), Set(Id("n")))
+
+ // when
+ when(estimator.estimateLabelScan(Some(labelId))).thenReturn(100)
+ val resultPlan = planner.plan(qg)
+
+ // then
+ resultPlan should equal(LabelNodesScan(Id("n"), Right(labelId), 100))
+ }
+}
Something went wrong with that request. Please try again.