org.neo4j.kernel.impl.api.index.IndexMap Maven / Gradle / Ivy
/*
* Copyright (c) 2018-2020 "Graph Foundation,"
* Graph Foundation, Inc. [https://graphfoundation.org]
*
* This file is part of ONgDB.
*
* ONgDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* Copyright (c) 2002-2020 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.kernel.impl.api.index;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import org.neo4j.collection.primitive.Primitive;
import org.neo4j.collection.primitive.PrimitiveIntIterator;
import org.neo4j.collection.primitive.PrimitiveIntObjectMap;
import org.neo4j.collection.primitive.PrimitiveIntSet;
import org.neo4j.collection.primitive.PrimitiveLongCollections;
import org.neo4j.collection.primitive.PrimitiveLongIterator;
import org.neo4j.collection.primitive.PrimitiveLongObjectMap;
import org.neo4j.internal.kernel.api.schema.SchemaDescriptor;
/**
* Bundles various mappings to IndexProxy. Used by IndexingService via IndexMapReference.
*
* IndexingService is expected to either make a copy before making any changes or update this
* while being single threaded.
*/
public final class IndexMap implements Cloneable
{
private final PrimitiveLongObjectMap indexesById;
private final Map indexesByDescriptor;
private final Map indexIdsByDescriptor;
private final PrimitiveIntObjectMap> descriptorsByLabel;
private final PrimitiveIntObjectMap> descriptorsByProperty;
public IndexMap()
{
this( Primitive.longObjectMap(), new HashMap<>(), new HashMap<>() );
}
IndexMap( PrimitiveLongObjectMap indexesById )
{
this( indexesById, indexesByDescriptor( indexesById ), indexIdsByDescriptor( indexesById ) );
}
private IndexMap(
PrimitiveLongObjectMap indexesById,
Map indexesByDescriptor,
Map indexIdsByDescriptor )
{
this.indexesById = indexesById;
this.indexesByDescriptor = indexesByDescriptor;
this.indexIdsByDescriptor = indexIdsByDescriptor;
this.descriptorsByLabel = Primitive.intObjectMap();
this.descriptorsByProperty = Primitive.intObjectMap();
for ( SchemaDescriptor schema : indexesByDescriptor.keySet() )
{
addDescriptorToLookups( schema );
}
}
public IndexProxy getIndexProxy( long indexId )
{
return indexesById.get( indexId );
}
public IndexProxy getIndexProxy( SchemaDescriptor descriptor )
{
return indexesByDescriptor.get( descriptor );
}
public long getIndexId( SchemaDescriptor descriptor )
{
return indexIdsByDescriptor.get( descriptor );
}
public void putIndexProxy( long indexId, IndexProxy indexProxy )
{
SchemaDescriptor schema = indexProxy.getDescriptor().schema();
indexesById.put( indexId, indexProxy );
indexesByDescriptor.put( schema, indexProxy );
indexIdsByDescriptor.put( schema, indexId );
addDescriptorToLookups( schema );
}
public IndexProxy removeIndexProxy( long indexId )
{
IndexProxy removedProxy = indexesById.remove( indexId );
if ( removedProxy == null )
{
return null;
}
SchemaDescriptor schema = removedProxy.getDescriptor().schema();
indexesByDescriptor.remove( schema );
removeFromLookup( schema.keyId(), schema, descriptorsByLabel );
for ( int propertyId : schema.getPropertyIds() )
{
removeFromLookup( propertyId, schema, descriptorsByProperty );
}
return removedProxy;
}
public void forEachIndexProxy( BiConsumer consumer )
{
indexesById.visitEntries( ( key, indexProxy ) ->
{
consumer.accept( key, indexProxy);
return false;
} );
}
public Iterable getAllIndexProxies()
{
return indexesById.values();
}
/**
* Get all indexes that would be affected by changes in the input labels and/or properties. The returned
* indexes are guaranteed to contain all affected indexes, but might also contain unaffected indexes as
* we cannot provide matching without checking unaffected properties for composite indexes.
*
* @param changedLabels set of labels that have changed
* @param unchangedLabels set of labels that are unchanged
* @param properties set of properties
* @return set of SchemaDescriptors describing the potentially affected indexes
*/
public Set getRelatedIndexes(
long[] changedLabels, long[] unchangedLabels, PrimitiveIntSet properties )
{
if ( changedLabels.length == 1 && properties.isEmpty() )
{
Set descriptors = descriptorsByLabel.get( (int)changedLabels[0] );
return descriptors == null ? Collections.emptySet() : descriptors;
}
if ( changedLabels.length == 0 && properties.size() == 1 )
{
return getDescriptorsByProperties( unchangedLabels, properties );
}
Set descriptors = extractIndexesByLabels( changedLabels );
descriptors.addAll( getDescriptorsByProperties( unchangedLabels, properties ) );
return descriptors;
}
@Override
public IndexMap clone()
{
return new IndexMap( clonePrimitiveMap( indexesById ), cloneMap( indexesByDescriptor ),
cloneMap( indexIdsByDescriptor ) );
}
public Iterator descriptors()
{
return indexesByDescriptor.keySet().iterator();
}
public PrimitiveLongIterator indexIds()
{
return indexesById.iterator();
}
public int size()
{
return indexesById.size();
}
// HELPERS
private Map cloneMap( Map map )
{
Map shallowCopy = new HashMap<>( map.size() );
shallowCopy.putAll( map );
return shallowCopy;
}
private PrimitiveLongObjectMap clonePrimitiveMap( PrimitiveLongObjectMap indexesById )
{
return PrimitiveLongCollections.copy( indexesById );
}
private void addDescriptorToLookups( SchemaDescriptor schema )
{
addToLookup( schema.keyId(), schema, descriptorsByLabel );
for ( int propertyId : schema.getPropertyIds() )
{
addToLookup( propertyId, schema, descriptorsByProperty );
}
}
private void addToLookup(
int key,
SchemaDescriptor schema,
PrimitiveIntObjectMap> lookup )
{
Set descriptors = lookup.get( key );
if ( descriptors == null )
{
descriptors = new HashSet<>();
lookup.put( key, descriptors );
}
descriptors.add( schema );
}
private void removeFromLookup(
int key,
SchemaDescriptor schema,
PrimitiveIntObjectMap> lookup )
{
Set descriptors = lookup.get( key );
descriptors.remove( schema );
if ( descriptors.isEmpty() )
{
lookup.remove( key );
}
}
private static Map indexesByDescriptor( PrimitiveLongObjectMap indexesById )
{
Map map = new HashMap<>();
for ( IndexProxy proxy : indexesById.values() )
{
map.put( proxy.schema(), proxy );
}
return map;
}
private static Map indexIdsByDescriptor( PrimitiveLongObjectMap indexesById )
{
Map map = new HashMap<>();
indexesById.visitEntries( ( key, indexProxy ) ->
{
map.put( indexProxy.schema(), key );
return false;
} );
return map;
}
/**
* Get descriptors affected by changed properties. Implementation checks whether doing
* the lookup using the unchanged labels or the changed properties given the smallest final
* set of indexes, and chooses the best way.
*
* @param unchangedLabels set of labels that are unchanged
* @param properties set of properties that have changed
* @return set of SchemaDescriptors describing the potentially affected indexes
*/
private Set getDescriptorsByProperties(
long[] unchangedLabels,
PrimitiveIntSet properties )
{
int nIndexesForLabels = countIndexesByLabels( unchangedLabels );
int nIndexesForProperties = countIndexesByProperties( properties );
if ( nIndexesForLabels == 0 || nIndexesForProperties == 0 )
{
return Collections.emptySet();
}
if ( nIndexesForLabels < nIndexesForProperties )
{
return extractIndexesByLabels( unchangedLabels );
}
else
{
return extractIndexesByProperties( properties );
}
}
private Set extractIndexesByLabels( long[] labels )
{
Set set = new HashSet<>();
for ( long label : labels )
{
Set forLabel = descriptorsByLabel.get( (int) label );
if ( forLabel != null )
{
set.addAll( forLabel );
}
}
return set;
}
private int countIndexesByLabels( long[] labels )
{
int count = 0;
for ( long label : labels )
{
Set forLabel = descriptorsByLabel.get( (int) label );
if ( forLabel != null )
{
count += forLabel.size();
}
}
return count;
}
private Set extractIndexesByProperties( PrimitiveIntSet properties )
{
Set set = new HashSet<>();
for ( PrimitiveIntIterator iterator = properties.iterator(); iterator.hasNext(); )
{
Set forProperty = descriptorsByProperty.get( iterator.next() );
if ( forProperty != null )
{
set.addAll( forProperty );
}
}
return set;
}
private int countIndexesByProperties( PrimitiveIntSet properties )
{
int count = 0;
for ( PrimitiveIntIterator iterator = properties.iterator(); iterator.hasNext(); )
{
Set forProperty = descriptorsByProperty.get( iterator.next() );
if ( forProperty != null )
{
count += forProperty.size();
}
}
return count;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy