Skip to content

Commit

Permalink
Refactored IndexTxStateUpdater to use NodeSchemaMatcher
Browse files Browse the repository at this point in the history
This simplifies the property change methods
  • Loading branch information
fickludd committed Mar 9, 2017
1 parent 3f9213d commit ba41165
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 126 deletions.
Expand Up @@ -1045,14 +1045,13 @@ public Property nodeSetProperty( KernelStatement state, long nodeId, DefinedProp
if ( existingProperty == NO_SUCH_PROPERTY ) if ( existingProperty == NO_SUCH_PROPERTY )
{ {
state.txState().nodeDoAddProperty( node.id(), property ); state.txState().nodeDoAddProperty( node.id(), property );
indexTxStateUpdater.onPropertyChange( state, node, indexTxStateUpdater.add( property ) ); indexTxStateUpdater.onPropertyAdd( state, node, property );
return Property.noProperty( property.propertyKeyId(), EntityType.NODE, node.id() ); return Property.noProperty( property.propertyKeyId(), EntityType.NODE, node.id() );
} }
else else
{ {
state.txState().nodeDoChangeProperty( node.id(), existingProperty, property ); state.txState().nodeDoChangeProperty( node.id(), existingProperty, property );
indexTxStateUpdater indexTxStateUpdater.onPropertyChange( state, node, existingProperty, property );
.onPropertyChange( state, node, indexTxStateUpdater.change( existingProperty, property ) );
return existingProperty; return existingProperty;
} }
} }
Expand Down Expand Up @@ -1119,7 +1118,7 @@ public Property nodeRemoveProperty( KernelStatement state, long nodeId, int prop
autoIndexing.nodes().propertyRemoved( ops, nodeId, propertyKeyId ); autoIndexing.nodes().propertyRemoved( ops, nodeId, propertyKeyId );
state.txState().nodeDoRemoveProperty( node.id(), existingProperty ); state.txState().nodeDoRemoveProperty( node.id(), existingProperty );


indexTxStateUpdater.onPropertyChange( state, node, indexTxStateUpdater.remove( existingProperty ) ); indexTxStateUpdater.onPropertyRemove( state, node, existingProperty );
} }
} }


Expand Down
Expand Up @@ -33,21 +33,23 @@
import org.neo4j.kernel.impl.api.KernelStatement; import org.neo4j.kernel.impl.api.KernelStatement;
import org.neo4j.kernel.impl.api.operations.EntityReadOperations; import org.neo4j.kernel.impl.api.operations.EntityReadOperations;
import org.neo4j.kernel.impl.api.operations.SchemaReadOperations; import org.neo4j.kernel.impl.api.operations.SchemaReadOperations;
import org.neo4j.kernel.impl.api.schema.NodeSchemaMatcher;
import org.neo4j.storageengine.api.NodeItem; import org.neo4j.storageengine.api.NodeItem;


import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_PROPERTY_KEY;
import static org.neo4j.kernel.api.properties.DefinedProperty.NO_SUCH_PROPERTY; import static org.neo4j.kernel.api.properties.DefinedProperty.NO_SUCH_PROPERTY;
import static org.neo4j.kernel.api.schema_new.SchemaDescriptorPredicates.hasProperty; import static org.neo4j.kernel.api.schema_new.SchemaDescriptorPredicates.hasProperty;


