Skip to content

Commit

Permalink
Use Kernel API for index seek in compiled runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusmelke committed Feb 1, 2018
1 parent 206d332 commit f17e740
Show file tree
Hide file tree
Showing 13 changed files with 181 additions and 64 deletions.
Expand Up @@ -19,12 +19,13 @@
*/ */
package org.neo4j.cypher.internal.codegen; package org.neo4j.cypher.internal.codegen;


import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.internal.kernel.api.CapableIndexReference;
import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.internal.kernel.api.CursorFactory;
import org.neo4j.kernel.api.ReadOperations; import org.neo4j.internal.kernel.api.IndexOrder;
import org.neo4j.kernel.api.exceptions.index.IndexNotApplicableKernelException; import org.neo4j.internal.kernel.api.IndexQuery;
import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.internal.kernel.api.NodeValueIndexCursor;
import org.neo4j.kernel.api.schema.index.IndexDescriptor; import org.neo4j.internal.kernel.api.Read;
import org.neo4j.internal.kernel.api.exceptions.KernelException;


import static org.neo4j.cypher.internal.codegen.CompiledConversionUtils.makeValueNeoSafe; import static org.neo4j.cypher.internal.codegen.CompiledConversionUtils.makeValueNeoSafe;
import static org.neo4j.internal.kernel.api.IndexQuery.exact; import static org.neo4j.internal.kernel.api.IndexQuery.exact;
Expand All @@ -45,23 +46,26 @@ private CompiledIndexUtils()
/** /**
* Performs an index seek. * Performs an index seek.
* *
* @param readOperations The ReadOperation instance to use for seeking * @param read The Read instance to use for seeking
* @param descriptor The descriptor of the index * @param cursors Used for cursor allocation
* @param propertyId The property to seek for * @param index A reference to an index
* @param value The value to seek for * @param value The value to seek for
* @return An iterator containing data found in index. * @return A cursor positioned at the data found in index.
*/ */
public static PrimitiveLongIterator indexSeek( ReadOperations readOperations, IndexDescriptor descriptor, public static NodeValueIndexCursor indexSeek( Read read, CursorFactory cursors, CapableIndexReference index, Object value )
int propertyId, Object value ) throws KernelException
throws IndexNotApplicableKernelException, IndexNotFoundKernelException
{ {
assert index.properties().length == 1;
if ( value == null ) if ( value == null )
{ {
return PrimitiveLongCollections.emptyIterator(); return NodeValueIndexCursor.EMPTY;
} }
else else
{ {
return readOperations.indexQuery( descriptor, exact( propertyId, makeValueNeoSafe( value ) ) ); NodeValueIndexCursor cursor = cursors.allocateNodeValueIndexCursor();
IndexQuery.ExactPredicate query = exact( index.properties()[0], makeValueNeoSafe( value ) );
read.nodeIndexSeek( index, cursor, IndexOrder.NONE, query );
return cursor;
} }
} }
} }
Expand Up @@ -21,53 +21,53 @@


import org.junit.Test; import org.junit.Test;


import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.internal.kernel.api.CapableIndexReference;
import org.neo4j.kernel.api.ReadOperations; import org.neo4j.internal.kernel.api.CursorFactory;
import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.internal.kernel.api.NodeValueIndexCursor;
import org.neo4j.kernel.api.exceptions.index.IndexNotApplicableKernelException; import org.neo4j.internal.kernel.api.Read;
import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.internal.kernel.api.exceptions.KernelException;
import org.neo4j.kernel.api.schema.index.IndexDescriptor;
import org.neo4j.kernel.api.schema.index.IndexDescriptorFactory;


import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.neo4j.kernel.api.index.IndexQueryHelper.exact; import static org.mockito.Mockito.when;


