Skip to content

Commit

Permalink
Provide information about which indexes a query uses
Browse files Browse the repository at this point in the history
Only works with Cypher version 3.2, support in older versions would
require changes to those versions and new releases.
  • Loading branch information
thobe committed Jan 19, 2017
1 parent 9e4478f commit 2694e14
Show file tree
Hide file tree
Showing 21 changed files with 458 additions and 39 deletions.
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2002-2017 "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.function;

import java.util.function.Consumer;

public interface FailableConsumer<TYPE> extends Consumer<TYPE>
{
void fail( Exception failure );
}
Expand Up @@ -32,6 +32,9 @@
import java.util.function.Supplier;
import javax.annotation.Nonnull;

import static org.neo4j.function.ThrowingPredicate.throwingPredicate;
import static org.neo4j.function.ThrowingSupplier.throwingSupplier;

/**
* Constructors for basic {@link Predicate} types
*/
Expand Down Expand Up @@ -147,7 +150,7 @@ public static <TYPE> TYPE await( Supplier<TYPE> supplier, Predicate<TYPE> predic
public static <TYPE> TYPE await( Supplier<TYPE> supplier, Predicate<TYPE> predicate, long timeout,
TimeUnit timeoutUnit ) throws TimeoutException
{
return awaitEx( supplier::get, predicate::test, timeout, timeoutUnit );
return awaitEx( throwingSupplier( supplier ), throwingPredicate( predicate ), timeout, timeoutUnit );
}

public static <TYPE, EXCEPTION extends Exception> TYPE awaitEx( ThrowingSupplier<TYPE,EXCEPTION> supplier,
Expand All @@ -159,8 +162,8 @@ public static <TYPE, EXCEPTION extends Exception> TYPE awaitEx( ThrowingSupplier
return composed.lastInput();
}

public static <TYPE, EXCEPTION extends Exception> TYPE awaitEx( ThrowingSupplier<TYPE,EXCEPTION> supplier,
ThrowingPredicate<TYPE,EXCEPTION> predicate, long timeout, TimeUnit timeoutUnit )
public static <TYPE, EXCEPTION extends Exception> TYPE awaitEx( ThrowingSupplier<TYPE,? extends EXCEPTION> supplier,
ThrowingPredicate<TYPE, ? extends EXCEPTION> predicate, long timeout, TimeUnit timeoutUnit )
throws TimeoutException, EXCEPTION
{
Suppliers.ThrowingCapturingSupplier<TYPE,EXCEPTION> composed = Suppliers.compose( supplier, predicate );
Expand Down
17 changes: 12 additions & 5 deletions community/common/src/main/java/org/neo4j/function/Suppliers.java
Expand Up @@ -118,8 +118,9 @@ public T get()
};
}

public static <T, E extends Exception> ThrowingCapturingSupplier<T,E> compose( final ThrowingSupplier<T,E> input,
final ThrowingPredicate<T,E> predicate )
public static <T, E extends Exception> ThrowingCapturingSupplier<T,E> compose(
final ThrowingSupplier<T,? extends E> input,
final ThrowingPredicate<T,? extends E> predicate )
{
return new ThrowingCapturingSupplier<>( input, predicate );
}
Expand All @@ -132,12 +133,12 @@ public static BooleanSupplier untilTimeExpired( long duration, TimeUnit unit )

static class ThrowingCapturingSupplier<T, E extends Exception> implements ThrowingSupplier<Boolean,E>
{
private final ThrowingSupplier<T,E> input;
private final ThrowingPredicate<T,E> predicate;
private final ThrowingSupplier<T,? extends E> input;
private final ThrowingPredicate<T,? extends E> predicate;

private T current;

ThrowingCapturingSupplier( ThrowingSupplier<T,E> input, ThrowingPredicate<T,E> predicate )
ThrowingCapturingSupplier( ThrowingSupplier<T,? extends E> input, ThrowingPredicate<T,? extends E> predicate )
{
this.input = input;
this.predicate = predicate;
Expand All @@ -154,5 +155,11 @@ public Boolean get() throws E
current = input.get();
return predicate.test( current );
}

@Override
public String toString()
{
return String.format( "%s on %s", predicate, input );
}
}
}
Expand Up @@ -19,6 +19,8 @@
*/
package org.neo4j.function;

import java.util.function.Predicate;

/**
* Represents a predicate (boolean-valued function) of one argument.
*
Expand All @@ -35,4 +37,22 @@ public interface ThrowingPredicate<T, E extends Exception>
* @throws E an exception if the predicate fails
*/
boolean test( T t ) throws E;

