Skip to content

Commit

Permalink
Implement contains and suffix seek for native string index
Browse files Browse the repository at this point in the history
This is done by filtering with IndexQuery just outside of SeekCursor (GBPTree) on deserialized value.
  • Loading branch information
burqen authored and tinwelint committed Mar 12, 2018
1 parent 4692b52 commit 5ff481b
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 26 deletions.
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2002-2018 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.index.schema;

import java.io.IOException;
import java.util.Collection;

import org.neo4j.cursor.RawCursor;
import org.neo4j.index.internal.gbptree.Hit;
import org.neo4j.internal.kernel.api.IndexQuery;
import org.neo4j.values.storable.Value;

class FilteringNativeHitIndexProgressor<KEY extends NativeSchemaKey, VALUE extends NativeSchemaValue> extends NativeHitIndexProgressor<KEY,VALUE>
{
private final IndexQuery[] filter;

FilteringNativeHitIndexProgressor( RawCursor<Hit<KEY,VALUE>,IOException> seeker, NodeValueClient client,
Collection<RawCursor<Hit<KEY,VALUE>,IOException>> toRemoveFromOnClose, IndexQuery[] filter )
{
super( seeker, client, toRemoveFromOnClose );
this.filter = filter;
}

@Override
boolean acceptValue( Value[] values )
{
for ( int i = 0; i < values.length; i++ )
{
if ( !filter[i].acceptsValue( values[i] ) )
{
return false;
}
}
return true;
}
}
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2002-2018 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.index.schema;

import java.io.IOException;
import java.util.Collection;

import org.neo4j.cursor.RawCursor;
import org.neo4j.index.internal.gbptree.Hit;
import org.neo4j.internal.kernel.api.IndexQuery;
import org.neo4j.values.storable.Value;

class FilteringNumberHitIterator<KEY extends NativeSchemaKey, VALUE extends NativeSchemaValue> extends NumberHitIterator<KEY,VALUE>
{
private final IndexQuery[] filters;

FilteringNumberHitIterator( RawCursor<Hit<KEY,VALUE>,IOException> seeker,
Collection<RawCursor<Hit<KEY,VALUE>,IOException>> toRemoveFromWhenExhausted, IndexQuery[] filters )
{
super( seeker, toRemoveFromWhenExhausted );
this.filters = filters;
}

@Override
boolean acceptValue( Value value )
{
return filters[0].acceptsValue( value );
}
}
Expand Up @@ -52,7 +52,7 @@ public boolean next()
{ {
KEY key = seeker.get().key(); KEY key = seeker.get().key();
Value[] values = extractValues( key ); Value[] values = extractValues( key );
if ( client.acceptNode( key.getEntityId(), values ) ) if ( acceptValue( values ) && client.acceptNode( key.getEntityId(), values ) )
{ {
return true; return true;
} }
Expand All @@ -65,6 +65,11 @@ public boolean next()
} }
} }


boolean acceptValue( Value[] values )
{
return true;
}

