diff --git a/community/cypher/cypher-compiler-2.2/src/main/scala/org/neo4j/cypher/internal/compiler/v2_2/NameId.scala b/community/cypher/cypher-compiler-2.2/src/main/scala/org/neo4j/cypher/internal/compiler/v2_2/NameId.scala
index 23c05a8c8ba16..c03f6ce7a9a93 100644
--- a/community/cypher/cypher-compiler-2.2/src/main/scala/org/neo4j/cypher/internal/compiler/v2_2/NameId.scala
+++ b/community/cypher/cypher-compiler-2.2/src/main/scala/org/neo4j/cypher/internal/compiler/v2_2/NameId.scala
@@ -26,3 +26,10 @@ sealed trait NameId {
final case class LabelId(id: Int) extends NameId
final case class RelTypeId(id: Int) extends NameId
final case class PropertyKeyId(id: Int) extends NameId
+
+object NameId {
+ val WILDCARD: Int = -1
+
+ implicit def toKernelEncode(nameId: NameId): Int = nameId.id
+ implicit def toKernelEncode(nameId: Option[NameId]): Int = nameId.map(toKernelEncode).getOrElse(WILDCARD)
+}
diff --git a/community/cypher/cypher-compiler-2.2/src/main/scala/org/neo4j/cypher/internal/compiler/v2_2/spi/GraphStatistics.scala b/community/cypher/cypher-compiler-2.2/src/main/scala/org/neo4j/cypher/internal/compiler/v2_2/spi/GraphStatistics.scala
index 3247f126ecf82..2ba4bf3b12913 100644
--- a/community/cypher/cypher-compiler-2.2/src/main/scala/org/neo4j/cypher/internal/compiler/v2_2/spi/GraphStatistics.scala
+++ b/community/cypher/cypher-compiler-2.2/src/main/scala/org/neo4j/cypher/internal/compiler/v2_2/spi/GraphStatistics.scala
@@ -22,6 +22,14 @@ package org.neo4j.cypher.internal.compiler.v2_2.spi
import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.{Cardinality, Selectivity}
import org.neo4j.cypher.internal.compiler.v2_2.{LabelId, PropertyKeyId, RelTypeId}
+object GraphStatistics {
+ val DEFAULT_RANGE_SELECTIVITY = Selectivity(0.3)
+ val DEFAULT_PREDICATE_SELECTIVITY = Selectivity(0.75)
+ val DEFAULT_EQUALITY_SELECTIVITY = Selectivity(0.1)
+ val DEFAULT_NUMBER_OF_ID_LOOKUPS = Cardinality(25)
+ val DEFAULT_REL_UNIQUENESS_SELECTIVITY = Selectivity(1.0 - 1 / 100 /*rel-cardinality*/)
+}
+
trait GraphStatistics {
def nodesWithLabelCardinality(labelId: Option[LabelId]): Cardinality
@@ -35,10 +43,35 @@ trait GraphStatistics {
def indexSelectivity(label: LabelId, property: PropertyKeyId): Option[Selectivity]
}
-object GraphStatistics {
- val DEFAULT_RANGE_SELECTIVITY = Selectivity(0.3)
- val DEFAULT_PREDICATE_SELECTIVITY = Selectivity(0.75)
- val DEFAULT_EQUALITY_SELECTIVITY = Selectivity(0.1)
- val DEFAULT_NUMBER_OF_ID_LOOKUPS = Cardinality(25)
- val DEFAULT_REL_UNIQUENESS_SELECTIVITY = Selectivity(1.0 - 1 / 100 /*rel-cardinality*/)
+class DelegatingGraphStatistics(delegate: GraphStatistics) extends GraphStatistics {
+ override def nodesWithLabelCardinality(labelId: Option[LabelId]): Cardinality =
+ delegate.nodesWithLabelCardinality(labelId)
+
+ override def cardinalityByLabelsAndRelationshipType(fromLabel: Option[LabelId], relTypeId: Option[RelTypeId], toLabel: Option[LabelId]): Cardinality =
+ delegate.cardinalityByLabelsAndRelationshipType(fromLabel, relTypeId, toLabel)
+
+ /*
+ Probability of any node with the given label, to have a property with a given value
+
+ indexSelectivity(:X, prop) = s => |MATCH (a:X)| * s = |MATCH (a:X) WHERE x.prop = *|
+ */
+ override def indexSelectivity(label: LabelId, property: PropertyKeyId): Option[Selectivity] =
+ delegate.indexSelectivity(label, property)
+}
+
+class StatisticsCompletingGraphStatistics(delegate: GraphStatistics)
+ extends DelegatingGraphStatistics(delegate) {
+
+ override def cardinalityByLabelsAndRelationshipType(fromLabel: Option[LabelId], relTypeId: Option[RelTypeId], toLabel: Option[LabelId]): Cardinality =
+ (fromLabel, toLabel) match {
+ case (Some(_), Some(_)) =>
+ // TODO: read real counts from readOperations when they are gonna be properly computed and updated
+ Cardinality.min(
+ super.cardinalityByLabelsAndRelationshipType(fromLabel, relTypeId, None),
+ super.cardinalityByLabelsAndRelationshipType(None, relTypeId, toLabel)
+ )
+ case _ =>
+ super.cardinalityByLabelsAndRelationshipType(fromLabel, relTypeId, toLabel)
+ }
}
+
diff --git a/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/DbStructureGraphStatistics.scala b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/DbStructureGraphStatistics.scala
new file mode 100644
index 0000000000000..b4dc38762f366
--- /dev/null
+++ b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/DbStructureGraphStatistics.scala
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2002-2015 "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 .
+ */
+package org.neo4j.cypher.internal.compiler.v2_2.planner
+
+import org.neo4j.cypher.internal.compiler.v2_2.{NameId, PropertyKeyId, RelTypeId, LabelId}
+import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.{Selectivity, Cardinality}
+import org.neo4j.cypher.internal.compiler.v2_2.spi.GraphStatistics
+import org.neo4j.kernel.impl.util.dbstructure.DbStructureLookup
+
+class DbStructureGraphStatistics(lookup: DbStructureLookup) extends GraphStatistics {
+
+ import NameId._
+
+ override def nodesWithLabelCardinality( label: Option[LabelId] ): Cardinality =
+ Cardinality(lookup.nodesWithLabelCardinality(label))
+
+ override def cardinalityByLabelsAndRelationshipType( fromLabel: Option[LabelId], relTypeId: Option[RelTypeId], toLabel: Option[LabelId] ): Cardinality =
+ Cardinality(lookup.cardinalityByLabelsAndRelationshipType(fromLabel, relTypeId, toLabel))
+
+ /*
+ Probability of any node with the given label, to have a property with a given value
+
+ indexSelectivity(:X, prop) = s => |MATCH (a:X)| * s = |MATCH (a:X) WHERE x.prop = *|
+ */
+ override def indexSelectivity( label: LabelId, property: PropertyKeyId ): Option[Selectivity] = {
+ val result = lookup.indexSelectivity( label.id, property.id )
+ if (result.isNaN) None else Some(Selectivity(result))
+ }
+}
diff --git a/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/DbStructureLogicalPlanningConfiguration.scala b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/DbStructureLogicalPlanningConfiguration.scala
new file mode 100644
index 0000000000000..f0e3dd6003af5
--- /dev/null
+++ b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/DbStructureLogicalPlanningConfiguration.scala
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2002-2015 "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 .
+ */
+package org.neo4j.cypher.internal.compiler.v2_2.planner
+
+import java.util
+
+import org.neo4j.cypher.internal.compiler.v2_2.spi.{GraphStatistics, StatisticsCompletingGraphStatistics}
+import org.neo4j.cypher.internal.compiler.v2_2.{LabelId, PropertyKeyId, RelTypeId}
+import org.neo4j.helpers.Pair
+import org.neo4j.helpers.collection.Visitable
+import org.neo4j.kernel.impl.util.dbstructure.{DbStructureCollector, DbStructureLookup, DbStructureVisitor}
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable
+
+object DbStructureLogicalPlanningConfiguration {
+
+ def apply(visitable: Visitable[DbStructureVisitor]): LogicalPlanningConfiguration = {
+ val collector = new DbStructureCollector
+ visitable.accept(collector)
+ val lookup = collector.lookup()
+ apply(lookup, new DbStructureGraphStatistics(lookup))
+ }
+
+ def apply(lookup: DbStructureLookup, underlyingStatistics: GraphStatistics): LogicalPlanningConfiguration = {
+ val resolvedLabels: mutable.Map[String, LabelId] = resolveTokens(lookup.labels())(LabelId)
+ val resolvedPropertyKeys = resolveTokens(lookup.properties())(PropertyKeyId)
+ val resolvedRelTypeNames = resolveTokens(lookup.relationshipTypes())(RelTypeId)
+
+ new RealLogicalPlanningConfiguration {
+
+ override val computeSemanticTable: SemanticTable = new SemanticTable(
+ resolvedLabelIds = resolvedLabels,
+ resolvedPropertyKeyNames = resolvedPropertyKeys,
+ resolvedRelTypeNames = resolvedRelTypeNames
+ )
+
+ override val graphStatistics: GraphStatistics =
+ new StatisticsCompletingGraphStatistics(underlyingStatistics)
+
+ override val indexes: Set[(String, String)] = indexSet(lookup.knownIndices())
+ override val knownLabels: Set[String] = resolvedLabels.keys.toSet
+ override val uniqueIndexes: Set[(String, String)] = indexSet(lookup.knownUniqueIndices())
+ }
+ }
+
+ private def indexSet(indices: util.Iterator[Pair[String, String]]): Set[(String, String)] =
+ indices.asScala.map { pair => pair.first() -> pair.other() }.toSet
+
+ private def resolveTokens[T](iterator: util.Iterator[Pair[Integer, String]])(f: Int => T): mutable.Map[String, T] = {
+ val builder = mutable.Map.newBuilder[String, T]
+ while (iterator.hasNext) {
+ val next = iterator.next()
+ builder += next.other() -> f(next.first())
+ }
+ builder.result()
+ }
+}
diff --git a/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/LogicalPlanningConfiguration.scala b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/LogicalPlanningConfiguration.scala
index dfed0a84e1399..e47ef603ddd0c 100644
--- a/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/LogicalPlanningConfiguration.scala
+++ b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/LogicalPlanningConfiguration.scala
@@ -19,18 +19,11 @@
*/
package org.neo4j.cypher.internal.compiler.v2_2.planner
-import org.neo4j.cypher.internal.compiler.v2_2.{RelTypeId, PropertyKeyId, LabelId, HardcodedGraphStatistics}
-import org.neo4j.cypher.internal.compiler.v2_2.ast.Expression
+import org.neo4j.cypher.internal.compiler.v2_2._
import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.Metrics._
-import org.neo4j.cypher.internal.compiler.v2_2.planner.logical._
-import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.plans.{NodeByLabelScan, LogicalPlan}
+import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.plans.LogicalPlan
+import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.{Cost, _}
import org.neo4j.cypher.internal.compiler.v2_2.spi.GraphStatistics
-import org.neo4j.helpers.collection.Visitable
-import org.neo4j.kernel.api.constraints.UniquenessConstraint
-import org.neo4j.kernel.api.index.IndexDescriptor
-import org.neo4j.kernel.impl.util.dbstructure.DbStructureVisitor
-
-import scala.collection.mutable
trait LogicalPlanningConfiguration {
def computeSemanticTable: SemanticTable
@@ -68,72 +61,3 @@ trait LogicalPlanningConfigurationAdHocSemanticTable {
table
}
}
-case class RealLogicalPlanningConfiguration()
- extends LogicalPlanningConfiguration with LogicalPlanningConfigurationAdHocSemanticTable {
-
- def cardinalityModel(queryGraphCardinalityModel: QueryGraphCardinalityModel, semanticTable: SemanticTable) = {
- val model: Metrics.CardinalityModel = new StatisticsBackedCardinalityModel(queryGraphCardinalityModel)
- ({ case (plan: LogicalPlan, card: QueryGraphCardinalityInput) => model(plan, card) })
- }
-
- def costModel(cardinality: CardinalityModel): PartialFunction[LogicalPlan, Cost] = {
- val model: Metrics.CostModel = new CardinalityCostModel(cardinality)
- ({ case (plan: LogicalPlan) => model(plan, QueryGraphCardinalityInput.empty) })
- }
-
- def graphStatistics: GraphStatistics = HardcodedGraphStatistics
-
- def indexes = Set.empty
- def uniqueIndexes = Set.empty
- def labelCardinality = Map.empty
- def knownLabels = Set.empty
-
- def qg: QueryGraph = ???
-}
-
-class StubbedLogicalPlanningConfiguration(parent: LogicalPlanningConfiguration)
- extends LogicalPlanningConfiguration with LogicalPlanningConfigurationAdHocSemanticTable {
-
- self =>
-
- var knownLabels: Set[String] = Set.empty
- var cardinality: PartialFunction[LogicalPlan, Cardinality] = PartialFunction.empty
- var cost: PartialFunction[LogicalPlan, Cost] = PartialFunction.empty
- var selectivity: PartialFunction[Expression, Selectivity] = PartialFunction.empty
- var labelCardinality: Map[String, Cardinality] = Map.empty
- var statistics = null
- var qg: QueryGraph = null
-
- var indexes: Set[(String, String)] = Set.empty
- var uniqueIndexes: Set[(String, String)] = Set.empty
- def indexOn(label: String, property: String) {
- indexes = indexes + (label -> property)
- }
- def uniqueIndexOn(label: String, property: String) {
- uniqueIndexes = uniqueIndexes + (label -> property)
- }
-
- def costModel(cardinality: Metrics.CardinalityModel) =
- cost.orElse(parent.costModel(cardinality))
-
- def cardinalityModel(queryGraphCardinalityModel: QueryGraphCardinalityModel, semanticTable: SemanticTable): Metrics.CardinalityModel = {
- val labelIdCardinality: Map[LabelId, Cardinality] = labelCardinality.map {
- case (name: String, cardinality: Cardinality) =>
- semanticTable.resolvedLabelIds(name) -> cardinality
- }
- val labelScanCardinality: PartialFunction[LogicalPlan, Cardinality] = {
- case NodeByLabelScan(_, label, _) if label.id(semanticTable).isDefined &&
- labelIdCardinality.contains(label.id(semanticTable).get) =>
- labelIdCardinality(label.id(semanticTable).get)
- }
-
- val r: PartialFunction[LogicalPlan, Cardinality] =
- labelScanCardinality.orElse(cardinality)
-
- (p: LogicalPlan, c: QueryGraphCardinalityInput) =>
- if(r.isDefinedAt(p)) r.apply(p) else parent.cardinalityModel(queryGraphCardinalityModel, semanticTable)(p, c)
- }
-
- def graphStatistics: GraphStatistics =
- Option(statistics).getOrElse(parent.graphStatistics)
-}
diff --git a/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/LogicalPlanningTestSupport2.scala b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/LogicalPlanningTestSupport2.scala
index 75bda335fa0c8..a98e5c6d68ec9 100644
--- a/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/LogicalPlanningTestSupport2.scala
+++ b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/LogicalPlanningTestSupport2.scala
@@ -91,6 +91,16 @@ trait LogicalPlanningTestSupport2 extends CypherTestSupport with AstConstruction
else
None
+ def getUniquenessConstraint(labelName: String, propertyKey: String): Option[UniquenessConstraint] = {
+ if (config.uniqueIndexes((labelName, propertyKey)))
+ Some(new UniquenessConstraint(
+ semanticTable.resolvedLabelIds(labelName).id,
+ semanticTable.resolvedPropertyKeyNames(propertyKey).id
+ ))
+ else
+ None
+ }
+
def getIndexRule(labelName: String, propertyKey: String): Option[IndexDescriptor] =
if (config.indexes((labelName, propertyKey)))
Some(new IndexDescriptor(
@@ -110,7 +120,6 @@ trait LogicalPlanningTestSupport2 extends CypherTestSupport with AstConstruction
semanticTable.resolvedRelTypeNames.get(relType).map(_.id)
def checkNodeIndex(idxName: String): Unit = ???
- def getUniquenessConstraint(labelName: String, propertyKey: String): Option[UniquenessConstraint] = ???
def checkRelIndex(idxName: String): Unit = ???
def getOrCreateFromSchemaState[T](key: Any, f: => T): T = ???
def getRelTypeName(id: Int): String = ???
diff --git a/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/RealLogicalPlanningConfiguration.scala b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/RealLogicalPlanningConfiguration.scala
new file mode 100644
index 0000000000000..a5fd8fe22e891
--- /dev/null
+++ b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/RealLogicalPlanningConfiguration.scala
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2002-2015 "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 .
+ */
+package org.neo4j.cypher.internal.compiler.v2_2.planner
+
+import org.neo4j.cypher.internal.compiler.v2_2.HardcodedGraphStatistics
+import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.plans.LogicalPlan
+import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.{CardinalityCostModel, Cost, StatisticsBackedCardinalityModel, Metrics}
+import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.Metrics.{CardinalityModel, QueryGraphCardinalityInput, QueryGraphCardinalityModel}
+import org.neo4j.cypher.internal.compiler.v2_2.spi.GraphStatistics
+
+case class RealLogicalPlanningConfiguration()
+ extends LogicalPlanningConfiguration with LogicalPlanningConfigurationAdHocSemanticTable {
+
+ def cardinalityModel(queryGraphCardinalityModel: QueryGraphCardinalityModel, semanticTable: SemanticTable) = {
+ val model: Metrics.CardinalityModel = new StatisticsBackedCardinalityModel(queryGraphCardinalityModel)
+ ({
+ case (plan: LogicalPlan, card: QueryGraphCardinalityInput) => model(plan, card)
+ })
+ }
+
+ def costModel(cardinality: CardinalityModel): PartialFunction[LogicalPlan, Cost] = {
+ val model: Metrics.CostModel = new CardinalityCostModel(cardinality)
+ ({
+ case (plan: LogicalPlan) => model(plan, QueryGraphCardinalityInput.empty)
+ })
+ }
+
+ def graphStatistics: GraphStatistics = HardcodedGraphStatistics
+ def indexes = Set.empty
+ def uniqueIndexes = Set.empty
+ def labelCardinality = Map.empty
+ def knownLabels = Set.empty
+
+ def qg: QueryGraph = ???
+}
diff --git a/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/StubbedLogicalPlanningConfiguration.scala b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/StubbedLogicalPlanningConfiguration.scala
new file mode 100644
index 0000000000000..20df5a335d41e
--- /dev/null
+++ b/community/cypher/cypher-compiler-2.2/src/test/scala/org/neo4j/cypher/internal/compiler/v2_2/planner/StubbedLogicalPlanningConfiguration.scala
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2002-2015 "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 .
+ */
+package org.neo4j.cypher.internal.compiler.v2_2.planner
+
+import org.neo4j.cypher.internal.compiler.v2_2.LabelId
+import org.neo4j.cypher.internal.compiler.v2_2.ast.Expression
+import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.Metrics.{QueryGraphCardinalityInput, QueryGraphCardinalityModel}
+import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.{Metrics, Selectivity, Cost, Cardinality}
+import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.plans.{NodeByLabelScan, LogicalPlan}
+import org.neo4j.cypher.internal.compiler.v2_2.spi.GraphStatistics
+
+class StubbedLogicalPlanningConfiguration(parent: LogicalPlanningConfiguration)
+ extends LogicalPlanningConfiguration with LogicalPlanningConfigurationAdHocSemanticTable {
+
+ self =>
+
+ var knownLabels: Set[String] = Set.empty
+ var cardinality: PartialFunction[LogicalPlan, Cardinality] = PartialFunction.empty
+ var cost: PartialFunction[LogicalPlan, Cost] = PartialFunction.empty
+ var selectivity: PartialFunction[Expression, Selectivity] = PartialFunction.empty
+ var labelCardinality: Map[String, Cardinality] = Map.empty
+ var statistics = null
+ var qg: QueryGraph = null
+
+ var indexes: Set[(String, String)] = Set.empty
+ var uniqueIndexes: Set[(String, String)] = Set.empty
+
+ def indexOn(label: String, property: String) {
+ indexes = indexes + (label -> property)
+ }
+
+ def uniqueIndexOn(label: String, property: String) {
+ uniqueIndexes = uniqueIndexes + (label -> property)
+ }
+
+ def costModel(cardinality: Metrics.CardinalityModel) =
+ cost.orElse(parent.costModel(cardinality))
+
+ def cardinalityModel(queryGraphCardinalityModel: QueryGraphCardinalityModel, semanticTable: SemanticTable): Metrics.CardinalityModel = {
+ val labelIdCardinality: Map[LabelId, Cardinality] = labelCardinality.map {
+ case (name: String, cardinality: Cardinality) =>
+ semanticTable.resolvedLabelIds(name) -> cardinality
+ }
+ val labelScanCardinality: PartialFunction[LogicalPlan, Cardinality] = {
+ case NodeByLabelScan(_, label, _) if label.id(semanticTable).isDefined &&
+ labelIdCardinality.contains(label.id(semanticTable).get) =>
+ labelIdCardinality(label.id(semanticTable).get)
+ }
+
+ val r: PartialFunction[LogicalPlan, Cardinality] =
+ labelScanCardinality.orElse(cardinality)
+
+ (p: LogicalPlan, c: QueryGraphCardinalityInput) =>
+ if (r.isDefinedAt(p)) r.apply(p) else parent.cardinalityModel(queryGraphCardinalityModel, semanticTable)(p, c)
+ }
+
+ def graphStatistics: GraphStatistics =
+ Option(statistics).getOrElse(parent.graphStatistics)
+}
diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CompatibilityFor2_2.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CompatibilityFor2_2.scala
index db403c2ccb2ad..daee8cdf09d35 100644
--- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CompatibilityFor2_2.scala
+++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/CompatibilityFor2_2.scala
@@ -155,7 +155,7 @@ trait CompatibilityFor2_2 {
def isPeriodicCommit = inner.isPeriodicCommit
def isStale(lastTxId: () => Long, statement: Statement) =
- inner.isStale(lastTxId, new TransactionBoundGraphStatistics(statement))
+ inner.isStale(lastTxId, TransactionBoundGraphStatistics(statement))
}
}
diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_2/TransactionBoundGraphStatistics.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_2/TransactionBoundGraphStatistics.scala
index 6b9a197d6cbac..016d3b99c6deb 100644
--- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_2/TransactionBoundGraphStatistics.scala
+++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_2/TransactionBoundGraphStatistics.scala
@@ -20,53 +20,41 @@
package org.neo4j.cypher.internal.spi.v2_2
import org.neo4j.cypher.internal.compiler.v2_2.planner.logical.{Cardinality, Selectivity}
-import org.neo4j.cypher.internal.compiler.v2_2.spi.GraphStatistics
-import org.neo4j.cypher.internal.compiler.v2_2.{LabelId, NameId, PropertyKeyId, RelTypeId}
+import org.neo4j.cypher.internal.compiler.v2_2.spi.{GraphStatistics, StatisticsCompletingGraphStatistics}
+import org.neo4j.cypher.internal.compiler.v2_2.{LabelId, PropertyKeyId, RelTypeId}
import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException
import org.neo4j.kernel.api.index.IndexDescriptor
import org.neo4j.kernel.api.{Statement => KernelStatement}
-class TransactionBoundGraphStatistics(statement: KernelStatement) extends GraphStatistics {
- import org.neo4j.cypher.internal.spi.v2_2.TransactionBoundGraphStatistics.{WILDCARD, toKernelEncode}
-
- def indexSelectivity(label: LabelId, property: PropertyKeyId): Option[Selectivity] =
- try {
- val indexDescriptor = new IndexDescriptor( label, property )
- val labeledNodes = statement.readOperations().countsForNode( label ).toDouble
+object TransactionBoundGraphStatistics {
+ def apply(statement: KernelStatement) = new StatisticsCompletingGraphStatistics(new BaseTransactionBoundGraphStatistics(statement))
- // approximation of number of unique values / index size (between 0 and 1)
- val indexEntrySelectivity = statement.readOperations( ).indexUniqueValuesSelectivity( indexDescriptor )
- val frequencyOfNodesWithSameValue = 1.0 / indexEntrySelectivity
- val indexSelectivity = frequencyOfNodesWithSameValue / labeledNodes
+ private class BaseTransactionBoundGraphStatistics(statement: KernelStatement) extends GraphStatistics {
- Selectivity.of(indexSelectivity)
- }
- catch {
- case e: IndexNotFoundKernelException => None
- }
+ import org.neo4j.cypher.internal.compiler.v2_2.NameId._
- def nodesWithLabelCardinality(labelId: Option[LabelId]): Cardinality =
- statement.readOperations().countsForNode(labelId)
+ def indexSelectivity(label: LabelId, property: PropertyKeyId): Option[Selectivity] =
+ try {
+ val indexDescriptor = new IndexDescriptor( label, property )
+ val labeledNodes = statement.readOperations().countsForNode( label ).toDouble
- def cardinalityByLabelsAndRelationshipType(fromLabel: Option[LabelId], relTypeId: Option[RelTypeId], toLabel: Option[LabelId]): Cardinality =
- (fromLabel, toLabel) match {
- case (Some(_), Some(_)) =>
- // TODO: read real counts from readOperations when they are gonna be properly computed and updated
- Math.min(
- statement.readOperations().countsForRelationship(fromLabel, relTypeId, WILDCARD ),
- statement.readOperations().countsForRelationship(WILDCARD, relTypeId, toLabel)
- )
- case _ =>
- statement.readOperations().countsForRelationship(fromLabel, relTypeId, toLabel)
- }
-}
+ // approximation of number of unique values / index size (between 0 and 1)
+ val indexEntrySelectivity = statement.readOperations( ).indexUniqueValuesSelectivity( indexDescriptor )
+ val frequencyOfNodesWithSameValue = 1.0 / indexEntrySelectivity
+ val indexSelectivity = frequencyOfNodesWithSameValue / labeledNodes
-object TransactionBoundGraphStatistics {
- val WILDCARD: Int = -1
+ Selectivity.of(indexSelectivity)
+ }
+ catch {
+ case e: IndexNotFoundKernelException => None
+ }
- private implicit def toKernelEncode(nameId: NameId): Int =
- nameId.id
+ def nodesWithLabelCardinality(labelId: Option[LabelId]): Cardinality =
+ statement.readOperations().countsForNode(labelId)
- private implicit def toKernelEncode(nameId: Option[NameId]): Int =
- nameId.map(toKernelEncode).getOrElse(WILDCARD)
+ def cardinalityByLabelsAndRelationshipType(fromLabel: Option[LabelId], relTypeId: Option[RelTypeId], toLabel: Option[LabelId]): Cardinality =
+ statement.readOperations().countsForRelationship(fromLabel, relTypeId, toLabel)
+ }
}
+
+
diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_2/TransactionBoundPlanContext.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_2/TransactionBoundPlanContext.scala
index e3f78041be3f1..870b78d53b103 100644
--- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_2/TransactionBoundPlanContext.scala
+++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_2/TransactionBoundPlanContext.scala
@@ -87,7 +87,7 @@ class TransactionBoundPlanContext(statement: Statement, val gdb: GraphDatabaseSe
}
val statistics: GraphStatistics =
- InstrumentedGraphStatistics(new TransactionBoundGraphStatistics(statement), MutableGraphStatisticsSnapshot())
+ InstrumentedGraphStatistics(TransactionBoundGraphStatistics(statement), MutableGraphStatisticsSnapshot())
val txIdProvider: () => Long = gdb.asInstanceOf[GraphDatabaseAPI]
.getDependencyResolver
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureCollector.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureCollector.java
new file mode 100644
index 0000000000000..6ffb71b7db18d
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureCollector.java
@@ -0,0 +1,370 @@
+/**
+ * Copyright (c) 2002-2015 "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 .
+ */
+package org.neo4j.kernel.impl.util.dbstructure;
+
+import org.neo4j.function.Function;
+import org.neo4j.helpers.Pair;
+import org.neo4j.helpers.collection.Iterables;
+import org.neo4j.kernel.api.constraints.UniquenessConstraint;
+import org.neo4j.kernel.api.index.IndexDescriptor;
+
+import java.util.*;
+
+import static java.lang.String.format;
+
+public class DbStructureCollector implements DbStructureVisitor
+{
+ private final TokenMap labels = new TokenMap( "label" );
+ private final TokenMap propertyKeys = new TokenMap( "property key" );
+ private final TokenMap relationshipTypes = new TokenMap( "relationship types" );
+ private final IndexDescriptorMap regularIndices = new IndexDescriptorMap( "regular" );
+ private final IndexDescriptorMap uniqueIndices = new IndexDescriptorMap( "unique" );
+ private final Set uniquenessConstraint = new HashSet<>();
+ private final Map nodeCounts = new HashMap<>();
+ private final Map relCounts = new HashMap<>();
+ private long allNodesCount = -1l;
+
+ public DbStructureLookup lookup()
+ {
+ return new DbStructureLookup()
+ {
+ @Override
+ public Iterator> labels()
+ {
+ return labels.iterator();
+ }
+
+ @Override
+ public Iterator> properties()
+ {
+ return propertyKeys.iterator();
+ }
+
+ @Override
+ public Iterator> relationshipTypes()
+ {
+ return relationshipTypes.iterator();
+ }
+
+ @Override
+ public Iterator> knownIndices()
+ {
+ return regularIndices.iterator();
+ }
+
+ @Override
+ public Iterator> knownUniqueIndices()
+ {
+ return uniqueIndices.iterator();
+ }
+
+ @Override
+ public Iterator> knownUniqueConstraints()
+ {
+ return Iterables.map( new Function>()
+ {
+ @Override
+ public Pair apply( UniquenessConstraint uniquenessConstraint ) throws RuntimeException
+ {
+ String label = labels.byIdOrFail( uniquenessConstraint.label() );
+ String propertyKey = propertyKeys.byIdOrFail( uniquenessConstraint.propertyKeyId() );
+ return Pair.of( label, propertyKey );
+ }
+ }, uniquenessConstraint.iterator() );
+ }
+
+ @Override
+ public long nodesWithLabelCardinality( int labelId )
+ {
+ Long result = labelId == -1 ? allNodesCount : nodeCounts.get( labelId );
+ return result == null ? 0l : result;
+ }
+
+ @Override
+ public long cardinalityByLabelsAndRelationshipType( int fromLabelId, int relTypeId, int toLabelId )
+ {
+ RelSpecifier specifier = new RelSpecifier( fromLabelId, relTypeId, toLabelId );
+ Long result = relCounts.get( specifier );
+ return result == null ? 0l : result;
+ }
+
+ @Override
+ public double indexSelectivity( int labelId, int propertyKeyId )
+ {
+ Double result1 = regularIndices.getIndex( labelId, propertyKeyId );
+ Double result2 = result1 == null ? uniqueIndices.getIndex( labelId, propertyKeyId ) : result1;
+ return result2 == null ? Double.NaN : result2;
+ }
+ };
+ }
+
+ @Override
+ public void visitLabel( int labelId, String labelName )
+ {
+ labels.putToken( labelId, labelName );
+ }
+
+ @Override
+ public void visitPropertyKey( int propertyKeyId, String propertyKeyName )
+ {
+ propertyKeys.putToken( propertyKeyId, propertyKeyName );
+ }
+
+ @Override
+ public void visitRelationshipType( int relTypeId, String relTypeName )
+ {
+ relationshipTypes.putToken( relTypeId, relTypeName );
+ }
+
+ @Override
+ public void visitIndex( IndexDescriptor descriptor, String userDescription, double uniqueValuesPercentage )
+ {
+ regularIndices.putIndex( descriptor, userDescription, uniqueValuesPercentage );
+ }
+
+ @Override
+ public void visitUniqueIndex( IndexDescriptor descriptor, String userDescription, double uniqueValuesPercentage )
+ {
+ uniqueIndices.putIndex( descriptor, userDescription, uniqueValuesPercentage );
+ }
+
+ @Override
+ public void visitUniqueConstraint( UniquenessConstraint constraint, String userDescription )
+ {
+ if ( !uniquenessConstraint.add( constraint ) )
+ {
+ throw new IllegalArgumentException(
+ format( "Duplicate unique constraint %s for %s", constraint, userDescription )
+ );
+ }
+ }
+
+ @Override
+ public void visitAllNodesCount( long nodeCount )
+ {
+ if ( allNodesCount < 0 )
+ {
+ allNodesCount = nodeCount;
+ }
+ else
+ {
+ throw new IllegalStateException( "Already received node count" );
+ }
+ }
+
+ @Override
+ public void visitNodeCount( int labelId, String labelName, long nodeCount )
+ {
+ if ( nodeCounts.put( labelId, nodeCount ) != null )
+ {
+ throw new IllegalArgumentException(
+ format( "Duplicate node count %s for label with id % s", nodeCount, labelName )
+ );
+ }
+ }
+
+ @Override
+ public void visitRelCount( int startLabelId, int relTypeId, int endLabelId, String relCountQuery, long relCount )
+ {
+ RelSpecifier specifier = new RelSpecifier( startLabelId, relTypeId, endLabelId );
+
+ if ( relCounts.put( specifier, relCount ) != null )
+ {
+ throw new IllegalArgumentException(
+ format( "Duplicate rel count %s for relationship specifier %s (corresponding query: %s)", relCount,
+ specifier, relCountQuery )
+ );
+ }
+ }
+
+ private static class RelSpecifier
+ {
+ public final int fromLabelId;
+ public final int relTypeId;
+ public final int toLabelId;
+
+ public RelSpecifier( int fromLabelId, int relTypeId, int toLabelId )
+ {
+ this.fromLabelId = fromLabelId;
+ this.relTypeId = relTypeId;
+ this.toLabelId = toLabelId;
+ }
+
+ @Override
+ public String toString()
+ {
+ return format(
+ "RelSpecifier{fromLabelId=%d, relTypeId=%d, toLabelId=%d}", fromLabelId, relTypeId, toLabelId
+ );
+ }
+
+ @Override
+ public boolean equals( Object o )
+ {
+ if ( this == o )
+ {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ RelSpecifier that = (RelSpecifier) o;
+ return fromLabelId == that.fromLabelId && relTypeId == that.relTypeId && toLabelId == that.toLabelId;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = fromLabelId;
+ result = 31 * result + relTypeId;
+ result = 31 * result + toLabelId;
+ return result;
+ }
+ }
+
+ private class IndexDescriptorMap implements Iterable>
+ {
+ private final String indexType;
+ private final Map indexMap = new HashMap<>();
+
+ public IndexDescriptorMap( String indexType )
+ {
+ this.indexType = indexType;
+ }
+
+ public void putIndex( IndexDescriptor descriptor, String userDescription, double uniqueValuesPercentage )
+ {
+ if ( indexMap.containsKey( descriptor ) )
+ {
+ throw new IllegalArgumentException(
+ format( "Duplicate index descriptor %s for %s index %s", descriptor, indexType,
+ userDescription )
+ );
+ }
+
+ indexMap.put( descriptor, uniqueValuesPercentage );
+ }
+
+ public Double getIndex( int labelId, int propertyKeyId )
+ {
+ return indexMap.get( new IndexDescriptor( labelId, propertyKeyId ) );
+ }
+
+ public Iterator> iterator()
+ {
+ final Iterator iterator = indexMap.keySet().iterator();
+ return new Iterator>()
+ {
+ @Override
+ public boolean hasNext()
+ {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public Pair next()
+ {
+ IndexDescriptor next = iterator.next();
+ String label = labels.byIdOrFail( next.getLabelId() );
+ String propertyKey = propertyKeys.byIdOrFail( next.getPropertyKeyId() );
+ return Pair.of( label, propertyKey );
+ }
+
+ @Override
+ public void remove()
+ {
+ iterator.remove();
+ }
+ };
+ }
+ }
+
+ private static class TokenMap implements Iterable>
+ {
+ private final String tokenType;
+ private final Map forward = new HashMap<>();
+ private final Map backward = new HashMap<>();
+
+ public TokenMap( String tokenType )
+ {
+ this.tokenType = tokenType;
+ }
+
+ public String byIdOrFail( int token )
+ {
+ String result = forward.get( token );
+ if ( result == null )
+ {
+ throw new IllegalArgumentException( format( "Didn't find %s token with id %s", tokenType, token ) );
+ }
+ return result;
+ }
+
+ public void putToken( int token, String name )
+ {
+ if ( forward.containsKey( token ) )
+ {
+ throw new IllegalArgumentException(
+ format( "Duplicate id %s for name %s in %s token map", token, name, tokenType )
+ );
+ }
+
+
+ if ( backward.containsKey( name ) )
+ {
+ throw new IllegalArgumentException(
+ format( "Duplicate name %s for id %s in %s token map", name, token, tokenType )
+ );
+ }
+
+ forward.put( token, name );
+ backward.put( name, token );
+ }
+
+ @Override
+ public Iterator> iterator()
+ {
+ final Iterator> iterator = forward.entrySet().iterator();
+ return new Iterator>()
+ {
+ @Override
+ public boolean hasNext()
+ {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public Pair next()
+ {
+ Map.Entry next = iterator.next();
+ return Pair.of( next.getKey(), next.getValue() );
+ }
+
+ @Override
+ public void remove()
+ {
+ iterator.remove();
+ }
+ };
+ }
+ }
+}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureLookup.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureLookup.java
new file mode 100644
index 0000000000000..47a86b85d2966
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureLookup.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2002-2015 "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 .
+ */
+package org.neo4j.kernel.impl.util.dbstructure;
+
+import org.neo4j.helpers.Pair;
+
+import java.util.Iterator;
+
+public interface DbStructureLookup
+{
+ Iterator> labels();
+ Iterator> properties();
+ Iterator> relationshipTypes();
+
+ Iterator> knownIndices();
+ Iterator> knownUniqueIndices();
+ Iterator> knownUniqueConstraints();
+
+ long nodesWithLabelCardinality( int labelId );
+ long cardinalityByLabelsAndRelationshipType( int fromLabelId, int relTypeId, int toLabelId );
+ double indexSelectivity( int labelId, int propertyKeyId );
+}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureTool.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureTool.java
index 4d5243e5020e4..1d242c4f3f7de 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureTool.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureTool.java
@@ -99,7 +99,7 @@ private static void traceDb( String generator,
);
DbStructureVisitor visitor = tracer.newProxy();
- DbStructureGuide guide = new DbStructureGuide( graph );
+ GraphDbStructureGuide guide = new GraphDbStructureGuide( graph );
guide.accept( visitor );
tracer.close();
}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureGuide.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/GraphDbStructureGuide.java
similarity index 97%
rename from community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureGuide.java
rename to community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/GraphDbStructureGuide.java
index 957dc2afd3080..b1cdbfed0f768 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureGuide.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/util/dbstructure/GraphDbStructureGuide.java
@@ -45,17 +45,8 @@
import static org.neo4j.kernel.api.ReadOperations.ANY_LABEL;
import static org.neo4j.kernel.api.ReadOperations.ANY_RELATIONSHIP_TYPE;
-public class DbStructureGuide implements Visitable
+public class GraphDbStructureGuide implements Visitable
{
- private static Label WILDCARD_LABEL = new Label()
- {
- @Override
- public String name()
- {
- return "";
- }
- };
-
private static RelationshipType WILDCARD_REL_TYPE = new RelationshipType()
{
@Override
@@ -69,7 +60,7 @@ public String name()
private final ThreadToStatementContextBridge bridge;
private final GlobalGraphOperations glops;
- public DbStructureGuide( GraphDatabaseService graph )
+ public GraphDbStructureGuide( GraphDatabaseService graph )
{
this.db = (GraphDatabaseAPI) graph;
DependencyResolver dependencyResolver = db.getDependencyResolver();
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureCollectorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureCollectorTest.java
new file mode 100644
index 0000000000000..e19801fb599ad
--- /dev/null
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureCollectorTest.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2002-2015 "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 .
+ */
+package org.neo4j.kernel.impl.util.dbstructure;
+
+import org.junit.Test;
+
+import org.neo4j.kernel.api.constraints.UniquenessConstraint;
+import org.neo4j.kernel.api.index.IndexDescriptor;
+
+import static java.util.Arrays.asList;
+
+import static org.junit.Assert.assertEquals;
+
+import static org.neo4j.helpers.Pair.of;
+import static org.neo4j.helpers.collection.Iterables.toList;
+
+public class DbStructureCollectorTest
+{
+ @Test
+ public void collectsDbStructure()
+ {
+ // GIVEN
+ DbStructureCollector collector = new DbStructureCollector();
+ collector.visitLabel( 1, "Person" );
+ collector.visitLabel( 2, "City" );
+ collector.visitPropertyKey( 1, "name" );
+ collector.visitPropertyKey( 2, "income" );
+ collector.visitRelationshipType( 1, "LIVES_IN" );
+ collector.visitRelationshipType( 2, "FRIEND" );
+ collector.visitUniqueIndex( new IndexDescriptor( 1, 1 ), ":Person(name)", 1.0d );
+ collector.visitUniqueConstraint( new UniquenessConstraint( 2, 1 ), ":Person(name)" );
+ collector.visitIndex( new IndexDescriptor( 2, 2 ), ":City(income)", 0.2d );
+ collector.visitAllNodesCount( 50 );
+ collector.visitNodeCount( 1, "Person", 20 );
+ collector.visitNodeCount( 2, "City", 30 );
+ collector.visitRelCount( 1, 2, -1, "(:Person)-[:FRIEND]->()", 500 );
+
+ // WHEN
+ DbStructureLookup lookup = collector.lookup();
+
+ // THEN
+ assertEquals( asList( of( 1, "Person" ), of( 2, "City" ) ), toList( lookup.labels() ) );
+ assertEquals( asList( of( 1, "name" ), of( 2, "income" ) ), toList( lookup.properties() ) );
+ assertEquals( asList( of( 1, "LIVES_IN" ), of( 2, "FRIEND" ) ), toList( lookup.relationshipTypes() ) );
+
+ assertEquals( asList( of( "City", "name" ) ), toList( lookup.knownUniqueConstraints() ) );
+ assertEquals( asList( of( "Person", "name" ) ), toList( lookup.knownUniqueIndices() ) );
+ assertEquals( asList( of( "City", "income" ) ), toList( lookup.knownIndices() ) );
+
+ assertEquals( 50, lookup.nodesWithLabelCardinality( -1 ) );
+ assertEquals( 20, lookup.nodesWithLabelCardinality( 1 ) );
+ assertEquals( 30, lookup.nodesWithLabelCardinality( 2 ) );
+ assertEquals( 500, lookup.cardinalityByLabelsAndRelationshipType( 1, 2, -1 ) );
+ assertEquals( 1.0d, lookup.indexSelectivity( 1, 1 ), 0.01d );
+ assertEquals( 0.2d, lookup.indexSelectivity( 2, 2 ), 0.01d );
+ }
+}
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureGuideTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/util/dbstructure/GraphDbStructureGuideTest.java
similarity index 98%
rename from community/kernel/src/test/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureGuideTest.java
rename to community/kernel/src/test/java/org/neo4j/kernel/impl/util/dbstructure/GraphDbStructureGuideTest.java
index 5f365a275f0ea..a163f76e39b2b 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/util/dbstructure/DbStructureGuideTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/util/dbstructure/GraphDbStructureGuideTest.java
@@ -46,7 +46,7 @@
import static org.neo4j.kernel.api.ReadOperations.ANY_LABEL;
import static org.neo4j.kernel.api.ReadOperations.ANY_RELATIONSHIP_TYPE;
-public class DbStructureGuideTest
+public class GraphDbStructureGuideTest
{
@Test
public void visitsLabelIds() throws Exception
@@ -318,7 +318,7 @@ public void accept( DbStructureVisitor visitor )
throw new IllegalStateException( "Dangling transaction before running visitable" );
}
- DbStructureGuide analyzer = new DbStructureGuide( graph );
+ GraphDbStructureGuide analyzer = new GraphDbStructureGuide( graph );
analyzer.accept( visitor );
}