Skip to content

Commit

Permalink
Points are indexed w/ coordinates in btree-1.0 index
Browse files Browse the repository at this point in the history
Allowing index to recreate actual PointValue instances
and so will avoid round-tripping to the property store.
  • Loading branch information
tinwelint committed Oct 1, 2018
1 parent 07b41cd commit 578fca2
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 46 deletions.
Expand Up @@ -360,10 +360,10 @@ private enum NamedDynamicValueGenerator
localDateTimeArray( SIZE_LOCAL_DATE_TIME, 336, i -> random.randomValues().nextLocalDateTimeArrayRaw( i, i ) ),
durationArray( SIZE_DURATION, 144, i -> random.randomValues().nextDurationArrayRaw( i, i ) ),
periodArray( SIZE_DURATION, 144, i -> random.randomValues().nextPeriodArrayRaw( i, i ) ),
cartesianPointArray( SIZE_GEOMETRY, 504, i -> random.randomValues().nextCartesianPointArray( i, i ).asObjectCopy() ),
cartesian3DPointArray( SIZE_GEOMETRY, 504, i -> random.randomValues().nextCartesian3DPointArray( i, i ).asObjectCopy() ),
geographicPointArray( SIZE_GEOMETRY, 504, i -> random.randomValues().nextGeographicPointArray( i, i ).asObjectCopy() ),
geographic3DPointArray( SIZE_GEOMETRY, 504, i -> random.randomValues().nextGeographic3DPointArray( i, i ).asObjectCopy() );
cartesianPointArray( SIZE_GEOMETRY, 168, i -> random.randomValues().nextCartesianPointArray( i, i ).asObjectCopy() ),
cartesian3DPointArray( SIZE_GEOMETRY, 126, i -> random.randomValues().nextCartesian3DPointArray( i, i ).asObjectCopy() ),
geographicPointArray( SIZE_GEOMETRY, 168, i -> random.randomValues().nextGeographicPointArray( i, i ).asObjectCopy() ),
geographic3DPointArray( SIZE_GEOMETRY, 126, i -> random.randomValues().nextGeographic3DPointArray( i, i ).asObjectCopy() );

private final int singleArrayEntrySize;
private final DynamicValueGenerator generator;
Expand Down
Expand Up @@ -52,6 +52,7 @@ public class GenericKeyState extends TemporalValueWriterAdapter<RuntimeException
// TODO also put this in Type enum
public static final int SIZE_GEOMETRY_HEADER = 3; /* 2b tableId and 22b code */
public static final int SIZE_GEOMETRY = Long.BYTES; /* rawValueBits */
static final int SIZE_GEOMETRY_COORDINATE = Long.BYTES; /* one coordinate */
public static final int SIZE_ZONED_DATE_TIME = Long.BYTES + /* epochSecond */
Integer.BYTES + /* nanoOfSecond */
Integer.BYTES; /* timeZone */
Expand All @@ -76,6 +77,7 @@ public class GenericKeyState extends TemporalValueWriterAdapter<RuntimeException
public static final int SIZE_NUMBER_DOUBLE = Long.BYTES; /* raw value bits */
public static final int SIZE_ARRAY_LENGTH = Short.BYTES;
static final int BIGGEST_REASONABLE_ARRAY_LENGTH = PAGE_SIZE / 2 / SIZE_NUMBER_BYTE;
private static final double[] EMPTY_COORDINATE_ARRAY = new double[0];