public class CompiledIndexUtilsTest public class CompiledIndexUtilsTest
{ {

@Test @Test
public void shouldCallIndexSeek() public void shouldCallIndexSeek() throws KernelException
throws EntityNotFoundException, IndexNotApplicableKernelException, IndexNotFoundKernelException
{ {

// GIVEN // GIVEN
ReadOperations read = mock( ReadOperations.class ); Read read = mock( Read.class );
CapableIndexReference index = mock( CapableIndexReference.class );
when( index.properties() ).thenReturn( new int[]{42} );


// WHEN // WHEN
IndexDescriptor descriptor = IndexDescriptorFactory.forLabel( 12, 42 ); CompiledIndexUtils.indexSeek( read, mock( CursorFactory.class ), index, "hello" );
CompiledIndexUtils.indexSeek( read, descriptor, 42, "hello" );


// THEN // THEN
verify( read, times( 1 ) ).indexQuery( descriptor, exact( 42, "hello" ) ); verify( read, times( 1 ) ).nodeIndexSeek( any(), any(), any(), any() );
} }


@Test @Test
public void shouldHandleNullInIndexSeek() public void shouldHandleNullInIndexSeek() throws KernelException
throws EntityNotFoundException, IndexNotApplicableKernelException, IndexNotFoundKernelException
{ {
// GIVEN // GIVEN
ReadOperations read = mock( ReadOperations.class ); Read read = mock( Read.class );
CapableIndexReference index = mock( CapableIndexReference.class );
when( index.properties() ).thenReturn( new int[]{42} );


// WHEN // WHEN
IndexDescriptor descriptor = IndexDescriptorFactory.forLabel( 12, 42 ); NodeValueIndexCursor cursor = CompiledIndexUtils.indexSeek( mock( Read.class ), mock( CursorFactory.class ),
PrimitiveLongIterator iterator = index, null );
CompiledIndexUtils.indexSeek( read, descriptor, 42, null );


// THEN // THEN
verify( read, never() ).indexQuery( any( ), any( ) ); verify( read, never() ).nodeIndexSeek( any(), any(), any() );
assertFalse( iterator.hasNext() ); assertFalse( cursor.next() );
} }
} }
Expand Up @@ -300,5 +300,7 @@ class DelegatingQueryTransactionalContext(val inner: QueryTransactionalContext)


override def tokenRead: TokenRead = inner.tokenRead override def tokenRead: TokenRead = inner.tokenRead


override def schemaRead: SchemaRead = inner.schemaRead

override def dataWrite: Write = inner.dataWrite override def dataWrite: Write = inner.dataWrite
} }
Expand Up @@ -22,8 +22,8 @@ package org.neo4j.cypher.internal.runtime.interpreted
import org.neo4j.cypher.internal.planner.v3_4.spi.KernelStatisticProvider import org.neo4j.cypher.internal.planner.v3_4.spi.KernelStatisticProvider
import org.neo4j.cypher.internal.runtime.QueryTransactionalContext import org.neo4j.cypher.internal.runtime.QueryTransactionalContext
import org.neo4j.graphdb.{Lock, PropertyContainer} import org.neo4j.graphdb.{Lock, PropertyContainer}
import org.neo4j.internal.kernel.api._
import org.neo4j.internal.kernel.api.security.SecurityContext import org.neo4j.internal.kernel.api.security.SecurityContext
import org.neo4j.internal.kernel.api.{CursorFactory, Read, TokenRead, Write}
import org.neo4j.kernel.GraphDatabaseQueryService import org.neo4j.kernel.GraphDatabaseQueryService
import org.neo4j.kernel.api.KernelTransaction.Revertable import org.neo4j.kernel.api.KernelTransaction.Revertable
import org.neo4j.kernel.api.dbms.DbmsOperations import org.neo4j.kernel.api.dbms.DbmsOperations
Expand Down Expand Up @@ -59,6 +59,8 @@ case class TransactionalContextWrapper(tc: TransactionalContext) extends QueryTr


override def tokenRead: TokenRead = tc.kernelTransaction().tokenRead() override def tokenRead: TokenRead = tc.kernelTransaction().tokenRead()


override def schemaRead: SchemaRead = tc.kernelTransaction().schemaRead()

override def dataWrite: Write = tc.kernelTransaction().dataWrite() override def dataWrite: Write = tc.kernelTransaction().dataWrite()


