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 ) ), localDateTimeArray( SIZE_LOCAL_DATE_TIME, 336, i -> random.randomValues().nextLocalDateTimeArrayRaw( i, i ) ),
durationArray( SIZE_DURATION, 144, i -> random.randomValues().nextDurationArrayRaw( i, i ) ), durationArray( SIZE_DURATION, 144, i -> random.randomValues().nextDurationArrayRaw( i, i ) ),
periodArray( SIZE_DURATION, 144, i -> random.randomValues().nextPeriodArrayRaw( i, i ) ), periodArray( SIZE_DURATION, 144, i -> random.randomValues().nextPeriodArrayRaw( i, i ) ),
cartesianPointArray( SIZE_GEOMETRY, 504, i -> random.randomValues().nextCartesianPointArray( i, i ).asObjectCopy() ), cartesianPointArray( SIZE_GEOMETRY, 168, i -> random.randomValues().nextCartesianPointArray( i, i ).asObjectCopy() ),
cartesian3DPointArray( SIZE_GEOMETRY, 504, i -> random.randomValues().nextCartesian3DPointArray( i, i ).asObjectCopy() ), cartesian3DPointArray( SIZE_GEOMETRY, 126, i -> random.randomValues().nextCartesian3DPointArray( i, i ).asObjectCopy() ),
geographicPointArray( SIZE_GEOMETRY, 504, i -> random.randomValues().nextGeographicPointArray( i, i ).asObjectCopy() ), geographicPointArray( SIZE_GEOMETRY, 168, i -> random.randomValues().nextGeographicPointArray( i, i ).asObjectCopy() ),
geographic3DPointArray( SIZE_GEOMETRY, 504, i -> random.randomValues().nextGeographic3DPointArray( i, i ).asObjectCopy() ); geographic3DPointArray( SIZE_GEOMETRY, 126, i -> random.randomValues().nextGeographic3DPointArray( i, i ).asObjectCopy() );