static <TYPE> ThrowingPredicate<TYPE,RuntimeException> throwingPredicate( Predicate<TYPE> predicate )
{
return new ThrowingPredicate<TYPE,RuntimeException>()
{
@Override
public boolean test( TYPE value )
{
return predicate.test( value );
}

@Override
public String toString()
{
return predicate.toString();
}
};
}
}
Expand Up @@ -19,6 +19,8 @@
*/
package org.neo4j.function;

import java.util.function.Supplier;

/**
* Represents a supplier of results, that may throw an exception.
*
Expand All @@ -34,4 +36,22 @@ public interface ThrowingSupplier<T, E extends Exception>
* @throws E an exception if the function fails
*/
T get() throws E;

static <TYPE> ThrowingSupplier<TYPE,RuntimeException> throwingSupplier( Supplier<TYPE> supplier )
{
return new ThrowingSupplier<TYPE,RuntimeException>()
{
@Override
public TYPE get()
{
return supplier.get();
}

@Override
public String toString()
{
return supplier.toString();
}
};
}
}
Expand Up @@ -81,6 +81,8 @@ object BuildCompiledExecutionPlan extends Phase {
override def runtimeUsed = CompiledRuntimeName

override def notifications(planContext: PlanContext): Seq[InternalNotification] = Seq.empty

override def plannedIndexUsage: Seq[IndexUsage] = compiled.plannedIndexUsage
}


Expand Down
Expand Up @@ -62,6 +62,8 @@ object BuildInterpretedExecutionPlan extends Phase {
override def runtimeUsed = InterpretedRuntimeName

override def notifications(planContext: PlanContext) = checkForNotifications(pipe, planContext, context.config)

override def plannedIndexUsage = logicalPlan.indexUsage
}

from.copy(maybeExecutionPlan = Some(execPlan))
Expand Down
Expand Up @@ -72,7 +72,7 @@ class CodeGenerator(val structure: CodeStructure[GeneratedQuery], clock: Clock,
}
}

CompiledPlan(updating = false, None, fp, plannerName, description, res.columns, builder)
CompiledPlan(updating = false, None, fp, plannerName, description, res.columns, builder, plan.indexUsage)

case _ => throw new CantCompileQueryException("Can only compile plans with ProduceResult on top")
}
Expand Down
Expand Up @@ -19,7 +19,7 @@
*/
package org.neo4j.cypher.internal.compiler.v3_2.executionplan

import org.neo4j.cypher.internal.compiler.v3_2.spi.{PlanContext, GraphStatistics, QueryContext}
import org.neo4j.cypher.internal.compiler.v3_2.spi.{GraphStatistics, PlanContext, QueryContext}
import org.neo4j.cypher.internal.compiler.v3_2.{ExecutionMode, PlannerName, RuntimeName}
import org.neo4j.cypher.internal.frontend.v3_2.notification.InternalNotification

Expand All @@ -30,4 +30,14 @@ abstract class ExecutionPlan {
def isStale(lastTxId: () => Long, statistics: GraphStatistics): Boolean
def runtimeUsed: RuntimeName
def notifications(planContext: PlanContext): Seq[InternalNotification]
def plannedIndexUsage: Seq[IndexUsage] = Seq.empty
}

sealed trait IndexUsage {
def identifier:String
}

final case class SchemaIndexSeekUsage(identifier: String, label: String, propertyKey: String) extends IndexUsage
final case class SchemaIndexScanUsage(identifier: String, label: String, propertyKey: String) extends IndexUsage
final case class LegacyNodeIndexUsage(identifier: String, index: String) extends IndexUsage
final case class LegacyRelationshipIndexUsage(identifier: String, index: String) extends IndexUsage
Expand Up @@ -45,7 +45,8 @@ case class CompiledPlan(updating: Boolean,
plannerUsed: PlannerName,
planDescription: InternalPlanDescription,
columns: Seq[String],
executionResultBuilder: RunnablePlan )
executionResultBuilder: RunnablePlan,
plannedIndexUsage: Seq[IndexUsage] = Seq.empty )

