Skip to content

Commit

Permalink
Implement nodeUniqueIndexSeek in Kernel API
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusmelke committed Mar 15, 2018
1 parent 2a297d8 commit bcd4a21
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 53 deletions.
Expand Up @@ -52,11 +52,10 @@ import org.neo4j.kernel.api._
import org.neo4j.kernel.api.exceptions.schema.{AlreadyConstrainedException, AlreadyIndexedException} import org.neo4j.kernel.api.exceptions.schema.{AlreadyConstrainedException, AlreadyIndexedException}
import org.neo4j.kernel.api.schema.SchemaDescriptorFactory import org.neo4j.kernel.api.schema.SchemaDescriptorFactory
import org.neo4j.kernel.api.schema.constaints.ConstraintDescriptorFactory import org.neo4j.kernel.api.schema.constaints.ConstraintDescriptorFactory
import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory
import org.neo4j.kernel.guard.TerminationGuard import org.neo4j.kernel.guard.TerminationGuard
import org.neo4j.kernel.impl.api.RelationshipVisitor import org.neo4j.kernel.impl.api.RelationshipVisitor
import org.neo4j.kernel.impl.api.operations.KeyReadOperations import org.neo4j.kernel.impl.api.operations.KeyReadOperations
import org.neo4j.kernel.impl.api.store.{DefaultCapableIndexReference, RelationshipIterator} import org.neo4j.kernel.impl.api.store.{DefaultIndexReference, RelationshipIterator}
import org.neo4j.kernel.impl.core.{EmbeddedProxySPI, ThreadToStatementContextBridge} import org.neo4j.kernel.impl.core.{EmbeddedProxySPI, ThreadToStatementContextBridge}
import org.neo4j.kernel.impl.coreapi.PropertyContainerLocker import org.neo4j.kernel.impl.coreapi.PropertyContainerLocker
import org.neo4j.kernel.impl.query.Neo4jTransactionalContext import org.neo4j.kernel.impl.query.Neo4jTransactionalContext
Expand Down Expand Up @@ -462,9 +461,9 @@ sealed class TransactionBoundQueryContext(val transactionalContext: Transactiona


override def lockingUniqueIndexSeek(indexReference: IndexReference, values: Seq[Any]): Option[NodeValue] = { override def lockingUniqueIndexSeek(indexReference: IndexReference, values: Seq[Any]): Option[NodeValue] = {
indexSearchMonitor.lockingUniqueIndexSeek(indexReference, values) indexSearchMonitor.lockingUniqueIndexSeek(indexReference, values)
val index = SchemaIndexDescriptorFactory.uniqueForLabel(indexReference.label, indexReference.properties:_*) val index = DefaultIndexReference.general(indexReference.label(), indexReference.properties():_*)
val predicates = indexReference.properties.zip(values).map(p => IndexQuery.exact(p._1, p._2)) val predicates = indexReference.properties.zip(values).map(p => IndexQuery.exact(p._1, p._2))
val nodeId = transactionalContext.statement.readOperations().nodeGetFromUniqueIndexSeek(index, predicates:_*) val nodeId = transactionalContext.kernelTransaction.dataRead().nodeUniqueIndexSeek(index, IndexOrder.NONE, predicates:_*)
if (StatementConstants.NO_SUCH_NODE == nodeId) None else Some(nodeOps.getById(nodeId)) if (StatementConstants.NO_SUCH_NODE == nodeId) None else Some(nodeOps.getById(nodeId))
} }


Expand Down Expand Up @@ -814,8 +813,7 @@ sealed class TransactionBoundQueryContext(val transactionalContext: Transactiona


override def dropIndexRule(descriptor: IndexDescriptor): Unit = override def dropIndexRule(descriptor: IndexDescriptor): Unit =
transactionalContext.kernelTransaction.schemaWrite().indexDrop( transactionalContext.kernelTransaction.schemaWrite().indexDrop(
new DefaultCapableIndexReference(false, IndexCapability.NO_CAPABILITY, null, descriptor.label, DefaultIndexReference.general(descriptor.label, descriptor.properties.map(_.id):_*)
descriptor.properties.map(_.id):_*)
) )


override def createNodeKeyConstraint(descriptor: IndexDescriptor): Boolean = try { override def createNodeKeyConstraint(descriptor: IndexDescriptor): Boolean = try {
Expand Down
Expand Up @@ -20,8 +20,6 @@
package org.neo4j.internal.kernel.api; package org.neo4j.internal.kernel.api;


import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.internal.kernel.api.exceptions.KernelException;
import org.neo4j.internal.kernel.api.procs.QualifiedName;
import org.neo4j.internal.kernel.api.procs.UserFunctionHandle;


/** /**
* Defines the graph read operations of the Kernel. * Defines the graph read operations of the Kernel.
Expand All @@ -41,6 +39,20 @@ public interface Read
void nodeIndexSeek( IndexReference index, NodeValueIndexCursor cursor, IndexOrder indexOrder, IndexQuery... query ) void nodeIndexSeek( IndexReference index, NodeValueIndexCursor cursor, IndexOrder indexOrder, IndexQuery... query )
throws KernelException; throws KernelException;


/**
* Returns node id of node found in unique index or -1 if no node was found.
*
* Note that this is a very special method and should be use with caution. It has special locking semantics in
* order to facilitate unique creation of nodes. If a node is found; a shared lock for the index entry will be
* held whereas if no node is found we will hold onto an exclusive lock until the close of the transaction.
*
* @param index {@link IndexReference} referencing index to query.
* @param indexOrder requested {@link IndexOrder} of result. Must be among the capabilities of
* {@link IndexReference referenced index}, or {@link IndexOrder#NONE}.
* @param predicates Combination of {@link IndexQuery.ExactPredicate index queries} to run against referenced index.
*/
long nodeUniqueIndexSeek( IndexReference index, IndexOrder indexOrder, IndexQuery.ExactPredicate... predicates )
throws KernelException;
/** /**
* @param index {@link IndexReference} referencing index to query. * @param index {@link IndexReference} referencing index to query.
* @param cursor the cursor to use for consuming the results. * @param cursor the cursor to use for consuming the results.
Expand Down
Expand Up @@ -31,6 +31,7 @@
import org.neo4j.internal.kernel.api.RelationshipScanCursor; import org.neo4j.internal.kernel.api.RelationshipScanCursor;
import org.neo4j.internal.kernel.api.RelationshipTraversalCursor; import org.neo4j.internal.kernel.api.RelationshipTraversalCursor;
import org.neo4j.internal.kernel.api.Scan; import org.neo4j.internal.kernel.api.Scan;
import org.neo4j.internal.kernel.api.exceptions.KernelException;


public class StubRead implements Read public class StubRead implements Read
{ {
Expand All @@ -41,6 +42,13 @@ public void nodeIndexSeek( IndexReference index, NodeValueIndexCursor cursor, In
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }


@Override
public long nodeUniqueIndexSeek( IndexReference index, IndexOrder indexOrder,
IndexQuery.ExactPredicate... predicates ) throws KernelException
{
throw new UnsupportedOperationException();
}

@Override @Override
public void nodeIndexScan( IndexReference index, NodeValueIndexCursor cursor, IndexOrder indexOrder ) public void nodeIndexScan( IndexReference index, NodeValueIndexCursor cursor, IndexOrder indexOrder )
{ {
Expand Down
Expand Up @@ -23,19 +23,17 @@
import org.junit.Test; import org.junit.Test;


import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.Transaction;
import org.neo4j.internal.kernel.api.CapableIndexReference;
import org.neo4j.internal.kernel.api.IndexOrder;
import org.neo4j.internal.kernel.api.Read;
import org.neo4j.internal.kernel.api.TokenWrite; import org.neo4j.internal.kernel.api.TokenWrite;
import org.neo4j.internal.kernel.api.Write; import org.neo4j.internal.kernel.api.Write;
import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.internal.kernel.api.exceptions.KernelException;
import org.neo4j.internal.kernel.api.schema.LabelSchemaDescriptor; import org.neo4j.internal.kernel.api.schema.LabelSchemaDescriptor;
import org.neo4j.internal.kernel.api.security.LoginContext; import org.neo4j.internal.kernel.api.security.LoginContext;
import org.neo4j.kernel.api.ReadOperations; import org.neo4j.kernel.api.KernelTransaction;
import org.neo4j.kernel.api.Statement;
import org.neo4j.kernel.api.StatementConstants; import org.neo4j.kernel.api.StatementConstants;
import org.neo4j.kernel.api.exceptions.index.IndexNotApplicableKernelException;
import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException;
import org.neo4j.kernel.api.exceptions.schema.IndexBrokenKernelException;
import org.neo4j.kernel.api.schema.SchemaDescriptorFactory; import org.neo4j.kernel.api.schema.SchemaDescriptorFactory;
import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor;
import org.neo4j.test.DoubleLatch; import org.neo4j.test.DoubleLatch;
import org.neo4j.values.storable.Value; import org.neo4j.values.storable.Value;
import org.neo4j.values.storable.Values; import org.neo4j.values.storable.Values;
Expand Down Expand Up @@ -80,14 +78,14 @@ public void createKeys() throws Exception
public void shouldFindMatchingNode() throws Exception public void shouldFindMatchingNode() throws Exception
{ {
// given // given
SchemaIndexDescriptor index = createUniquenessConstraint( labelId, propertyId1 ); CapableIndexReference index = createUniquenessConstraint( labelId, propertyId1 );
Value value = Values.of( "value" ); Value value = Values.of( "value" );
long nodeId = createNodeWithValue( value ); long nodeId = createNodeWithValue( value );


// when looking for it // when looking for it
ReadOperations readOperations = readOperationsInNewTransaction(); Read read = newTransaction().dataRead();
int propertyId = index.schema().getPropertyId(); int propertyId = index.properties()[0];
long foundId = readOperations.nodeGetFromUniqueIndexSeek( index, exact( propertyId, value ) ); long foundId = read.nodeUniqueIndexSeek( index, IndexOrder.NONE, exact( propertyId, value ) );
commit(); commit();


// then // then
Expand All @@ -98,13 +96,13 @@ public void shouldFindMatchingNode() throws Exception
public void shouldNotFindNonMatchingNode() throws Exception public void shouldNotFindNonMatchingNode() throws Exception
{ {
// given // given
SchemaIndexDescriptor index = createUniquenessConstraint( labelId, propertyId1 ); CapableIndexReference index = createUniquenessConstraint( labelId, propertyId1 );
Value value = Values.of( "value" ); Value value = Values.of( "value" );
createNodeWithValue( Values.of( "other_" + value ) ); createNodeWithValue( Values.of( "other_" + value ) );


// when looking for it // when looking for it
ReadOperations readOperations = readOperationsInNewTransaction(); KernelTransaction transaction = newTransaction();
long foundId = readOperations.nodeGetFromUniqueIndexSeek( index, exact( propertyId1, value ) ); long foundId = transaction.dataRead().nodeUniqueIndexSeek( index, IndexOrder.NONE, exact( propertyId1, value ) );
commit(); commit();


// then // then
Expand All @@ -115,14 +113,15 @@ public void shouldNotFindNonMatchingNode() throws Exception
public void shouldCompositeFindMatchingNode() throws Exception public void shouldCompositeFindMatchingNode() throws Exception
{ {
// given // given
SchemaIndexDescriptor index = createUniquenessConstraint( labelId, propertyId1, propertyId2 ); CapableIndexReference index = createUniquenessConstraint( labelId, propertyId1, propertyId2 );
Value value1 = Values.of( "value1" ); Value value1 = Values.of( "value1" );
Value value2 = Values.of( "value2" ); Value value2 = Values.of( "value2" );
long nodeId = createNodeWithValues( value1, value2 ); long nodeId = createNodeWithValues( value1, value2 );


// when looking for it // when looking for it
ReadOperations readOperations = readOperationsInNewTransaction(); KernelTransaction transaction = newTransaction();
long foundId = readOperations.nodeGetFromUniqueIndexSeek( index, long foundId = transaction.dataRead().nodeUniqueIndexSeek( index,
IndexOrder.NONE,
exact( propertyId1, value1 ), exact( propertyId1, value1 ),
exact( propertyId2, value2 ) ); exact( propertyId2, value2 ) );
commit(); commit();
Expand All @@ -135,14 +134,15 @@ public void shouldCompositeFindMatchingNode() throws Exception
public void shouldNotCompositeFindNonMatchingNode() throws Exception public void shouldNotCompositeFindNonMatchingNode() throws Exception
{ {
// given // given
SchemaIndexDescriptor index = createUniquenessConstraint( labelId, propertyId1, propertyId2 ); CapableIndexReference index = createUniquenessConstraint( labelId, propertyId1, propertyId2 );
Value value1 = Values.of( "value1" ); Value value1 = Values.of( "value1" );
Value value2 = Values.of( "value2" ); Value value2 = Values.of( "value2" );
createNodeWithValues( Values.of( "other_" + value1 ), Values.of( "other_" + value2 ) ); createNodeWithValues( Values.of( "other_" + value1 ), Values.of( "other_" + value2 ) );


// when looking for it // when looking for it
ReadOperations readOperations = readOperationsInNewTransaction(); KernelTransaction transaction = newTransaction();
long foundId = readOperations.nodeGetFromUniqueIndexSeek( index, long foundId = transaction.dataRead().nodeUniqueIndexSeek( index,
IndexOrder.NONE,
exact( propertyId1, value1 ), exact( propertyId1, value1 ),
exact( propertyId2, value2 ) ); exact( propertyId2, value2 ) );
commit(); commit();
Expand Down Expand Up @@ -171,7 +171,7 @@ public void shouldBlockUniqueIndexSeekFromCompetingTransaction() throws Exceptio
// assert that we complete before timeout // assert that we complete before timeout
final DoubleLatch latch = new DoubleLatch(); final DoubleLatch latch = new DoubleLatch();


final SchemaIndexDescriptor index = createUniquenessConstraint( labelId, propertyId1 ); final CapableIndexReference index = createUniquenessConstraint( labelId, propertyId1 );
final Value value = Values.of( "value" ); final Value value = Values.of( "value" );


Write write = dataWriteInNewTransaction(); Write write = dataWriteInNewTransaction();
Expand All @@ -184,15 +184,14 @@ public void shouldBlockUniqueIndexSeekFromCompetingTransaction() throws Exceptio
Runnable runnableForThread2 = () -> Runnable runnableForThread2 = () ->
{ {
latch.waitForAllToStart(); latch.waitForAllToStart();
try ( Transaction tx = db.beginTx() ) try ( Transaction tx = db.beginTx();
KernelTransaction transaction = statementContextSupplier
.getKernelTransactionBoundToThisThread( true ) )
{ {
try ( Statement statement1 = statementContextSupplier.get() ) transaction.dataRead().nodeUniqueIndexSeek( index, IndexOrder.NONE, exact( propertyId1, value ) );
{
statement1.readOperations().nodeGetFromUniqueIndexSeek( index, exact( propertyId1, value ) );
}
tx.success(); tx.success();
} }
catch ( IndexNotFoundKernelException | IndexNotApplicableKernelException | IndexBrokenKernelException e ) catch ( KernelException e )
{ {
throw new RuntimeException( e ); throw new RuntimeException( e );
} }
Expand Down Expand Up @@ -246,12 +245,12 @@ private long createNodeWithValues( Value value1, Value value2 ) throws KernelExc
return nodeId; return nodeId;
} }


private SchemaIndexDescriptor createUniquenessConstraint( int labelId, int... propertyIds ) throws Exception private CapableIndexReference createUniquenessConstraint( int labelId, int... propertyIds ) throws Exception
{ {
Statement statement = statementInNewTransaction( LoginContext.AUTH_DISABLED ); KernelTransaction transaction = newTransaction( LoginContext.AUTH_DISABLED );
LabelSchemaDescriptor descriptor = SchemaDescriptorFactory.forLabel( labelId, propertyIds ); LabelSchemaDescriptor descriptor = SchemaDescriptorFactory.forLabel( labelId, propertyIds );
statement.schemaWriteOperations().uniquePropertyConstraintCreate( descriptor ); transaction.schemaWrite().uniquePropertyConstraintCreate( descriptor );
SchemaIndexDescriptor result = statement.readOperations().indexGetForSchema( descriptor ); CapableIndexReference result = transaction.schemaRead().index( descriptor.getLabelId(), descriptor.getPropertyIds() );
commit(); commit();
return result; return result;
} }
Expand Down
Expand Up @@ -21,17 +21,17 @@


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


import org.neo4j.internal.kernel.api.CapableIndexReference;
import org.neo4j.internal.kernel.api.IndexOrder;
import org.neo4j.internal.kernel.api.SchemaWrite; import org.neo4j.internal.kernel.api.SchemaWrite;
import org.neo4j.internal.kernel.api.TokenNameLookup; import org.neo4j.internal.kernel.api.TokenNameLookup;
import org.neo4j.internal.kernel.api.TokenRead;
import org.neo4j.internal.kernel.api.TokenWrite; import org.neo4j.internal.kernel.api.TokenWrite;
import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.internal.kernel.api.exceptions.KernelException;
import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.api.KernelTransaction;
import org.neo4j.kernel.api.ReadOperations;
import org.neo4j.kernel.api.SilentTokenNameLookup; import org.neo4j.kernel.api.SilentTokenNameLookup;
import org.neo4j.kernel.api.Statement; import org.neo4j.kernel.api.Statement;
import org.neo4j.kernel.api.exceptions.schema.UniquePropertyValueValidationException; import org.neo4j.kernel.api.exceptions.schema.UniquePropertyValueValidationException;
import org.neo4j.kernel.api.schema.SchemaDescriptorFactory;
import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor;
import org.neo4j.kernel.api.security.AnonymousContext; import org.neo4j.kernel.api.security.AnonymousContext;
import org.neo4j.values.storable.Values; import org.neo4j.values.storable.Values;


Expand Down Expand Up @@ -301,18 +301,18 @@ public void unrelatedNodesWithSamePropertyShouldNotInterfereWithUniquenessCheck(
} }


KernelTransaction transaction = newTransaction( AnonymousContext.writeToken() ); KernelTransaction transaction = newTransaction( AnonymousContext.writeToken() );
try ( Statement statement = transaction.acquireStatement() ) try ( Statement ignore = transaction.acquireStatement() )
{ {
ReadOperations readOps = statement.readOperations(); TokenRead tokenRead = transaction.tokenRead();
int person = readOps.labelGetForName( "Person" ); int person = tokenRead.nodeLabel( "Person" );
int propId = readOps.propertyKeyGetForName( "id" ); int propId = tokenRead.propertyKey( "id" );
SchemaIndexDescriptor idx = readOps.indexGetForSchema( SchemaDescriptorFactory.forLabel( person, propId ) ); CapableIndexReference idx = transaction.schemaRead().index( person, propId );


// when // when
createLabeledNode( transaction, "Item", "id", 2 ); createLabeledNode( transaction, "Item", "id", 2 );


// then I should find the original node // then I should find the original node
assertThat( readOps.nodeGetFromUniqueIndexSeek( idx, exact( propId, Values.of( 1 ) ) ), assertThat( transaction.dataRead().nodeUniqueIndexSeek( idx, IndexOrder.NONE, exact( propId, Values.of( 1 ) ) ),
equalTo( ourNode ) ); equalTo( ourNode ) );
} }
commit(); commit();
Expand All @@ -332,18 +332,18 @@ public void addingUniqueNodeWithUnrelatedValueShouldNotAffectLookup() throws Exc
} }


KernelTransaction transaction = newTransaction( AnonymousContext.writeToken() ); KernelTransaction transaction = newTransaction( AnonymousContext.writeToken() );
try ( Statement statement = transaction.acquireStatement() ) try ( Statement ignore = transaction.acquireStatement() )
{ {
ReadOperations readOps = statement.readOperations(); TokenRead tokenRead = transaction.tokenRead();
int person = readOps.labelGetForName( "Person" ); int person = tokenRead.nodeLabel( "Person" );
int propId = readOps.propertyKeyGetForName( "id" ); int propId = tokenRead.propertyKey( "id" );
SchemaIndexDescriptor idx = readOps.indexGetForSchema( SchemaDescriptorFactory.forLabel( person, propId ) ); CapableIndexReference idx = transaction.schemaRead().index( person, propId );


// when // when
createLabeledNode( transaction, "Person", "id", 2 ); createLabeledNode( transaction, "Person", "id", 2 );


// then I should find the original node // then I should find the original node
assertThat( readOps.nodeGetFromUniqueIndexSeek( idx, exact( propId, Values.of( 1 ) ) ), assertThat( transaction.dataRead().nodeUniqueIndexSeek( idx, IndexOrder.NONE, exact( propId, Values.of( 1 ) ) ),
equalTo( ourNode ) ); equalTo( ourNode ) );
} }
commit(); commit();
Expand Down

0 comments on commit bcd4a21

Please sign in to comment.