private final int singleArrayEntrySize; private final int singleArrayEntrySize;
private final DynamicValueGenerator generator; 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 // 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_HEADER = 3; /* 2b tableId and 22b code */
public static final int SIZE_GEOMETRY = Long.BYTES; /* rawValueBits */ 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 */ public static final int SIZE_ZONED_DATE_TIME = Long.BYTES + /* epochSecond */
Integer.BYTES + /* nanoOfSecond */ Integer.BYTES + /* nanoOfSecond */
Integer.BYTES; /* timeZone */ 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_NUMBER_DOUBLE = Long.BYTES; /* raw value bits */
public static final int SIZE_ARRAY_LENGTH = Short.BYTES; public static final int SIZE_ARRAY_LENGTH = Short.BYTES;
static final int BIGGEST_REASONABLE_ARRAY_LENGTH = PAGE_SIZE / 2 / SIZE_NUMBER_BYTE; 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 TRUE = 1;
static final long FALSE = 0; static final long FALSE = 0;
Expand Down Expand Up @@ -474,22 +476,22 @@ public void writePoint( CoordinateReferenceSystem crs, double[] coordinate )
{ {
if ( !isArray ) if ( !isArray )
{ {
updateCurve( crs.getTable().getTableId(), crs.getCode() ); updateCurve( crs );
setType( Types.GEOMETRY ).write( this, spaceFillingCurve.derivedValueFor( coordinate ) ); setType( Types.GEOMETRY ).write( this, spaceFillingCurve.derivedValueFor( coordinate ), coordinate );
} }
else else
{ {
if ( currentArrayOffset == 0 ) if ( currentArrayOffset == 0 )
{ {
updateCurve( crs.getTable().getTableId(), crs.getCode() ); updateCurve( crs );
} }
else if ( this.long1 != crs.getTable().getTableId() || this.long2 != crs.getCode() ) else if ( this.long1 != crs.getTable().getTableId() || this.long2 != crs.getCode() )
{ {
throw new IllegalStateException( format( throw new IllegalStateException( format(
"Tried to assign a geometry array containing different coordinate reference systems, first:%s, violating:%s at array position:%d", "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 ) ); 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. " + "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" ); "As such it's unexpected that this key state thinks that it's holds state for an array" );
} }
updateCurve( crs.getTable().getTableId(), crs.getCode() ); updateCurve( crs );
setType( Types.GEOMETRY ).write( this, derivedValue ); setType( Types.GEOMETRY ).write( this, derivedValue, EMPTY_COORDINATE_ARRAY );
this.inclusion = inclusion; 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 ) if ( this.long1 != tableId || this.long2 != code )
{ {
long1 = tableId; long1 = tableId;
long2 = code; long2 = code;
long3 = crs.getDimension();
spaceFillingCurve = settings.forCrs( tableId, code, true ); 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 ) GenericLayout( int numberOfSlots, IndexSpecificSpaceFillingCurveSettingsCache spatialSettings )
{ {
super( "NSIL", 0, 3 ); super( "NSIL", 0, 4 );
this.numberOfSlots = numberOfSlots; this.numberOfSlots = numberOfSlots;
this.spatialSettings = spatialSettings; this.spatialSettings = spatialSettings;
} }
Expand Down
Expand Up @@ -21,18 +21,22 @@


import java.util.Arrays; import java.util.Arrays;


import org.neo4j.graphdb.spatial.Point;
import org.neo4j.io.pagecache.PageCursor; import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.values.storable.CoordinateReferenceSystem;
import org.neo4j.values.storable.PointValue; import org.neo4j.values.storable.PointValue;
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.ValueWriter; import org.neo4j.values.storable.ValueWriter;
import org.neo4j.values.storable.Values;


import static java.lang.String.format; 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.put;
import static org.neo4j.kernel.impl.index.schema.GeometryType.putCrs; import static org.neo4j.kernel.impl.index.schema.GeometryType.putCrs;
import static org.neo4j.kernel.impl.index.schema.GeometryType.readCrs; import static org.neo4j.kernel.impl.index.schema.GeometryType.readCrs;
import static org.neo4j.values.storable.Values.NO_VALUE;


class GeometryArrayType extends AbstractArrayType<PointValue> class GeometryArrayType extends AbstractArrayType<PointValue>
{ {
Expand All @@ -53,7 +57,8 @@ class GeometryArrayType extends AbstractArrayType<PointValue>
@Override @Override
int valueSize( GenericKeyState state ) 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 @Override
Expand All @@ -63,27 +68,49 @@ void copyValue( GenericKeyState to, GenericKeyState from, int length )
System.arraycopy( from.long0Array, 0, to.long0Array, 0, length ); System.arraycopy( from.long0Array, 0, to.long0Array, 0, length );
to.long1 = from.long1; to.long1 = from.long1;
to.long2 = from.long2; 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 @Override
void initializeArray( GenericKeyState key, int length, ValueWriter.ArrayType arrayType ) void initializeArray( GenericKeyState key, int length, ValueWriter.ArrayType arrayType )
{ {
key.long0Array = ensureBigEnough( key.long0Array, length ); 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 @Override
Value asValue( GenericKeyState state ) 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 @Override
void putValue( PageCursor cursor, GenericKeyState state ) void putValue( PageCursor cursor, GenericKeyState state )
{ {
putCrs( cursor, state.long1, state.long2 ); putCrs( cursor, state.long1, state.long2, state.long3 );
putArray( cursor, state, ( c, k, i ) -> put( c, state.long0Array[i] ) ); int dimensions = dimensions( state );
putArray( cursor, state, ( c, k, i ) -> put( c, state.long0Array[i], state.long3, state.long1Array, i * dimensions ) );
} }


@Override @Override
Expand All @@ -96,18 +123,40 @@ boolean readValue( PageCursor cursor, int size, GenericKeyState into )
@Override @Override
String toString( GenericKeyState state ) 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 ) ) ); state.long1, state.long2, Arrays.toString( Arrays.copyOf( state.long0Array, state.arrayLength ) ) );
} }


private static boolean readGeometryArrayItem( PageCursor cursor, GenericKeyState into ) 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; return true;
} }


void write( GenericKeyState state, int offset, long derivedSpaceFillingCurveValue ) void write( GenericKeyState state, int offset, long derivedSpaceFillingCurveValue, double[] coordinates )
{ {
state.long0Array[offset] = derivedSpaceFillingCurveValue; 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.