static final long TRUE = 1;
static final long FALSE = 0;
Expand Down Expand Up @@ -474,22 +476,22 @@ public void writePoint( CoordinateReferenceSystem crs, double[] coordinate )
{
if ( !isArray )
{
updateCurve( crs.getTable().getTableId(), crs.getCode() );
setType( Types.GEOMETRY ).write( this, spaceFillingCurve.derivedValueFor( coordinate ) );
updateCurve( crs );
setType( Types.GEOMETRY ).write( this, spaceFillingCurve.derivedValueFor( coordinate ), coordinate );
}
else
{
if ( currentArrayOffset == 0 )
{
updateCurve( crs.getTable().getTableId(), crs.getCode() );
updateCurve( crs );
}
else if ( this.long1 != crs.getTable().getTableId() || this.long2 != crs.getCode() )
{
throw new IllegalStateException( format(
"Tried to assign a geometry array containing different coordinate reference systems, first:%s, violating:%s at array position:%d",
CoordinateReferenceSystem.get( (int) long1, (int) long2 ), crs, currentArrayOffset ) );
}
Types.GEOMETRY_ARRAY.write( this, currentArrayOffset++, spaceFillingCurve.derivedValueFor( coordinate ) );
Types.GEOMETRY_ARRAY.write( this, currentArrayOffset++, spaceFillingCurve.derivedValueFor( coordinate ), coordinate );
}
}

Expand All @@ -501,17 +503,20 @@ void writePointDerived( CoordinateReferenceSystem crs, long derivedValue, Native
"from a queried range and each sub-range written to separate keys. " +
"As such it's unexpected that this key state thinks that it's holds state for an array" );
}
updateCurve( crs.getTable().getTableId(), crs.getCode() );
setType( Types.GEOMETRY ).write( this, derivedValue );
updateCurve( crs );
setType( Types.GEOMETRY ).write( this, derivedValue, EMPTY_COORDINATE_ARRAY );
this.inclusion = inclusion;
}

private void updateCurve( int tableId, int code )
private void updateCurve( CoordinateReferenceSystem crs )
{
int tableId = crs.getTable().getTableId();
int code = crs.getCode();
if ( this.long1 != tableId || this.long2 != code )
{
long1 = tableId;
long2 = code;
long3 = crs.getDimension();
spaceFillingCurve = settings.forCrs( tableId, code, true );
}
}
Expand Down
Expand Up @@ -29,7 +29,7 @@ class GenericLayout extends IndexLayout<CompositeGenericKey,NativeIndexValue>

GenericLayout( int numberOfSlots, IndexSpecificSpaceFillingCurveSettingsCache spatialSettings )
{
super( "NSIL", 0, 3 );
super( "NSIL", 0, 4 );
this.numberOfSlots = numberOfSlots;
this.spatialSettings = spatialSettings;
}
Expand Down
Expand Up @@ -21,18 +21,22 @@

import java.util.Arrays;

import org.neo4j.graphdb.spatial.Point;
import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.values.storable.CoordinateReferenceSystem;
import org.neo4j.values.storable.PointValue;
import org.neo4j.values.storable.Value;
import org.neo4j.values.storable.ValueGroup;
import org.neo4j.values.storable.ValueWriter;
import org.neo4j.values.storable.Values;

import static java.lang.String.format;
import static org.neo4j.kernel.impl.index.schema.GenericKeyState.toNonNegativeShortExact;
import static org.neo4j.collection.PrimitiveLongCollections.EMPTY_LONG_ARRAY;
import static org.neo4j.kernel.impl.index.schema.GeometryType.assertHasCoordinates;
import static org.neo4j.kernel.impl.index.schema.GeometryType.dimensions;
import static org.neo4j.kernel.impl.index.schema.GeometryType.put;
import static org.neo4j.kernel.impl.index.schema.GeometryType.putCrs;
import static org.neo4j.kernel.impl.index.schema.GeometryType.readCrs;
import static org.neo4j.values.storable.Values.NO_VALUE;

class GeometryArrayType extends AbstractArrayType<PointValue>
{
Expand All @@ -53,7 +57,8 @@ class GeometryArrayType extends AbstractArrayType<PointValue>
@Override
int valueSize( GenericKeyState state )
{
return GenericKeyState.SIZE_GEOMETRY_HEADER + arrayKeySize( state, GenericKeyState.SIZE_GEOMETRY );
return GenericKeyState.SIZE_GEOMETRY_HEADER +
arrayKeySize( state, GenericKeyState.SIZE_GEOMETRY + dimensions( state ) * GenericKeyState.SIZE_GEOMETRY_COORDINATE );
}

@Override
Expand All @@ -63,27 +68,49 @@ void copyValue( GenericKeyState to, GenericKeyState from, int length )
System.arraycopy( from.long0Array, 0, to.long0Array, 0, length );
to.long1 = from.long1;
to.long2 = from.long2;
to.long3 = from.long3;
int dimensions = dimensions( from );
to.long1Array = ensureBigEnough( to.long1Array, dimensions * length );
System.arraycopy( from.long1Array, 0, to.long1Array, 0, dimensions * length );
to.spaceFillingCurve = from.spaceFillingCurve;
}

