Skip to content

Commit

Permalink
Define index capabilities for spatial and temporal
Browse files Browse the repository at this point in the history
Spatial
  ValueCapability: No
  OrderCapability: None

Temporal
  ValueVapability: Yes
  OrderCapability: Ascending
  • Loading branch information
burqen authored and tinwelint committed Mar 26, 2018
1 parent bcee440 commit 9a29de0
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 38 deletions.
Expand Up @@ -30,6 +30,9 @@
*/ */
public interface IndexCapability public interface IndexCapability
{ {
IndexOrder[] ORDER_ASC = {IndexOrder.ASCENDING};
IndexOrder[] ORDER_NONE = new IndexOrder[0];

/** /**
* What possible orderings is this index capable to provide for a query on given combination of {@link ValueGroup}. * What possible orderings is this index capable to provide for a query on given combination of {@link ValueGroup}.
* Ordering of ValueGroup correspond to ordering of related {@link IndexReference#properties()}. * Ordering of ValueGroup correspond to ordering of related {@link IndexReference#properties()}.
Expand All @@ -56,12 +59,17 @@ public interface IndexCapability
*/ */
IndexValueCapability valueCapability( ValueGroup... valueGroups ); IndexValueCapability valueCapability( ValueGroup... valueGroups );


default boolean singleWildcard( ValueGroup[] valueGroups )
{
return valueGroups.length == 1 && valueGroups[0] == ValueGroup.UNKNOWN;
}

IndexCapability NO_CAPABILITY = new IndexCapability() IndexCapability NO_CAPABILITY = new IndexCapability()
{ {
@Override @Override
public IndexOrder[] orderCapability( ValueGroup... valueGroups ) public IndexOrder[] orderCapability( ValueGroup... valueGroups )
{ {
return new IndexOrder[0]; return ORDER_NONE;
} }


@Override @Override
Expand Down
Expand Up @@ -26,6 +26,9 @@
import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.Transaction;
import org.neo4j.internal.kernel.api.exceptions.KernelException;
import org.neo4j.values.storable.CoordinateReferenceSystem;
import org.neo4j.values.storable.DateValue;
import org.neo4j.values.storable.Value; import org.neo4j.values.storable.Value;
import org.neo4j.values.storable.ValueGroup; import org.neo4j.values.storable.ValueGroup;
import org.neo4j.values.storable.Values; import org.neo4j.values.storable.Values;
Expand All @@ -38,6 +41,10 @@
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.neo4j.graphdb.Label.label; import static org.neo4j.graphdb.Label.label;
import static org.neo4j.values.storable.CoordinateReferenceSystem.Cartesian;
import static org.neo4j.values.storable.CoordinateReferenceSystem.Cartesian_3D;
import static org.neo4j.values.storable.CoordinateReferenceSystem.WGS84;
import static org.neo4j.values.storable.CoordinateReferenceSystem.WGS84_3D;
import static org.neo4j.values.storable.Values.stringValue; import static org.neo4j.values.storable.Values.stringValue;


public abstract class NodeValueIndexCursorTestBase<G extends KernelAPIReadTestSupport> public abstract class NodeValueIndexCursorTestBase<G extends KernelAPIReadTestSupport>
Expand All @@ -47,6 +54,7 @@ public abstract class NodeValueIndexCursorTestBase<G extends KernelAPIReadTestSu
private static long boolTrue, num5, num6, num12a, num12b; private static long boolTrue, num5, num6, num12a, num12b;
private static long strOneNoLabel; private static long strOneNoLabel;
private static long joeDalton, williamDalton, jackDalton, averellDalton; private static long joeDalton, williamDalton, jackDalton, averellDalton;
private static long date891, date892, date86;


@Override @Override
void createTestGraph( GraphDatabaseService graphDb ) void createTestGraph( GraphDatabaseService graphDb )
Expand Down Expand Up @@ -101,6 +109,18 @@ void createTestGraph( GraphDatabaseService graphDb )
williamDalton = person( graphDb, "William", "Dalton" ); williamDalton = person( graphDb, "William", "Dalton" );
jackDalton = person( graphDb, "Jack", "Dalton" ); jackDalton = person( graphDb, "Jack", "Dalton" );
averellDalton = person( graphDb, "Averell", "Dalton" ); averellDalton = person( graphDb, "Averell", "Dalton" );
nodeWithProp( graphDb, Values.pointValue( Cartesian, 1, 0 ) ); // Purposely mix order
nodeWithProp( graphDb, Values.pointValue( Cartesian, 0, 0 ) );
nodeWithProp( graphDb, Values.pointValue( Cartesian, 0, 0 ) );
nodeWithProp( graphDb, Values.pointValue( Cartesian, 0, 0 ) );
nodeWithProp( graphDb, Values.pointValue( Cartesian, 0, 1 ) );
nodeWithProp( graphDb, Values.pointValue( Cartesian_3D, 0, 0, 0 ) );
nodeWithProp( graphDb, Values.pointValue( WGS84, 0, 0 ) );
nodeWithProp( graphDb, Values.pointValue( WGS84_3D, 0, 0, 0 ) );
date891 = nodeWithProp( graphDb, DateValue.date( 1989, 3, 24 ) ); // Purposely mix order
date86 = nodeWithProp( graphDb, DateValue.date( 1986, 11, 18 ) );
date892 = nodeWithProp( graphDb, DateValue.date( 1989, 3, 24 ) );

tx.success(); tx.success();
} }
} }
Expand Down Expand Up @@ -181,6 +201,44 @@ public void shouldPerformExactLookup() throws Exception