Value[] extractValues( KEY key ) Value[] extractValues( KEY key )
{ {
return client.needsValues() ? new Value[]{ key.asValue()} : null; return client.needsValues() ? new Value[]{ key.asValue()} : null;
Expand Down
Expand Up @@ -113,7 +113,7 @@ public PrimitiveLongResourceIterator query( IndexQuery... predicates )
KEY treeKeyFrom = layout.newKey(); KEY treeKeyFrom = layout.newKey();
KEY treeKeyTo = layout.newKey(); KEY treeKeyTo = layout.newKey();


initializeRangeForQuery( treeKeyFrom, treeKeyTo, predicates ); boolean needFilter = initializeRangeForQuery( treeKeyFrom, treeKeyTo, predicates );
if ( isBackwardsSeek( treeKeyFrom, treeKeyTo ) ) if ( isBackwardsSeek( treeKeyFrom, treeKeyTo ) )
{ {
return PrimitiveLongResourceCollections.emptyIterator(); return PrimitiveLongResourceCollections.emptyIterator();
Expand All @@ -123,14 +123,26 @@ public PrimitiveLongResourceIterator query( IndexQuery... predicates )
{ {
RawCursor<Hit<KEY,VALUE>,IOException> seeker = tree.seek( treeKeyFrom, treeKeyTo ); RawCursor<Hit<KEY,VALUE>,IOException> seeker = tree.seek( treeKeyFrom, treeKeyTo );
openSeekers.add( seeker ); openSeekers.add( seeker );
return new NumberHitIterator<>( seeker, openSeekers ); return getNumberHitIterator( seeker, needFilter, predicates );
} }
catch ( IOException e ) catch ( IOException e )
{ {
throw new UncheckedIOException( e ); throw new UncheckedIOException( e );
} }
} }


private PrimitiveLongResourceIterator getNumberHitIterator( RawCursor<Hit<KEY,VALUE>,IOException> seeker, boolean needFilter, IndexQuery[] predicates )
{
if ( needFilter )
{
return new FilteringNumberHitIterator<>( seeker, openSeekers, predicates );
}
else
{
return new NumberHitIterator<>( seeker, openSeekers );
}
}

@Override @Override
public void query( IndexProgressor.NodeValueClient cursor, IndexOrder indexOrder, IndexQuery... predicates ) public void query( IndexProgressor.NodeValueClient cursor, IndexOrder indexOrder, IndexQuery... predicates )
{ {
Expand All @@ -139,18 +151,21 @@ public void query( IndexProgressor.NodeValueClient cursor, IndexOrder indexOrder
KEY treeKeyFrom = layout.newKey(); KEY treeKeyFrom = layout.newKey();
KEY treeKeyTo = layout.newKey(); KEY treeKeyTo = layout.newKey();


initializeRangeForQuery( treeKeyFrom, treeKeyTo, predicates ); boolean needFilter = initializeRangeForQuery( treeKeyFrom, treeKeyTo, predicates );
startSeekForInitializedRange( cursor, treeKeyFrom, treeKeyTo, predicates ); startSeekForInitializedRange( cursor, treeKeyFrom, treeKeyTo, predicates, needFilter );
} }


@Override @Override
public abstract boolean hasFullValuePrecision( IndexQuery... predicates ); public abstract boolean hasFullValuePrecision( IndexQuery... predicates );


abstract void validateQuery( IndexOrder indexOrder, IndexQuery[] predicates ); abstract void validateQuery( IndexOrder indexOrder, IndexQuery[] predicates );


abstract void initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] predicates ); /**
* @return true if query results from seek will need to be filtered through the predicates, else false
*/
abstract boolean initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] predicates );