@Override
void initializeArray( GenericKeyState key, int length, ValueWriter.ArrayType arrayType )
{
key.long0Array = ensureBigEnough( key.long0Array, length );
// plain long1 for tableId
// plain long2 for code

// Since this method is called when serializing a PointValue into the key state, the CRS and number of dimensions
// are unknown at this point. Instead key.long1Array will be initialized lazily upon observing the first array item,
// because that's when we first will know that information.
if ( length == 0 && key.long1Array == null )
{
// There's this special case where we're initializing an empty geometry array and so the long1Array
// won't be initialized at all. Therefore we're preemptively making sure it's at least not null.
key.long1Array = EMPTY_LONG_ARRAY;
}
}

@Override
Value asValue( GenericKeyState state )
{
return NO_VALUE;
assertHasCoordinates( state.long3, state.long1Array );
CoordinateReferenceSystem crs = CoordinateReferenceSystem.get( (int) state.long1, (int) state.long2 );
Point[] points = new Point[state.arrayLength];
int dimensions = dimensions( state );
for ( int i = 0; i < points.length; i++ )
{
points[i] = GeometryType.asValue( state, crs, dimensions * i );
}
return Values.pointArray( points );
}

@Override
void putValue( PageCursor cursor, GenericKeyState state )
{
putCrs( cursor, state.long1, state.long2 );
putArray( cursor, state, ( c, k, i ) -> put( c, state.long0Array[i] ) );
putCrs( cursor, state.long1, state.long2, state.long3 );
int dimensions = dimensions( state );
putArray( cursor, state, ( c, k, i ) -> put( c, state.long0Array[i], state.long3, state.long1Array, i * dimensions ) );
}

@Override
Expand All @@ -96,18 +123,40 @@ boolean readValue( PageCursor cursor, int size, GenericKeyState into )
@Override
String toString( GenericKeyState state )
{
return format( "Geometry[tableId:%d, code:%d, rawValues:%s]",
return format( "GeometryArray[tableId:%d, code:%d, rawValues:%s]",
state.long1, state.long2, Arrays.toString( Arrays.copyOf( state.long0Array, state.arrayLength ) ) );
}

private static boolean readGeometryArrayItem( PageCursor cursor, GenericKeyState into )
{
into.long0Array[into.currentArrayOffset++] = cursor.getLong();
into.long0Array[into.currentArrayOffset] = cursor.getLong();
int dimensions = dimensions( into );
if ( into.currentArrayOffset == 0 )
{
// Initialize the coordinates array lazily because we don't know the dimension count
// when initializeArray is called, only afterwards when the header have been read.
into.long1Array = ensureBigEnough( into.long1Array, dimensions * into.arrayLength );
}
for ( int i = 0, offset = into.currentArrayOffset * dimensions; i < dimensions; i++ )
{
into.long1Array[offset + i] = cursor.getLong();
}
into.currentArrayOffset++;
return true;
}

void write( GenericKeyState state, int offset, long derivedSpaceFillingCurveValue )
void write( GenericKeyState state, int offset, long derivedSpaceFillingCurveValue, double[] coordinates )
{
state.long0Array[offset] = derivedSpaceFillingCurveValue;
if ( offset == 0 )
{
int dimensions = coordinates.length;
state.long1Array = ensureBigEnough( state.long1Array, dimensions * state.arrayLength );
state.long3 = dimensions;
}
for ( int i = 0, base = dimensions( state ) * offset; i < coordinates.length; i++ )
{
state.long1Array[base + i] = Double.doubleToLongBits( coordinates[i] );
}
}
}

0 comments on commit 578fca2

Please sign in to comment.