// then // then
assertFoundNodesAndNoValue( node, uniqueIds, boolTrue ); assertFoundNodesAndNoValue( node, uniqueIds, boolTrue );

// when
valueCapability = index.valueCapability( ValueGroup.GEOMETRY );
read.nodeIndexSeek( index, node, IndexOrder.NONE, IndexQuery.exact( prop, Values.pointValue( Cartesian, 0, 0 ) ) );

// then
assertFoundNodesAndNoValue( node, 3, uniqueIds );

// when
read.nodeIndexSeek( index, node, IndexOrder.NONE, IndexQuery.exact( prop, Values.pointValue( Cartesian_3D, 0, 0, 0 ) ) );

// then
assertFoundNodesAndNoValue( node, 1, uniqueIds );

// when
read.nodeIndexSeek( index, node, IndexOrder.NONE, IndexQuery.exact( prop, Values.pointValue( WGS84, 0, 0 ) ) );

// then
assertFoundNodesAndNoValue( node, 1, uniqueIds );

// when
read.nodeIndexSeek( index, node, IndexOrder.NONE, IndexQuery.exact( prop, Values.pointValue( WGS84_3D, 0, 0, 0 ) ) );

// then
assertFoundNodesAndNoValue( node, 1, uniqueIds );

// when
valueCapability = index.valueCapability( ValueGroup.DATE );
read.nodeIndexSeek( index, node, IndexOrder.NONE, IndexQuery.exact( prop, DateValue.date( 1989, 3, 24 ) ) );

// then
assertFoundNodesAndNoValue( node, 2, uniqueIds );

// when
read.nodeIndexSeek( index, node, IndexOrder.NONE, IndexQuery.exact( prop, DateValue.date( 1986, 11, 18 ) ) );

// then
assertFoundNodesAndNoValue( node, 1, uniqueIds );
} }
} }


Expand Down Expand Up @@ -349,6 +407,84 @@ public void shouldPerformNumericRangeSearch() throws Exception
} }
} }