override def readOperations: ReadOperations = tc.readOperations() override def readOperations: ReadOperations = tc.readOperations()
Expand Down
Expand Up @@ -246,6 +246,8 @@ trait QueryTransactionalContext extends CloseableResource {


def tokenRead: TokenRead def tokenRead: TokenRead


def schemaRead: SchemaRead

def dataWrite: Write def dataWrite: Write


def readOperations: ReadOperations def readOperations: ReadOperations
Expand Down
Expand Up @@ -21,6 +21,8 @@


import org.neo4j.values.storable.Value; import org.neo4j.values.storable.Value;


import static org.neo4j.values.storable.Values.NO_VALUE;

/** /**
* Cursor for scanning the property values of nodes in a schema index. * Cursor for scanning the property values of nodes in a schema index.
* <p> * <p>
Expand Down Expand Up @@ -65,4 +67,70 @@ public interface NodeValueIndexCursor extends NodeIndexCursor
boolean hasValue(); boolean hasValue();


Value propertyValue( int offset ); Value propertyValue( int offset );

class Empty implements NodeValueIndexCursor
{

@Override
public void node( NodeCursor cursor )
{

}

@Override
public long nodeReference()
{
return -1L;
}

@Override
public boolean next()
{
return false;
}

@Override
public boolean shouldRetry()
{
return false;
}

@Override
public void close()
{

}

@Override
public boolean isClosed()
{
return false;
}

@Override
public int numberOfProperties()
{
return 0;
}

@Override
public int propertyKey( int offset )
{
return -1;
}

@Override
public boolean hasValue()
{
return false;
}

@Override
public Value propertyValue( int offset )
{
return NO_VALUE;
}
}

NodeValueIndexCursor EMPTY = new Empty();
} }
Expand Up @@ -33,7 +33,7 @@ case class IndexSeek(opName: String, labelName: String, propNames: Seq[String],
val propKeyVar = context.namer.newVarName() val propKeyVar = context.namer.newVarName()
generator.lookupLabelId(labelVar, labelName) generator.lookupLabelId(labelVar, labelName)
generator.lookupPropertyKey(propNames.head, propKeyVar) generator.lookupPropertyKey(propNames.head, propKeyVar)
generator.newIndexDescriptor(descriptorVar, labelVar, propKeyVar) generator.newIndexReference(descriptorVar, labelVar, propKeyVar)
} }


override def produceIterator[E](iterVar: String, generator: MethodStructure[E])(implicit context: CodeGenContext) = { override def produceIterator[E](iterVar: String, generator: MethodStructure[E])(implicit context: CodeGenContext) = {
Expand All @@ -44,8 +44,8 @@ case class IndexSeek(opName: String, labelName: String, propNames: Seq[String],
override def produceNext[E](nextVar: Variable, iterVar: String, generator: MethodStructure[E]) override def produceNext[E](nextVar: Variable, iterVar: String, generator: MethodStructure[E])
(implicit context: CodeGenContext) = { (implicit context: CodeGenContext) = {
generator.incrementDbHits() generator.incrementDbHits()
generator.nextNode(nextVar.name, iterVar) generator.nodeFromNodeValueIndexCursor(nextVar.name, iterVar)
} }


override def hasNext[E](generator: MethodStructure[E], iterVar: String): E = generator.hasNextNode(iterVar) override def hasNext[E](generator: MethodStructure[E], iterVar: String): E = generator.advanceNodeValueIndexCursor(iterVar)
} }
Expand Up @@ -158,7 +158,7 @@ trait MethodStructure[E] {
def nodeGetRelationshipsWithDirectionAndTypes(iterVar: String, nodeVar: String, nodeVarType: CodeGenType, direction: SemanticDirection, typeVars: Seq[String]): Unit def nodeGetRelationshipsWithDirectionAndTypes(iterVar: String, nodeVar: String, nodeVarType: CodeGenType, direction: SemanticDirection, typeVars: Seq[String]): Unit
def connectingRelationships(iterVar: String, fromNode: String, fromNodeType: CodeGenType, dir: SemanticDirection, toNode:String, toNodeType: CodeGenType) def connectingRelationships(iterVar: String, fromNode: String, fromNodeType: CodeGenType, dir: SemanticDirection, toNode:String, toNodeType: CodeGenType)
def connectingRelationships(iterVar: String, fromNode: String, fromNodeType: CodeGenType, dir: SemanticDirection, types: Seq[String], toNode: String, toNodeType: CodeGenType) def connectingRelationships(iterVar: String, fromNode: String, fromNodeType: CodeGenType, dir: SemanticDirection, types: Seq[String], toNode: String, toNodeType: CodeGenType)
def nextNode(targetVar: String, iterVar: String): Unit def nodeFromNodeValueIndexCursor(targetVar: String, iterVar: String): Unit
def nodeFromNodeCursor(targetVar: String, iterVar: String): Unit def nodeFromNodeCursor(targetVar: String, iterVar: String): Unit
def nodeFromNodeLabelIndexCursor(targetVar: String, iterVar: String): Unit def nodeFromNodeLabelIndexCursor(targetVar: String, iterVar: String): Unit
def nextRelationshipAndNode(toNodeVar: String, iterVar: String, direction: SemanticDirection, fromNodeVar: String, relVar: String): Unit def nextRelationshipAndNode(toNodeVar: String, iterVar: String, direction: SemanticDirection, fromNodeVar: String, relVar: String): Unit
Expand All @@ -167,6 +167,8 @@ trait MethodStructure[E] {
def advanceNodeCursor(iterVar: String): E def advanceNodeCursor(iterVar: String): E
def advanceNodeLabelIndexCursor(iterVar: String): E def advanceNodeLabelIndexCursor(iterVar: String): E
def advanceRelationshipSelectionCursor(iterVar: String): E def advanceRelationshipSelectionCursor(iterVar: String): E
def advanceNodeValueIndexCursor(iterVar: String): E
def hasNextRelationship(iterVar: String): E
def nodeGetPropertyById(nodeVar: String, nodeVarType: CodeGenType, propId: Int, propValueVar: String): Unit def nodeGetPropertyById(nodeVar: String, nodeVarType: CodeGenType, propId: Int, propValueVar: String): Unit
def nodeGetPropertyForVar(nodeVar: String, nodeVarType: CodeGenType, propIdVar: String, propValueVar: String): Unit def nodeGetPropertyForVar(nodeVar: String, nodeVarType: CodeGenType, propIdVar: String, propValueVar: String): Unit
def nodeIdSeek(nodeIdVar: String, expression: E, codeGenType: CodeGenType)(block: MethodStructure[E] => Unit): Unit def nodeIdSeek(nodeIdVar: String, expression: E, codeGenType: CodeGenType)(block: MethodStructure[E] => Unit): Unit
Expand All @@ -175,7 +177,8 @@ trait MethodStructure[E] {
def lookupPropertyKey(propName: String, propVar: String) def lookupPropertyKey(propName: String, propVar: String)
def indexSeek(iterVar: String, descriptorVar: String, value: E, codeGenType: CodeGenType): Unit def indexSeek(iterVar: String, descriptorVar: String, value: E, codeGenType: CodeGenType): Unit
def relType(relIdVar: String, typeVar: String): Unit def relType(relIdVar: String, typeVar: String): Unit
def newIndexDescriptor(descriptorVar: String, labelVar: String, propKeyVar: String): Unit def newIndexReference(descriptorVar: String, labelVar: String, propKeyVar: String): Unit
def createRelExtractor(extractorName: String): Unit
def nodeCountFromCountStore(expression: E): E def nodeCountFromCountStore(expression: E): E
def relCountFromCountStore(start: E, end: E, types: E*): E def relCountFromCountStore(start: E, end: E, types: E*): E
def token(t: Int): E def token(t: Int): E
Expand Down
Expand Up @@ -35,5 +35,6 @@ case class Fields(closer: FieldReference,
nodeCursor: FieldReference, nodeCursor: FieldReference,
propertyCursor: FieldReference, propertyCursor: FieldReference,
dataRead: FieldReference, dataRead: FieldReference,
tokenRead: FieldReference tokenRead: FieldReference,
schemaRead: FieldReference
) )

0 comments on commit f17e740

Please sign in to comment.