void startSeekForInitializedRange( IndexProgressor.NodeValueClient client, KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] query ) void startSeekForInitializedRange( IndexProgressor.NodeValueClient client, KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] query, boolean needFilter )
{ {
if ( isBackwardsSeek( treeKeyFrom, treeKeyTo ) ) if ( isBackwardsSeek( treeKeyFrom, treeKeyTo ) )
{ {
Expand All @@ -160,7 +175,7 @@ void startSeekForInitializedRange( IndexProgressor.NodeValueClient client, KEY t
try try
{ {
RawCursor<Hit<KEY,VALUE>,IOException> seeker = makeIndexSeeker( treeKeyFrom, treeKeyTo ); RawCursor<Hit<KEY,VALUE>,IOException> seeker = makeIndexSeeker( treeKeyFrom, treeKeyTo );
IndexProgressor hitProgressor = new NativeHitIndexProgressor<>( seeker, client, openSeekers ); IndexProgressor hitProgressor = getIndexProgressor( seeker, client, needFilter, query );
client.initialize( descriptor, hitProgressor, query ); client.initialize( descriptor, hitProgressor, query );
} }
catch ( IOException e ) catch ( IOException e )
Expand All @@ -176,6 +191,19 @@ RawCursor<Hit<KEY,VALUE>,IOException> makeIndexSeeker( KEY treeKeyFrom, KEY tree
return seeker; return seeker;
} }


private IndexProgressor getIndexProgressor( RawCursor<Hit<KEY,VALUE>,IOException> seeker, IndexProgressor.NodeValueClient client, boolean needFilter,
IndexQuery[] query )
{
if ( needFilter )
{
return new FilteringNativeHitIndexProgressor<>( seeker, client, openSeekers, query );
}
else
{
return new NativeHitIndexProgressor<>( seeker, client, openSeekers );
}
}

private boolean isBackwardsSeek( KEY treeKeyFrom, KEY treeKeyTo ) private boolean isBackwardsSeek( KEY treeKeyFrom, KEY treeKeyTo )
{ {
return layout.compare( treeKeyFrom, treeKeyTo ) > 0; return layout.compare( treeKeyFrom, treeKeyTo ) > 0;
Expand Down
Expand Up @@ -28,6 +28,7 @@
import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.collection.primitive.PrimitiveLongResourceIterator;
import org.neo4j.cursor.RawCursor; import org.neo4j.cursor.RawCursor;
import org.neo4j.index.internal.gbptree.Hit; import org.neo4j.index.internal.gbptree.Hit;
import org.neo4j.values.storable.Value;


/** /**
* Wraps number key/value results in a {@link PrimitiveLongIterator}. * Wraps number key/value results in a {@link PrimitiveLongIterator}.
Expand Down Expand Up @@ -55,9 +56,12 @@ protected boolean fetchNext()
{ {
try try
{ {
if ( seeker.next() ) while ( seeker.next() )
{ {
return next( seeker.get().key().getEntityId() ); if ( acceptValue( seeker.get().key().asValue() ) )
{
return next( seeker.get().key().getEntityId() );
}
} }
return false; return false;
} }
Expand All @@ -67,6 +71,11 @@ protected boolean fetchNext()
} }
} }


boolean acceptValue( Value value )
{
return true;
}

private void ensureCursorClosed() throws IOException private void ensureCursorClosed() throws IOException
{ {
if ( !closed ) if ( !closed )
Expand Down
Expand Up @@ -49,7 +49,7 @@ void validateQuery( IndexOrder indexOrder, IndexQuery[] predicates )
} }


@Override @Override
void initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] predicates ) boolean initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] predicates )
{ {
IndexQuery predicate = predicates[0]; IndexQuery predicate = predicates[0];
switch ( predicate.type() ) switch ( predicate.type() )
Expand All @@ -71,6 +71,7 @@ void initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] predi
default: default:
throw new IllegalArgumentException( "IndexQuery of type " + predicate.type() + " is not supported." ); throw new IllegalArgumentException( "IndexQuery of type " + predicate.type() + " is not supported." );
} }
return false;
} }


private void initToForRange( NumberRangePredicate rangePredicate, KEY treeKeyTo ) private void initToForRange( NumberRangePredicate rangePredicate, KEY treeKeyTo )
Expand Down
Expand Up @@ -73,7 +73,7 @@ void validateQuery( IndexOrder indexOrder, IndexQuery[] predicates )
} }


@Override @Override
void initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] predicates ) boolean initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] predicates )
{ {
throw new UnsupportedOperationException( "Cannot initialize 1D range in multidimensional spatial index reader" ); throw new UnsupportedOperationException( "Cannot initialize 1D range in multidimensional spatial index reader" );
} }
Expand Down Expand Up @@ -111,7 +111,7 @@ private void startSeekForExists( IndexProgressor.NodeValueClient client, IndexQu
KEY treeKeyTo = layout.newKey(); KEY treeKeyTo = layout.newKey();
treeKeyFrom.initAsLowest(); treeKeyFrom.initAsLowest();
treeKeyTo.initAsHighest(); treeKeyTo.initAsHighest();
startSeekForInitializedRange( client, treeKeyFrom, treeKeyTo, predicates ); startSeekForInitializedRange( client, treeKeyFrom, treeKeyTo, predicates, false );
} }


private void startSeekForExact( IndexProgressor.NodeValueClient client, Value value, IndexQuery... predicates ) private void startSeekForExact( IndexProgressor.NodeValueClient client, Value value, IndexQuery... predicates )
Expand All @@ -120,7 +120,7 @@ private void startSeekForExact( IndexProgressor.NodeValueClient client, Value va
KEY treeKeyTo = layout.newKey(); KEY treeKeyTo = layout.newKey();
treeKeyFrom.from( Long.MIN_VALUE, value ); treeKeyFrom.from( Long.MIN_VALUE, value );
treeKeyTo.from( Long.MAX_VALUE, value ); treeKeyTo.from( Long.MAX_VALUE, value );
startSeekForInitializedRange( client, treeKeyFrom, treeKeyTo, predicates ); startSeekForInitializedRange( client, treeKeyFrom, treeKeyTo, predicates, false );
} }


private void startSeekForRange( IndexProgressor.NodeValueClient client, GeometryRangePredicate rangePredicate, IndexQuery[] query ) private void startSeekForRange( IndexProgressor.NodeValueClient client, GeometryRangePredicate rangePredicate, IndexQuery[] query )
Expand Down Expand Up @@ -158,7 +158,7 @@ private void startSeekForRange( IndexProgressor.NodeValueClient client, Geometry
} }


@Override @Override
void startSeekForInitializedRange( IndexProgressor.NodeValueClient client, KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] query ) void startSeekForInitializedRange( IndexProgressor.NodeValueClient client, KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] query, boolean needFilter )
{ {
if ( layout.compare( treeKeyFrom, treeKeyTo ) > 0 ) if ( layout.compare( treeKeyFrom, treeKeyTo ) > 0 )
{ {
Expand Down
Expand Up @@ -52,34 +52,35 @@ void validateQuery( IndexOrder indexOrder, IndexQuery[] predicates )
} }


@Override @Override
void initializeRangeForQuery( StringSchemaKey treeKeyFrom, StringSchemaKey treeKeyTo, IndexQuery[] predicates ) boolean initializeRangeForQuery( StringSchemaKey treeKeyFrom, StringSchemaKey treeKeyTo, IndexQuery[] predicates )
{ {
IndexQuery predicate = predicates[0]; IndexQuery predicate = predicates[0];
switch ( predicate.type() ) switch ( predicate.type() )
{ {
case exists: case exists:
treeKeyFrom.initAsLowest(); treeKeyFrom.initAsLowest();
treeKeyTo.initAsHighest(); treeKeyTo.initAsHighest();
break; return false;
case exact: case exact:
ExactPredicate exactPredicate = (ExactPredicate) predicate; ExactPredicate exactPredicate = (ExactPredicate) predicate;
treeKeyFrom.from( Long.MIN_VALUE, exactPredicate.value() ); treeKeyFrom.from( Long.MIN_VALUE, exactPredicate.value() );
treeKeyTo.from( Long.MAX_VALUE, exactPredicate.value() ); treeKeyTo.from( Long.MAX_VALUE, exactPredicate.value() );
break; return false;
case rangeString: case rangeString:
StringRangePredicate rangePredicate = (StringRangePredicate)predicate; StringRangePredicate rangePredicate = (StringRangePredicate)predicate;
initFromForRange( rangePredicate, treeKeyFrom ); initFromForRange( rangePredicate, treeKeyFrom );
initToForRange( rangePredicate, treeKeyTo ); initToForRange( rangePredicate, treeKeyTo );
break; return false;
case stringPrefix: case stringPrefix:
StringPrefixPredicate prefixPredicate = (StringPrefixPredicate) predicate; StringPrefixPredicate prefixPredicate = (StringPrefixPredicate) predicate;
treeKeyFrom.initAsPrefixLow( prefixPredicate.prefix() ); treeKeyFrom.initAsPrefixLow( prefixPredicate.prefix() );
treeKeyTo.initAsPrefixHigh( prefixPredicate.prefix() ); treeKeyTo.initAsPrefixHigh( prefixPredicate.prefix() );
break; return false;
// case stringSuffix: case stringSuffix:
// break; case stringContains:
// case stringContains: treeKeyFrom.initAsLowest();
// break; treeKeyTo.initAsHighest();
return true;
default: default:
throw new IllegalArgumentException( "IndexQuery of type " + predicate.type() + " is not supported." ); throw new IllegalArgumentException( "IndexQuery of type " + predicate.type() + " is not supported." );
} }
Expand Down
Expand Up @@ -49,7 +49,7 @@ protected void validateQuery( IndexOrder indexOrder, IndexQuery[] predicates )
} }


@Override @Override
protected void initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] predicates ) protected boolean initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] predicates )
{ {
IndexQuery predicate = predicates[0]; IndexQuery predicate = predicates[0];
switch ( predicate.type() ) switch ( predicate.type() )
Expand All @@ -67,6 +67,7 @@ protected void initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQue
default: default:
throw new IllegalArgumentException( "IndexQuery of type " + predicate.type() + " is not supported." ); throw new IllegalArgumentException( "IndexQuery of type " + predicate.type() + " is not supported." );
} }
return false; // no filtering
} }


@Override @Override
Expand Down
Expand Up @@ -122,7 +122,10 @@ public PrimitiveLongResourceIterator query( IndexQuery... predicates ) throws In
return numberReader.query( predicates ); return numberReader.query( predicates );
} }


if ( predicate instanceof StringRangePredicate ) if ( predicate instanceof StringRangePredicate ||
predicate instanceof StringPrefixPredicate ||
predicate instanceof StringSuffixPredicate ||
predicate instanceof StringContainsPredicate )
{ {
return stringReader.query( predicate ); return stringReader.query( predicate );
} }
Expand Down

0 comments on commit 5ff481b

Please sign in to comment.