@Test
public void shouldPerformTemporalRangeSearch() throws KernelException
{
// given
int label = token.nodeLabel( "Node" );
int prop = token.propertyKey( "prop" );
CapableIndexReference index = schemaRead.index( label, prop );
IndexValueCapability temporalCapability = index.valueCapability( ValueGroup.DATE );
try ( NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor();
PrimitiveLongSet uniqueIds = Primitive.longSet() )
{
// when
read.nodeIndexSeek( index, node, IndexOrder.NONE,
IndexQuery.range( prop, DateValue.date( 1986, 11, 18 ), true, DateValue.date( 1989, 3, 24 ), true ) );

// then
assertFoundNodesAndValue( node, uniqueIds, temporalCapability, date86, date891, date892 );

// when
read.nodeIndexSeek( index, node, IndexOrder.NONE,
IndexQuery.range( prop, DateValue.date( 1986, 11, 18 ), true, DateValue.date( 1989, 3, 24 ), false ) );

// then
assertFoundNodesAndValue( node, uniqueIds, temporalCapability, date86 );

// when
read.nodeIndexSeek( index, node, IndexOrder.NONE,
IndexQuery.range( prop, DateValue.date( 1986, 11, 18 ), false, DateValue.date( 1989, 3, 24 ), true ) );

// then
assertFoundNodesAndValue( node, uniqueIds, temporalCapability, date891, date892 );

// when
read.nodeIndexSeek( index, node, IndexOrder.NONE,
IndexQuery.range( prop, DateValue.date( 1986, 11, 18 ), false, DateValue.date( 1989, 3, 24 ), false ) );

// then
assertFoundNodesAndValue( node, uniqueIds, temporalCapability );
}
}

@Test
public void shouldPerformSpatialRangeSearch() throws KernelException
{
// given
int label = token.nodeLabel( "Node" );
int prop = token.propertyKey( "prop" );
CapableIndexReference index = schemaRead.index( label, prop );
IndexValueCapability spatialCapability = index.valueCapability( ValueGroup.GEOMETRY );
try ( NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor();
PrimitiveLongSet uniqueIds = Primitive.longSet() )
{
// when
read.nodeIndexSeek( index, node, IndexOrder.NONE, IndexQuery.range( prop, Cartesian ) );

// then
assertFoundNodesAndValue( node, 5, uniqueIds, spatialCapability );

// when
read.nodeIndexSeek( index, node, IndexOrder.NONE, IndexQuery.range( prop, Cartesian_3D ) );

// then
assertFoundNodesAndValue( node, 1, uniqueIds, spatialCapability );

// when
read.nodeIndexSeek( index, node, IndexOrder.NONE, IndexQuery.range( prop, WGS84 ) );

// then
assertFoundNodesAndValue( node, 1, uniqueIds, spatialCapability );

// when
read.nodeIndexSeek( index, node, IndexOrder.NONE, IndexQuery.range( prop, WGS84_3D ) );

// then
assertFoundNodesAndValue( node, 1, uniqueIds, spatialCapability );
}
}

@Test @Test
public void shouldPerformIndexScan() throws Exception public void shouldPerformIndexScan() throws Exception
{ {
Expand All @@ -365,7 +501,7 @@ public void shouldPerformIndexScan() throws Exception


// then // then
assertThat( node.numberOfProperties(), equalTo( 1 ) ); assertThat( node.numberOfProperties(), equalTo( 1 ) );
assertFoundNodesAndValue( node, 24, uniqueIds, wildcardCapability ); assertFoundNodesAndValue( node, 35, uniqueIds, wildcardCapability );
} }
} }


Expand Down Expand Up @@ -411,6 +547,49 @@ public void shouldRespectOrderCapabilitiesForStrings() throws Exception
} }
} }


@Test
public void shouldRespectOrderCapabilitiesForTemporal() throws KernelException
{
// given
int label = token.nodeLabel( "Node" );
int prop = token.propertyKey( "prop" );
CapableIndexReference index = schemaRead.index( label, prop );
IndexOrder[] orderCapabilities = index.orderCapability( ValueGroup.DATE );
try ( NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor() )
{
for ( IndexOrder orderCapability : orderCapabilities )
{
// when
read.nodeIndexSeek( index, node, orderCapability,
IndexQuery.range( prop, DateValue.date( 1986, 11, 18 ), true, DateValue.date( 1989, 3, 24 ), true ) );

// then
assertFoundNodesInOrder( node, orderCapability );
}
}
}