case class PipeInfo(pipe: Pipe,
updating: Boolean,
Expand Down
Expand Up @@ -22,10 +22,11 @@ package org.neo4j.cypher.internal.compiler.v3_2.planner.logical.plans
import java.lang.reflect.Method

import org.neo4j.cypher.internal.compiler.v3_2.commands.QueryExpression
import org.neo4j.cypher.internal.compiler.v3_2.executionplan._
import org.neo4j.cypher.internal.compiler.v3_2.planner.{CardinalityEstimation, PlannerQuery}
import org.neo4j.cypher.internal.frontend.v3_2.Foldable._
import org.neo4j.cypher.internal.frontend.v3_2.Rewritable._
import org.neo4j.cypher.internal.frontend.v3_2.ast.Expression
import org.neo4j.cypher.internal.frontend.v3_2.ast.{Expression, NodeByIdentifiedIndex, NodeByIndexQuery, RelationshipByIdentifiedIndex, RelationshipByIndexQuery}
import org.neo4j.cypher.internal.frontend.v3_2.{InternalException, Rewritable}
import org.neo4j.cypher.internal.ir.v3_2.{IdName, Strictness}

Expand Down Expand Up @@ -113,6 +114,26 @@ abstract class LogicalPlan
def debugId: String = f"0x${hashCode()}%08x"

def flatten: Seq[LogicalPlan] = Flattener.create(this)

def indexUsage: Seq[IndexUsage] = {
import org.neo4j.cypher.internal.frontend.v3_2.Foldable._
this.fold(Seq.empty[IndexUsage]) {
case NodeIndexSeek(idName, label, propertyKey, _, _) =>
(acc) => acc :+ SchemaIndexSeekUsage(idName.name, label.name, propertyKey.name)
case NodeUniqueIndexSeek(idName, label, propertyKey, _, _) =>
(acc) => acc :+ SchemaIndexSeekUsage(idName.name, label.name, propertyKey.name)
case NodeIndexScan(idName, label, propertyKey, _) =>
(acc) => acc :+ SchemaIndexScanUsage(idName.name, label.name, propertyKey.name)
case LegacyNodeIndexSeek(idName, NodeByIdentifiedIndex(_, index, _, _), _) =>
(acc) => acc :+ LegacyNodeIndexUsage(idName.name, index)
case LegacyNodeIndexSeek(idName, NodeByIndexQuery(_, index, _), _) =>
(acc) => acc :+ LegacyNodeIndexUsage(idName.name, index)
case LegacyRelationshipIndexSeek(idName, RelationshipByIdentifiedIndex(_, index, _, _), _) =>
(acc) => acc :+ LegacyRelationshipIndexUsage(idName.name, index)
case LegacyRelationshipIndexSeek(idName, RelationshipByIndexQuery(_, index, _), _) =>
(acc) => acc :+ LegacyRelationshipIndexUsage(idName.name, index)
}
}
}

abstract class LogicalLeafPlan extends LogicalPlan with LazyLogicalPlan {
Expand All @@ -135,4 +156,4 @@ case object Flattener extends TreeBuilder[Seq[LogicalPlan]] {
override protected def build(plan: LogicalPlan, source: Seq[LogicalPlan]): Seq[LogicalPlan] = plan +: source

override protected def build(plan: LogicalPlan, lhs: Seq[LogicalPlan], rhs: Seq[LogicalPlan]): Seq[LogicalPlan] = (plan +: lhs) ++ rhs
}
}
Expand Up @@ -19,6 +19,8 @@
*/
package org.neo4j.cypher.internal.compatibility.v2_3

import java.util.Collections.emptyList

import org.neo4j.cypher.internal._
import org.neo4j.cypher.internal.compatibility._
import org.neo4j.cypher.internal.compiler.v2_3.executionplan.{EntityAccessor, ExecutionPlan => ExecutionPlan_v2_3}
Expand All @@ -31,7 +33,7 @@ import org.neo4j.cypher.internal.spi.v3_2.TransactionalContextWrapper
import org.neo4j.graphdb.{Node, Relationship}
import org.neo4j.kernel.GraphDatabaseQueryService
import org.neo4j.kernel.api.ExecutingQuery.PlannerInfo
import org.neo4j.kernel.api.KernelAPI
import org.neo4j.kernel.api.{IndexUsage, KernelAPI}
import org.neo4j.kernel.impl.core.NodeManager
import org.neo4j.kernel.impl.query.QueryExecutionMonitor
import org.neo4j.kernel.monitoring.{Monitors => KernelMonitors}
Expand Down Expand Up @@ -114,7 +116,7 @@ trait Compatibility {
def isStale(lastCommittedTxId: LastCommittedTxIdProvider, ctx: TransactionalContextWrapper): Boolean =
inner.isStale(lastCommittedTxId, TransactionBoundGraphStatistics(ctx.readOperations))

override def plannerInfo = new PlannerInfo(inner.plannerUsed.name, inner.runtimeUsed.name)
override def plannerInfo = new PlannerInfo(inner.plannerUsed.name, inner.runtimeUsed.name, emptyList[IndexUsage])
}

}
Expand Down
Expand Up @@ -19,6 +19,8 @@
*/
package org.neo4j.cypher.internal.compatibility.v3_1

