Skip to content

Commit

Permalink
Fast lookup of index updaters by labels
Browse files Browse the repository at this point in the history
  • Loading branch information
fickludd committed Mar 27, 2017
1 parent 56c62f5 commit 0103583
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 10 deletions.
Expand Up @@ -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;

/**
Expand All @@ -37,17 +43,26 @@ public final class IndexMap implements Cloneable
private final Map<Long, IndexProxy> indexesById;
private final Map<LabelSchemaDescriptor,IndexProxy> indexesByDescriptor;
private final Map<LabelSchemaDescriptor,Long> indexIdsByDescriptor;
private final PrimitiveLongObjectMap<Set<LabelSchemaDescriptor>> descriptorsByLabel;

public IndexMap()
{
this( new HashMap<>(), new HashMap<>(), new HashMap<>() );
}

private IndexMap( Map<Long, IndexProxy> indexesById, Map<LabelSchemaDescriptor,IndexProxy> indexesByDescriptor, Map<LabelSchemaDescriptor,Long> indexIdsByDescriptor )
private IndexMap(
Map<Long, IndexProxy> indexesById,
Map<LabelSchemaDescriptor,IndexProxy> indexesByDescriptor,
Map<LabelSchemaDescriptor,Long> 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 )
Expand All @@ -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<LabelSchemaDescriptor> descriptors = descriptorsByLabel.get( schema.getLabelId() );
descriptors.remove( schema );
if ( descriptors.isEmpty() )
{
indexesByDescriptor.remove( removedProxy.getDescriptor().schema() );
descriptorsByLabel.remove( schema.getLabelId() );
}

return removedProxy;
}

Expand Down Expand Up @@ -114,6 +142,21 @@ public Iterator<LabelSchemaDescriptor> descriptors()
return indexesByDescriptor.keySet().iterator();
}

public Iterator<LabelSchemaDescriptor> descriptorsForLabels( long[] labels )
{
if ( labels.length == 1 )
{
return descriptorsByLabel.get( labels[0] ).iterator();
}

List<LabelSchemaDescriptor> descriptors = new ArrayList<>();
for ( long label : labels )
{
descriptors.addAll( descriptorsByLabel.get( label ) );
}
return descriptors.iterator();
}

public Iterator<Long> indexIds()
{
return indexesById.keySet().iterator();
Expand All @@ -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 );
}
}
Expand Up @@ -61,11 +61,16 @@ class IndexUpdaterMap implements AutoCloseable, Iterable<IndexUpdater>
this.updaterMap = new HashMap<>();
}

Iterable<IndexUpdaterWithSchema> updaters()
/**
* Returns IndexUpdaters that are relevant to the input labels
* @param labels set of labels
* @return The IndexUpdaters
*/
Iterable<IndexUpdaterWithSchema> updatersForLabels( long[] labels )
{
return Iterables.map(
IndexUpdaterWithSchema::new,
indexMap::descriptors );
() -> indexMap.descriptorsForLabels( labels ) );
}

IndexUpdater getUpdater( LabelSchemaDescriptor descriptor )
Expand Down
Expand Up @@ -468,7 +468,7 @@ private void apply( IndexUpdates updates, IndexUpdateMode updateMode )
for ( NodeUpdates update : updates )
{
for ( IndexEntryUpdate<IndexUpdaterMap.IndexUpdaterWithSchema> indexUpdate :
update.forIndexKeys( updaterMap.updaters() ) )
update.forIndexKeys( updaterMap.updatersForLabels( update.labelsAffected() ) ) )
{
indexUpdate.indexKey().process( indexUpdate );
}
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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 )
{
Expand Down
Expand Up @@ -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;
Expand All @@ -40,8 +47,6 @@

public class IndexUpdaterMapTest
{
private final long transactionId = 42L;

private IndexMap indexMap;

private IndexProxy indexProxy1;
Expand All @@ -51,6 +56,9 @@ public class IndexUpdaterMapTest
private IndexProxy indexProxy2;
private NewIndexDescriptor indexDescriptor2;

private IndexProxy indexProxy3;
private NewIndexDescriptor indexDescriptor3;

private IndexUpdaterMap updaterMap;

@Before
Expand All @@ -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 );
}

Expand Down Expand Up @@ -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<IndexUpdaterMap.IndexUpdaterWithSchema> 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<IndexUpdaterMap.IndexUpdaterWithSchema> 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<LabelSchemaDescriptor> updaters =
Iterables.stream(
updaterMap.updatersForLabels(
new long[]{labelIdFor2and3} ) )
.map( IndexUpdaterMap.IndexUpdaterWithSchema::schema )
.collect( Collectors.toSet() );

// then
assertThat( updaters, contains( indexDescriptor2.schema(), indexDescriptor3.schema() ) );
}
}

0 comments on commit 0103583

Please sign in to comment.