@Test
public void shouldRespectOrderCapabilitiesForSpatial() throws KernelException
{
// given
int label = token.nodeLabel( "Node" );
int prop = token.propertyKey( "prop" );
CapableIndexReference index = schemaRead.index( label, prop );
IndexOrder[] orderCapabilities = index.orderCapability( ValueGroup.GEOMETRY );
try ( NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor() )
{
for ( IndexOrder orderCapability : orderCapabilities )
{
// when
read.nodeIndexSeek( index, node, orderCapability, IndexQuery.range( prop, CoordinateReferenceSystem.Cartesian ) );

// then
assertFoundNodesInOrder( node, orderCapability );
}
}
}

@Test @Test
public void shouldRespectOrderCapabilitiesForWildcard() throws Exception public void shouldRespectOrderCapabilitiesForWildcard() throws Exception
{ {
Expand Down Expand Up @@ -593,12 +772,12 @@ public void shouldNotFindDeletedNodeInIndexScan() throws Exception
// when // when
tx.dataRead().nodeIndexScan( index, node, IndexOrder.NONE ); tx.dataRead().nodeIndexScan( index, node, IndexOrder.NONE );
assertThat( node.numberOfProperties(), equalTo( 1 ) ); assertThat( node.numberOfProperties(), equalTo( 1 ) );
assertFoundNodesAndValue( node, 24, uniqueIds, wildcardCapability ); assertFoundNodesAndValue( node, 35, uniqueIds, wildcardCapability );


// then // then
tx.dataWrite().nodeDelete( strOne ); tx.dataWrite().nodeDelete( strOne );
tx.dataRead().nodeIndexScan( index, node, IndexOrder.NONE ); tx.dataRead().nodeIndexScan( index, node, IndexOrder.NONE );
assertFoundNodesAndValue( node, 23, uniqueIds, wildcardCapability ); assertFoundNodesAndValue( node, 34, uniqueIds, wildcardCapability );
} }
} }


Expand Down
Expand Up @@ -99,17 +99,14 @@ public IndexCapability getCapability( SchemaIndexDescriptor schemaIndexDescripto
*/ */
private static class NumberIndexCapability implements IndexCapability private static class NumberIndexCapability implements IndexCapability
{ {
private static final IndexOrder[] SUPPORTED_ORDER = {IndexOrder.ASCENDING};
private static final IndexOrder[] EMPTY_ORDER = new IndexOrder[0];

@Override @Override
public IndexOrder[] orderCapability( ValueGroup... valueGroups ) public IndexOrder[] orderCapability( ValueGroup... valueGroups )
{ {
if ( support( valueGroups ) ) if ( support( valueGroups ) )
{ {
return SUPPORTED_ORDER; return ORDER_ASC;
} }
return EMPTY_ORDER; return ORDER_NONE;
} }


@Override @Override
Expand All @@ -126,11 +123,6 @@ public IndexValueCapability valueCapability( ValueGroup... valueGroups )
return IndexValueCapability.NO; return IndexValueCapability.NO;
} }


private boolean singleWildcard( ValueGroup[] valueGroups )
{
return valueGroups.length == 1 && valueGroups[0] == ValueGroup.UNKNOWN;
}

private boolean support( ValueGroup[] valueGroups ) private boolean support( ValueGroup[] valueGroups )
{ {
return valueGroups.length == 1 && valueGroups[0] == ValueGroup.NUMBER; return valueGroups.length == 1 && valueGroups[0] == ValueGroup.NUMBER;
Expand Down
Expand Up @@ -66,12 +66,7 @@ void validateQuery( IndexOrder indexOrder, IndexQuery[] predicates )
throw new UnsupportedOperationException( "Spatial index doesn't handle composite queries" ); throw new UnsupportedOperationException( "Spatial index doesn't handle composite queries" );
} }


if ( indexOrder != IndexOrder.NONE ) CapabilityValidator.validateQuery( SpatialIndexProvider.CAPABILITY, indexOrder, predicates );
{
throw new UnsupportedOperationException(
format( "Tried to query index with unsupported order %s. Supported orders for query %s are %s.", indexOrder, Arrays.toString( predicates ),
IndexOrder.NONE ) );
}
} }