import java.util.Collections.emptyList

import org.neo4j.cypher.internal._
import org.neo4j.cypher.internal.compatibility._
import org.neo4j.cypher.internal.compatibility.v3_1.helpers._
Expand All @@ -31,7 +33,7 @@ import org.neo4j.cypher.internal.spi.v3_1.{TransactionalContextWrapper => Transa
import org.neo4j.cypher.internal.spi.v3_2.{TransactionalContextWrapper => TransactionalContextWrapperV3_2}
import org.neo4j.kernel.GraphDatabaseQueryService
import org.neo4j.kernel.api.ExecutingQuery.PlannerInfo
import org.neo4j.kernel.api.KernelAPI
import org.neo4j.kernel.api.{IndexUsage, KernelAPI}
import org.neo4j.kernel.impl.query.QueryExecutionMonitor
import org.neo4j.kernel.monitoring.{Monitors => KernelMonitors}
import org.neo4j.logging.Log
Expand Down Expand Up @@ -115,7 +117,7 @@ trait Compatibility {
def isStale(lastCommittedTxId: LastCommittedTxIdProvider, ctx: TransactionalContextWrapperV3_2): Boolean =
inner.isStale(lastCommittedTxId, TransactionBoundGraphStatistics(ctx.readOperations))

override def plannerInfo = new PlannerInfo(inner.plannerUsed.name, inner.runtimeUsed.name)
override def plannerInfo = new PlannerInfo(inner.plannerUsed.name, inner.runtimeUsed.name, emptyList[IndexUsage])
}
}

Expand Down
Expand Up @@ -22,12 +22,13 @@ package org.neo4j.cypher.internal.compatibility.v3_2
import org.neo4j.cypher.internal._
import org.neo4j.cypher.internal.compatibility._
import org.neo4j.cypher.internal.compiler.v3_2
import org.neo4j.cypher.internal.compiler.v3_2.executionplan.{ExecutionPlan => ExecutionPlan_v3_2}
import org.neo4j.cypher.internal.compiler.v3_2.executionplan.{LegacyNodeIndexUsage, LegacyRelationshipIndexUsage, SchemaIndexScanUsage, SchemaIndexSeekUsage, ExecutionPlan => ExecutionPlan_v3_2}
import org.neo4j.cypher.internal.compiler.v3_2.tracing.rewriters.RewriterStepSequencer
import org.neo4j.cypher.internal.compiler.v3_2.{InfoLogger, ExplainMode => ExplainModev3_2, NormalMode => NormalModev3_2, ProfileMode => ProfileModev3_2, _}
import org.neo4j.cypher.internal.spi.v3_2.TransactionBoundQueryContext.IndexSearchMonitor
import org.neo4j.cypher.internal.spi.v3_2._
import org.neo4j.kernel.api.ExecutingQuery.PlannerInfo
import org.neo4j.kernel.api.IndexUsage.{legacyIndexUsage, schemaIndexUsage}
import org.neo4j.kernel.api.KernelAPI
import org.neo4j.kernel.impl.query.QueryExecutionMonitor
import org.neo4j.kernel.monitoring.{Monitors => KernelMonitors}
Expand Down Expand Up @@ -108,7 +109,15 @@ trait Compatibility {
def isStale(lastCommittedTxId: LastCommittedTxIdProvider, ctx: TransactionalContextWrapper): Boolean =
inner.isStale(lastCommittedTxId, TransactionBoundGraphStatistics(ctx.readOperations))

override def plannerInfo = new PlannerInfo(inner.plannerUsed.name, inner.runtimeUsed.name)
override def plannerInfo = {
import scala.collection.JavaConverters._
new PlannerInfo(inner.plannerUsed.name, inner.runtimeUsed.name, inner.plannedIndexUsage.map {
case SchemaIndexSeekUsage(identifier, label, propertyKey) => schemaIndexUsage(identifier, label, propertyKey)
case SchemaIndexScanUsage(identifier, label, propertyKey) => schemaIndexUsage(identifier, label, propertyKey)
case LegacyNodeIndexUsage(identifier, index) => legacyIndexUsage(identifier, "NODE", index)
case LegacyRelationshipIndexUsage(identifier, index) => legacyIndexUsage(identifier, "RELATIONSHIP", index)
}.asJava)
}
}
}

Expand Down

0 comments on commit 2694e14

Please sign in to comment.