diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexMap.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexMap.java index 0ba01e35a42c..098fe6691bf2 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexMap.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexMap.java @@ -19,11 +19,17 @@ */ package org.neo4j.kernel.impl.api.index; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.BiConsumer; +import org.neo4j.collection.primitive.Primitive; +import org.neo4j.collection.primitive.PrimitiveLongObjectMap; import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor; /** @@ -37,17 +43,26 @@ public final class IndexMap implements Cloneable private final Map indexesById; private final Map indexesByDescriptor; private final Map indexIdsByDescriptor; + private final PrimitiveLongObjectMap> descriptorsByLabel; public IndexMap() { this( new HashMap<>(), new HashMap<>(), new HashMap<>() ); } - private IndexMap( Map indexesById, Map indexesByDescriptor, Map indexIdsByDescriptor ) + private IndexMap( + Map indexesById, + Map indexesByDescriptor, + Map indexIdsByDescriptor ) { this.indexesById = indexesById; this.indexesByDescriptor = indexesByDescriptor; this.indexIdsByDescriptor = indexIdsByDescriptor; + this.descriptorsByLabel = Primitive.longObjectMap(); + for ( LabelSchemaDescriptor schema : indexesByDescriptor.keySet() ) + { + addDescriptorToLabelLookup( schema ); + } } public IndexProxy getIndexProxy( long indexId ) @@ -67,18 +82,31 @@ public long getIndexId( LabelSchemaDescriptor descriptor ) public void putIndexProxy( long indexId, IndexProxy indexProxy ) { + LabelSchemaDescriptor schema = indexProxy.getDescriptor().schema(); indexesById.put( indexId, indexProxy ); - indexesByDescriptor.put( indexProxy.getDescriptor().schema(), indexProxy ); - indexIdsByDescriptor.put( indexProxy.getDescriptor().schema(), indexId ); + indexesByDescriptor.put( schema, indexProxy ); + indexIdsByDescriptor.put( schema, indexId ); + addDescriptorToLabelLookup( schema ); } public IndexProxy removeIndexProxy( long indexId ) { IndexProxy removedProxy = indexesById.remove( indexId ); - if ( null != removedProxy ) + if ( removedProxy == null ) + { + return null; + } + + LabelSchemaDescriptor schema = removedProxy.getDescriptor().schema(); + indexesByDescriptor.remove( schema ); + + Set descriptors = descriptorsByLabel.get( schema.getLabelId() ); + descriptors.remove( schema ); + if ( descriptors.isEmpty() ) { - indexesByDescriptor.remove( removedProxy.getDescriptor().schema() ); + descriptorsByLabel.remove( schema.getLabelId() ); } + return removedProxy; } @@ -114,6 +142,21 @@ public Iterator descriptors() return indexesByDescriptor.keySet().iterator(); } + public Iterator descriptorsForLabels( long[] labels ) + { + if ( labels.length == 1 ) + { + return descriptorsByLabel.get( labels[0] ).iterator(); + } + + List descriptors = new ArrayList<>(); + for ( long label : labels ) + { + descriptors.addAll( descriptorsByLabel.get( label ) ); + } + return descriptors.iterator(); + } + public Iterator indexIds() { return indexesById.keySet().iterator(); @@ -123,4 +166,13 @@ public int size() { return indexesById.size(); } + + private void addDescriptorToLabelLookup( LabelSchemaDescriptor schema ) + { + if ( !descriptorsByLabel.containsKey( schema.getLabelId() ) ) + { + descriptorsByLabel.put( schema.getLabelId(), new HashSet<>() ); + } + descriptorsByLabel.get( schema.getLabelId() ).add( schema ); + } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexUpdaterMap.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexUpdaterMap.java index bcdf2200f890..3b824c863bbb 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexUpdaterMap.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexUpdaterMap.java @@ -61,11 +61,16 @@ class IndexUpdaterMap implements AutoCloseable, Iterable this.updaterMap = new HashMap<>(); } - Iterable updaters() + /** + * Returns IndexUpdaters that are relevant to the input labels + * @param labels set of labels + * @return The IndexUpdaters + */ + Iterable updatersForLabels( long[] labels ) { return Iterables.map( IndexUpdaterWithSchema::new, - indexMap::descriptors ); + () -> indexMap.descriptorsForLabels( labels ) ); } IndexUpdater getUpdater( LabelSchemaDescriptor descriptor ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexingService.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexingService.java index a9071d54007d..a5f5a986df20 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexingService.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/IndexingService.java @@ -468,7 +468,7 @@ private void apply( IndexUpdates updates, IndexUpdateMode updateMode ) for ( NodeUpdates update : updates ) { for ( IndexEntryUpdate indexUpdate : - update.forIndexKeys( updaterMap.updaters() ) ) + update.forIndexKeys( updaterMap.updatersForLabels( update.labelsAffected() ) ) ) { indexUpdate.indexKey().process( indexUpdate ); } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/NodeUpdates.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/NodeUpdates.java index 2e97aa5cdba9..4ee315fc2c9b 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/NodeUpdates.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/NodeUpdates.java @@ -27,6 +27,7 @@ import org.neo4j.collection.primitive.PrimitiveIntIterator; import org.neo4j.collection.primitive.PrimitiveIntObjectMap; import org.neo4j.collection.primitive.PrimitiveIntSet; +import org.neo4j.helpers.SortedLongArrayUtil; import org.neo4j.helpers.collection.Iterables; import org.neo4j.kernel.api.index.IndexEntryUpdate; import org.neo4j.kernel.api.properties.DefinedProperty; @@ -128,6 +129,11 @@ public final long getNodeId() return nodeId; } + public long[] labelsAffected() + { + return SortedLongArrayUtil.union( labelsBefore, labelsAfter ); + } + @Override public void onProperty( int propertyId, Object value ) { diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/IndexUpdaterMapTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/IndexUpdaterMapTest.java index ff0a724fb5aa..3416795ab870 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/IndexUpdaterMapTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/index/IndexUpdaterMapTest.java @@ -23,13 +23,20 @@ import org.junit.Test; import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.neo4j.helpers.collection.Iterables; import org.neo4j.kernel.api.index.IndexUpdater; +import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptorFactory; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -40,8 +47,6 @@ public class IndexUpdaterMapTest { - private final long transactionId = 42L; - private IndexMap indexMap; private IndexProxy indexProxy1; @@ -51,6 +56,9 @@ public class IndexUpdaterMapTest private IndexProxy indexProxy2; private NewIndexDescriptor indexDescriptor2; + private IndexProxy indexProxy3; + private NewIndexDescriptor indexDescriptor3; + private IndexUpdaterMap updaterMap; @Before @@ -70,6 +78,12 @@ public void before() throws IOException when( indexProxy2.getDescriptor() ).thenReturn( indexDescriptor2 ); when( indexProxy2.newUpdater( any( IndexUpdateMode.class ) ) ).thenReturn( indexUpdater2 ); + indexProxy3 = mock( IndexProxy.class ); + indexDescriptor3 = NewIndexDescriptorFactory.forLabel( 5, 7, 8 ); + IndexUpdater indexUpdater3 = mock( IndexUpdater.class ); + when( indexProxy3.getDescriptor() ).thenReturn( indexDescriptor3 ); + when( indexProxy3.newUpdater( any( IndexUpdateMode.class ) ) ).thenReturn( indexUpdater3 ); + updaterMap = new IndexUpdaterMap( indexMap, IndexUpdateMode.ONLINE ); } @@ -146,4 +160,65 @@ public void shouldCloseAllUpdaters() throws Exception assertTrue( "updater map must be empty", updaterMap.isEmpty() ); } + @Test + public void shouldGetUpdatersForLabel() + { + // given + indexMap.putIndexProxy( 0, indexProxy1 ); + indexMap.putIndexProxy( 1, indexProxy2 ); + + // when + List updaters = Iterables.asList( + updaterMap.updatersForLabels( + new long[]{ + indexDescriptor1.schema().getLabelId() + } ) ); + + // then + assertThat( updaters, hasSize( 1 ) ); + assertThat( updaters.get( 0 ).schema(), equalTo( indexDescriptor1.schema() ) ); + } + + @Test + public void shouldGetUpdatersForLabels() + { + // given + indexMap.putIndexProxy( 0, indexProxy1 ); + indexMap.putIndexProxy( 1, indexProxy2 ); + + // when + List updaters = Iterables.asList( + updaterMap.updatersForLabels( + new long[]{ + indexDescriptor1.schema().getLabelId(), + indexDescriptor2.schema().getLabelId() + } ) ); + + // then + assertThat( updaters, hasSize( 2 ) ); + assertThat( updaters.get( 0 ).schema(), equalTo( indexDescriptor1.schema() ) ); + assertThat( updaters.get( 1 ).schema(), equalTo( indexDescriptor2.schema() ) ); + } + + @Test + public void shouldGetMultipleUpdatersForLabels() + { + // given + indexMap.putIndexProxy( 0, indexProxy1 ); + indexMap.putIndexProxy( 1, indexProxy2 ); + indexMap.putIndexProxy( 2, indexProxy3 ); + + // when + int labelIdFor2and3 = indexDescriptor2.schema().getLabelId(); + + Set updaters = + Iterables.stream( + updaterMap.updatersForLabels( + new long[]{labelIdFor2and3} ) ) + .map( IndexUpdaterMap.IndexUpdaterWithSchema::schema ) + .collect( Collectors.toSet() ); + + // then + assertThat( updaters, contains( indexDescriptor2.schema(), indexDescriptor3.schema() ) ); + } }