@Override @Override
Expand Down
Expand Up @@ -28,6 +28,8 @@
import org.neo4j.index.internal.gbptree.MetadataMismatchException; import org.neo4j.index.internal.gbptree.MetadataMismatchException;
import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector; import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
import org.neo4j.internal.kernel.api.IndexCapability; import org.neo4j.internal.kernel.api.IndexCapability;
import org.neo4j.internal.kernel.api.IndexOrder;
import org.neo4j.internal.kernel.api.IndexValueCapability;
import org.neo4j.internal.kernel.api.InternalIndexState; import org.neo4j.internal.kernel.api.InternalIndexState;
import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.io.pagecache.PageCache; import org.neo4j.io.pagecache.PageCache;
Expand All @@ -41,10 +43,12 @@
import org.neo4j.kernel.impl.index.schema.config.SpaceFillingCurveSettingsFactory; import org.neo4j.kernel.impl.index.schema.config.SpaceFillingCurveSettingsFactory;
import org.neo4j.kernel.impl.index.schema.config.SpatialIndexSettings; import org.neo4j.kernel.impl.index.schema.config.SpatialIndexSettings;
import org.neo4j.kernel.impl.storemigration.StoreMigrationParticipant; import org.neo4j.kernel.impl.storemigration.StoreMigrationParticipant;
import org.neo4j.values.storable.ValueGroup;


public class SpatialIndexProvider extends IndexProvider public class SpatialIndexProvider extends IndexProvider
{ {
public static final String KEY = "spatial"; public static final String KEY = "spatial";
static final IndexCapability CAPABILITY = new SpatialIndexCapability();
private static final Descriptor SPATIAL_PROVIDER_DESCRIPTOR = new Descriptor( KEY, "1.0" ); private static final Descriptor SPATIAL_PROVIDER_DESCRIPTOR = new Descriptor( KEY, "1.0" );


private final PageCache pageCache; private final PageCache pageCache;
Expand Down Expand Up @@ -162,7 +166,7 @@ public InternalIndexState getInitialState( long indexId, SchemaIndexDescriptor d
@Override @Override
public IndexCapability getCapability( SchemaIndexDescriptor indexDescriptor ) public IndexCapability getCapability( SchemaIndexDescriptor indexDescriptor )
{ {
return IndexCapability.NO_CAPABILITY; return CAPABILITY;
} }


@Override @Override
Expand All @@ -172,4 +176,24 @@ public StoreMigrationParticipant storeMigrationParticipant( FileSystemAbstractio
// Migration should happen in the combined layer for the time being. // Migration should happen in the combined layer for the time being.
return StoreMigrationParticipant.NOT_PARTICIPATING; return StoreMigrationParticipant.NOT_PARTICIPATING;
} }

/**
* For single property spatial queries capabilities are
* Order: NONE (can not provide results in ordering)
* Value: NO (can not provide exact value)
*/
private static class SpatialIndexCapability implements IndexCapability
{
@Override
public IndexOrder[] orderCapability( ValueGroup... valueGroups )
{
return ORDER_NONE;
}

@Override
public IndexValueCapability valueCapability( ValueGroup... valueGroups )
{
return IndexValueCapability.NO;
}
}
} }

0 comments on commit 9a29de0

Please sign in to comment.