public class IndexTxStateUpdater public class IndexTxStateUpdater
{ {
private final SchemaReadOperations schemaReadOps; private final SchemaReadOperations schemaReadOps;
private final EntityReadOperations readOps; private final EntityReadOperations readOps;
private final NodeSchemaMatcher<NewIndexDescriptor> nodeIndexMatcher;


public IndexTxStateUpdater( SchemaReadOperations schemaReadOps, EntityReadOperations readOps ) public IndexTxStateUpdater( SchemaReadOperations schemaReadOps, EntityReadOperations readOps )
{ {
this.schemaReadOps = schemaReadOps; this.schemaReadOps = schemaReadOps;
this.readOps = readOps; this.readOps = readOps;
this.nodeIndexMatcher = new NodeSchemaMatcher<>( readOps );
} }


// LABEL CHANGES // LABEL CHANGES
Expand All @@ -57,8 +59,8 @@ public enum LabelChangeType { ADDED_LABEL, REMOVED_LABEL };
public void onLabelChange( KernelStatement state, int labelId, NodeItem node, LabelChangeType changeType ) public void onLabelChange( KernelStatement state, int labelId, NodeItem node, LabelChangeType changeType )
throws EntityNotFoundException throws EntityNotFoundException
{ {
PrimitiveIntSet propertyIds = Primitive.intSet(); PrimitiveIntSet nodePropertyIds = Primitive.intSet();
propertyIds.addAll( readOps.nodeGetPropertyKeys( state, node ).iterator() ); nodePropertyIds.addAll( readOps.nodeGetPropertyKeys( state, node ).iterator() );


Iterator<NewIndexDescriptor> indexes = Iterator<NewIndexDescriptor> indexes =
Iterators.concat( Iterators.concat(
Expand All @@ -68,9 +70,10 @@ public void onLabelChange( KernelStatement state, int labelId, NodeItem node, La
while ( indexes.hasNext() ) while ( indexes.hasNext() )
{ {
NewIndexDescriptor index = indexes.next(); NewIndexDescriptor index = indexes.next();
OrderedPropertyValues values = valuesIfPropertiesMatch( state, propertyIds, index, node ); int[] indexPropertyIds = index.schema().getPropertyIds();
if ( values != null ) if ( nodeHasIndexProperties( nodePropertyIds, indexPropertyIds ) )
{ {
OrderedPropertyValues values = getOrderedPropertyValues( state, node, indexPropertyIds );
if ( changeType == LabelChangeType.ADDED_LABEL ) if ( changeType == LabelChangeType.ADDED_LABEL )
{ {
state.txState().indexDoUpdateEntry( index.schema(), node.id(), null, values ); state.txState().indexDoUpdateEntry( index.schema(), node.id(), null, values );
Expand All @@ -85,75 +88,44 @@ public void onLabelChange( KernelStatement state, int labelId, NodeItem node, La


// PROPERTY CHANGES // PROPERTY CHANGES


interface PropertyUpdate public void onPropertyAdd( KernelStatement state, NodeItem node, DefinedProperty after )
{ throws EntityNotFoundException
void updateIndexIfApplicable( KernelStatement state, NodeItem node, PrimitiveIntSet nodePropertyIds,
NewIndexDescriptor index ) throws EntityNotFoundException;

int propertyId();
}

public PropertyUpdate add( DefinedProperty after )
{ {
return new PropertyUpdate() Iterator<NewIndexDescriptor> indexes = getIndexesForProperty( state, after.propertyKeyId() );
{ nodeIndexMatcher.onMatchingSchema( state, indexes, node, after.propertyKeyId(),
@Override index -> {
public void updateIndexIfApplicable( KernelStatement state, NodeItem node, OrderedPropertyValues values = getOrderedPropertyValues( state, node, after, index.schema().getPropertyIds() );
PrimitiveIntSet nodePropertyIds, NewIndexDescriptor index ) throws EntityNotFoundException if ( values != null )
{ {
OrderedPropertyValues values = valuesIfPropertiesMatch( values.validate();
state, nodePropertyIds, index, node, after ); state.txState().indexDoUpdateEntry( index.schema(), node.id(), null, values );
if ( values != null ) }
{ });
values.validate();
state.txState().indexDoUpdateEntry( index.schema(), node.id(), null, values );
}
}

@Override
public int propertyId()
{
return after.propertyKeyId();
}
};
} }


public PropertyUpdate remove( DefinedProperty before ) public void onPropertyRemove( KernelStatement state, NodeItem node, DefinedProperty before )
throws EntityNotFoundException
{ {
return new PropertyUpdate() Iterator<NewIndexDescriptor> indexes = getIndexesForProperty( state, before.propertyKeyId() );
{ nodeIndexMatcher.onMatchingSchema( state, indexes, node, before.propertyKeyId(),
@Override index -> {
public void updateIndexIfApplicable( KernelStatement state, NodeItem node, OrderedPropertyValues values = getOrderedPropertyValues( state, node, before, index.schema().getPropertyIds() );
PrimitiveIntSet nodePropertyIds, NewIndexDescriptor index ) throws EntityNotFoundException if ( values != null )
{ {
OrderedPropertyValues values = valuesIfPropertiesMatch( state, nodePropertyIds, index, node, state.txState().indexDoUpdateEntry( index.schema(), node.id(), values, null );
before ); }
if ( values != null ) });
{
state.txState().indexDoUpdateEntry( index.schema(), node.id(), values, null );
}
}

@Override
public int propertyId()
{
return before.propertyKeyId();
}
};
} }


public PropertyUpdate change( DefinedProperty before, DefinedProperty after ) public void onPropertyChange( KernelStatement state, NodeItem node, DefinedProperty before, DefinedProperty after )
throws EntityNotFoundException
{ {
assert before.propertyKeyId() == after.propertyKeyId(); assert before.propertyKeyId() == after.propertyKeyId();
return new PropertyUpdate() Iterator<NewIndexDescriptor> indexes = getIndexesForProperty( state, before.propertyKeyId() );
{ nodeIndexMatcher.onMatchingSchema( state, indexes, node, before.propertyKeyId(),
@Override index -> {
public void updateIndexIfApplicable( KernelStatement state, NodeItem node, int[] indexPropertyIds = index.schema().getPropertyIds();
PrimitiveIntSet nodePropertyIds, NewIndexDescriptor index ) throws EntityNotFoundException
{
int[] indexPropertyIds = index.schema().getPropertyIds();
if ( nodeHasIndexProperties( nodePropertyIds, indexPropertyIds ) )
{
Object[] valuesBefore = new Object[indexPropertyIds.length]; Object[] valuesBefore = new Object[indexPropertyIds.length];
Object[] valuesAfter = new Object[indexPropertyIds.length]; Object[] valuesAfter = new Object[indexPropertyIds.length];
for ( int i = 0; i < indexPropertyIds.length; i++ ) for ( int i = 0; i < indexPropertyIds.length; i++ )
Expand All @@ -173,54 +145,20 @@ public void updateIndexIfApplicable( KernelStatement state, NodeItem node,
} }
state.txState().indexDoUpdateEntry( index.schema(), node.id(), state.txState().indexDoUpdateEntry( index.schema(), node.id(),
OrderedPropertyValues.ofUndefined( valuesBefore ), OrderedPropertyValues.ofUndefined( valuesAfter ) ); OrderedPropertyValues.ofUndefined( valuesBefore ), OrderedPropertyValues.ofUndefined( valuesAfter ) );
} });
}

@Override
public int propertyId()
{
return before.propertyKeyId();
}
};
} }


public void onPropertyChange( KernelStatement state, NodeItem node, PropertyUpdate update ) // HELPERS
throws EntityNotFoundException
{
PrimitiveIntSet nodePropertyIds = null;
Iterator<NewIndexDescriptor> indexes = getIndexesForProperty( state, update.propertyId() );
while ( indexes.hasNext() )
{
NewIndexDescriptor index = indexes.next();
LabelSchemaDescriptor schema = index.schema();
if ( node.labels().contains( schema.getLabelId() ) )
{
if ( nodePropertyIds == null )
{
nodePropertyIds = Primitive.intSet();
nodePropertyIds.addAll( readOps.nodeGetPropertyKeys( state, node ).iterator() );
}

update.updateIndexIfApplicable( state, node, nodePropertyIds, index );
}
}
}


private OrderedPropertyValues valuesIfPropertiesMatch( KernelStatement state, PrimitiveIntSet nodeProperties, private OrderedPropertyValues getOrderedPropertyValues( KernelStatement state, NodeItem node,
NewIndexDescriptor index, NodeItem node ) throws EntityNotFoundException int[] indexPropertyIds )
{ {
return valuesIfPropertiesMatch( state, nodeProperties, index, node, NO_SUCH_PROPERTY ); return getOrderedPropertyValues( state, node, NO_SUCH_PROPERTY, indexPropertyIds );
} }


private OrderedPropertyValues valuesIfPropertiesMatch( KernelStatement state, PrimitiveIntSet nodeProperties, private OrderedPropertyValues getOrderedPropertyValues( KernelStatement state, NodeItem node,
NewIndexDescriptor index, NodeItem node, DefinedProperty changedProperty ) throws EntityNotFoundException DefinedProperty changedProperty, int[] indexPropertyIds )
{ {
int[] indexPropertyIds = index.schema().getPropertyIds();
if ( !nodeHasIndexProperties( nodeProperties, indexPropertyIds, changedProperty.propertyKeyId() ) )
{
return null;
}

DefinedProperty[] values = new DefinedProperty[indexPropertyIds.length]; DefinedProperty[] values = new DefinedProperty[indexPropertyIds.length];
for ( int i = 0; i < values.length; i++ ) for ( int i = 0; i < values.length; i++ )
{ {
Expand All @@ -234,23 +172,19 @@ private OrderedPropertyValues valuesIfPropertiesMatch( KernelStatement state, Pr
} }


private static boolean nodeHasIndexProperties( PrimitiveIntSet nodeProperties, int[] indexPropertyIds ) private static boolean nodeHasIndexProperties( PrimitiveIntSet nodeProperties, int[] indexPropertyIds )
{
return nodeHasIndexProperties( nodeProperties, indexPropertyIds, NO_SUCH_PROPERTY_KEY );
}

private static boolean nodeHasIndexProperties(
PrimitiveIntSet nodeProperties, int[] indexPropertyIds, int changedPropertyId )
{ {
for ( int indexPropertyId : indexPropertyIds ) for ( int indexPropertyId : indexPropertyIds )
{ {
if ( indexPropertyId != changedPropertyId && !nodeProperties.contains( indexPropertyId ) ) if ( !nodeProperties.contains( indexPropertyId ) )
{ {
return false; return false;
} }
} }
return true; return true;
} }


// Lifting this method to the schemaReadOps layer could allow more efficient finding of indexes, by introducing
// suitable maps in the SchemaCache. This can be done when we have a benchmarking reason.
private Iterator<NewIndexDescriptor> getIndexesForProperty( KernelStatement state, int propertyId ) private Iterator<NewIndexDescriptor> getIndexesForProperty( KernelStatement state, int propertyId )
{ {
Iterator<NewIndexDescriptor> allIndexes = Iterator<NewIndexDescriptor> allIndexes =
Expand Down
Expand Up @@ -161,10 +161,10 @@ public void shouldUpdateIndexesOnRemovedLabel() throws EntityNotFoundException
public void shouldNotUpdateIndexesOnChangedIrrelevantProperty() throws EntityNotFoundException public void shouldNotUpdateIndexesOnChangedIrrelevantProperty() throws EntityNotFoundException
{ {
// WHEN // WHEN
indexTxUpdater.onPropertyChange( state, node, indexTxUpdater.add( property( unIndexedPropId, "whAt" ) )); indexTxUpdater.onPropertyAdd( state, node, property( unIndexedPropId, "whAt" ) );
indexTxUpdater.onPropertyChange( state, node, indexTxUpdater.remove( property( unIndexedPropId, "whAt" ) )); indexTxUpdater.onPropertyRemove( state, node, property( unIndexedPropId, "whAt" ) );
indexTxUpdater.onPropertyChange( state, node, indexTxUpdater.change( indexTxUpdater.onPropertyChange( state, node,
property( unIndexedPropId, "whAt" ), property( unIndexedPropId, "whAt2" ) )); property( unIndexedPropId, "whAt" ), property( unIndexedPropId, "whAt2" ) );


// THEN // THEN
verify( txState, times( 0 ) ).indexDoUpdateEntry( any(), anyInt(), any(), any() ); verify( txState, times( 0 ) ).indexDoUpdateEntry( any(), anyInt(), any(), any() );
Expand All @@ -174,7 +174,7 @@ public void shouldNotUpdateIndexesOnChangedIrrelevantProperty() throws EntityNot
public void shouldUpdateIndexesOnAddedProperty() throws EntityNotFoundException public void shouldUpdateIndexesOnAddedProperty() throws EntityNotFoundException
{ {
// WHEN // WHEN
indexTxUpdater.onPropertyChange( state, node, indexTxUpdater.add( property( newPropId, "newHi" ) )); indexTxUpdater.onPropertyAdd( state, node, property( newPropId, "newHi" ) );


// THEN // THEN
verifyIndexUpdate( indexOn2_new.schema(), node.id(), null, values( "newHi" ) ); verifyIndexUpdate( indexOn2_new.schema(), node.id(), null, values( "newHi" ) );
Expand All @@ -186,7 +186,7 @@ public void shouldUpdateIndexesOnAddedProperty() throws EntityNotFoundException
public void shouldUpdateIndexesOnRemovedProperty() throws EntityNotFoundException public void shouldUpdateIndexesOnRemovedProperty() throws EntityNotFoundException
{ {
// WHEN // WHEN
indexTxUpdater.onPropertyChange( state, node, indexTxUpdater.remove( property( propId2, "hi2" ) )); indexTxUpdater.onPropertyRemove( state, node, property( propId2, "hi2" ) );


// THEN // THEN
verifyIndexUpdate( uniqueOn1_2.schema(), node.id(), values( "hi2" ), null ); verifyIndexUpdate( uniqueOn1_2.schema(), node.id(), values( "hi2" ), null );
Expand All @@ -198,8 +198,8 @@ public void shouldUpdateIndexesOnRemovedProperty() throws EntityNotFoundExceptio
public void shouldUpdateIndexesOnChangesProperty() throws EntityNotFoundException public void shouldUpdateIndexesOnChangesProperty() throws EntityNotFoundException
{ {
// WHEN // WHEN
indexTxUpdater.onPropertyChange( state, node, indexTxUpdater.change( indexTxUpdater.onPropertyChange( state, node,
property( propId2, "hi2" ), property( propId2, "new2" ) )); property( propId2, "hi2" ), property( propId2, "new2" ) );


// THEN // THEN
verifyIndexUpdate( uniqueOn1_2.schema(), node.id(), values( "hi2" ), values( "new2" ) ); verifyIndexUpdate( uniqueOn1_2.schema(), node.id(), values( "hi2" ), values( "new2" ) );
Expand Down

0 comments on commit ba41165

Please sign in to comment.