Skip to content

Commit

Permalink
Add support for local time to temporal index
Browse files Browse the repository at this point in the history
  • Loading branch information
OliviaYtterbrink authored and fickludd committed Mar 9, 2018
1 parent e515013 commit 8bc6827
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 20 deletions.
Expand Up @@ -23,6 +23,9 @@
import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.kernel.api.schema.index.IndexDescriptor;

/**
* {@link Layout} for dates.
*/
class DateLayout extends SchemaLayout<DateSchemaKey>
{
public static Layout<DateSchemaKey,NativeSchemaValue> of( IndexDescriptor descriptor )
Expand Down
Expand Up @@ -19,20 +19,18 @@
*/
package org.neo4j.kernel.impl.index.schema;

import org.neo4j.index.internal.gbptree.GBPTree;
import org.neo4j.values.storable.DateValue;
import org.neo4j.values.storable.Value;

import static java.lang.String.format;

/**
* Includes value and entity id (to be able to handle non-unique values). A value can be any {@link String},
* or rather any string that {@link GBPTree} can handle.
* Includes value and entity id (to be able to handle non-unique values). A value can be any {@link DateValue}.
*/
class DateSchemaKey extends NativeSchemaKey
{
static final int SIZE =
Long.BYTES + /* raw value bits */
Long.BYTES + /* epochDay */
Long.BYTES; /* entityId */

long epochDay;
Expand Down
@@ -0,0 +1,164 @@
/*
* 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 org.neo4j.index.internal.gbptree.Layout;
import org.neo4j.io.pagecache.PageCursor;
import org.neo4j.kernel.api.schema.index.IndexDescriptor;

/**
* {@link Layout} for local times.
*/
abstract class LocalTimeLayout extends Layout.Adapter<LocalTimeSchemaKey,NativeSchemaValue>
{
public static Layout<LocalTimeSchemaKey,NativeSchemaValue> of( IndexDescriptor descriptor )
{
return descriptor.type() == IndexDescriptor.Type.UNIQUE ? LocalTimeLayout.UNIQUE : LocalTimeLayout.NON_UNIQUE;
}

private static final long UNIQUE_LAYOUT_IDENTIFIER = Layout.namedIdentifier( "UTlt", NativeSchemaValue.SIZE );
public static LocalTimeLayout UNIQUE = new LocalTimeLayout()
{
@Override
public long identifier()
{
return UNIQUE_LAYOUT_IDENTIFIER;
}

@Override
public int majorVersion()
{
return 0;
}

@Override
public int minorVersion()
{
return 1;
}

@Override
public int compare( LocalTimeSchemaKey o1, LocalTimeSchemaKey o2 )
{
int comparison = o1.compareValueTo( o2 );
if ( comparison == 0 )
{
// This is a special case where we need also compare entityId to support inclusive/exclusive
if ( o1.getCompareId() || o2.getCompareId() )
{
return Long.compare( o1.getEntityId(), o2.getEntityId() );
}
}
return comparison;
}
};

private static final long NON_UNIQUE_LAYOUT_IDENTIFIER = Layout.namedIdentifier( "NTlt", NativeSchemaValue.SIZE );
public static LocalTimeLayout NON_UNIQUE = new LocalTimeLayout()
{
@Override
public long identifier()
{
return NON_UNIQUE_LAYOUT_IDENTIFIER;
}

@Override
public int majorVersion()
{
return 0;
}

@Override
public int minorVersion()
{
return 1;
}

@Override
public int compare( LocalTimeSchemaKey o1, LocalTimeSchemaKey o2 )
{
int comparison = o1.compareValueTo( o2 );
return comparison != 0 ? comparison : Long.compare( o1.getEntityId(), o2.getEntityId() );
}
};

@Override
public LocalTimeSchemaKey newKey()
{
return new LocalTimeSchemaKey();
}

@Override
public LocalTimeSchemaKey copyKey( LocalTimeSchemaKey key, LocalTimeSchemaKey into )
{
into.nanoOfDay = key.nanoOfDay;
into.setEntityId( key.getEntityId() );
into.setCompareId( key.getCompareId() );
return into;
}

@Override
public NativeSchemaValue newValue()
{
return NativeSchemaValue.INSTANCE;
}

@Override
public int keySize( LocalTimeSchemaKey key )
{
return LocalTimeSchemaKey.SIZE;
}

@Override
public int valueSize( NativeSchemaValue value )
{
return NativeSchemaValue.SIZE;
}

@Override
public void writeKey( PageCursor cursor, LocalTimeSchemaKey key )
{
cursor.putLong( key.nanoOfDay );
cursor.putLong( key.getEntityId() );
}

@Override
public void writeValue( PageCursor cursor, NativeSchemaValue value )
{
}

@Override
public void readKey( PageCursor cursor, LocalTimeSchemaKey into, int keySize )
{
into.nanoOfDay = cursor.getLong();
into.setEntityId( cursor.getLong() );
}

@Override
public void readValue( PageCursor cursor, NativeSchemaValue into, int valueSize )
{
}

@Override
public boolean fixedSize()
{
return true;
}
}
@@ -0,0 +1,103 @@
/*
* 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 org.neo4j.values.storable.LocalTimeValue;
import org.neo4j.values.storable.Value;

import static java.lang.String.format;

/**
* Includes value and entity id (to be able to handle non-unique values). A value can be any {@link LocalTimeValue}.
*/
class LocalTimeSchemaKey extends NativeSchemaKey
{
static final int SIZE =
Long.BYTES + /* nanoOfDay */
Long.BYTES; /* entityId */

long nanoOfDay;

@Override
public void from( Value... values )
{
assertValidValue( values ).writeTo( this );
}

private LocalTimeValue assertValidValue( Value... values )
{
if ( values.length > 1 )
{
throw new IllegalArgumentException( "Tried to create composite key with non-composite schema key layout" );
}
if ( values.length < 1 )
{
throw new IllegalArgumentException( "Tried to create key without value" );
}
if ( !(values[0] instanceof LocalTimeValue) )
{
throw new IllegalArgumentException(
"Key layout does only support LocalTimeValue, tried to create key from " + values[0] );
}
return (LocalTimeValue) values[0];
}

@Override
public Value asValue()
{
return LocalTimeValue.localTime( nanoOfDay );
}

@Override
public void initValueAsLowest()
{
nanoOfDay = Long.MIN_VALUE;
}

@Override
public void initValueAsHighest()
{
nanoOfDay = Long.MAX_VALUE;
}

/**
* Compares the value of this key to that of another key.
* This method is expected to be called in scenarios where inconsistent reads may happen (and later retried).
*
* @param other the {@link LocalTimeSchemaKey} to compare to.
* @return comparison against the {@code other} {@link LocalTimeSchemaKey}.
*/
int compareValueTo( LocalTimeSchemaKey other )
{
return Long.compare( nanoOfDay, other.nanoOfDay );
}

@Override
public String toString()
{
return format( "value=%s,entityId=%d,nanoOfDay=%s", asValue(), getEntityId(), nanoOfDay );
}

@Override
public void writeLocalTime( long nanoOfDay )
{
this.nanoOfDay = nanoOfDay;
}
}
Expand Up @@ -230,10 +230,7 @@ static class PartFactory implements TemporalIndexCache.Factory<PartAccessor<?>,
@Override
public PartAccessor<?> newDate() throws IOException
{
TemporalIndexFiles.FileLayout<DateSchemaKey> fileLayout = temporalIndexFiles.date();
ensureIndexExists( fileLayout );
return new PartAccessor<>( pageCache, fs, fileLayout,
recoveryCleanupWorkCollector, monitor, descriptor, indexId, samplingConfig );
return createPartAccessor( temporalIndexFiles.date() );
}

@Override
Expand All @@ -249,9 +246,10 @@ public PartAccessor<?> newDateTimeZoned()
}

@Override
public PartAccessor<?> newTime()
public PartAccessor<?> newTime() throws IOException
{
throw new UnsupportedOperationException( "no comprende" );
return createPartAccessor( temporalIndexFiles.time() );

}

@Override
Expand All @@ -266,12 +264,13 @@ public PartAccessor<?> newDuration()
throw new UnsupportedOperationException( "no comprende" );
}

private <KEY extends NativeSchemaKey> void ensureIndexExists( TemporalIndexFiles.FileLayout<KEY> fileLayout ) throws IOException
private <KEY extends NativeSchemaKey> PartAccessor<KEY> createPartAccessor( TemporalIndexFiles.FileLayout<KEY> fileLayout ) throws IOException
{
if ( !fs.fileExists( fileLayout.indexFile ) )
{
createEmptyIndex( fileLayout );
}
return new PartAccessor<>( pageCache, fs, fileLayout, recoveryCleanupWorkCollector, monitor, descriptor, indexId, samplingConfig );
}

private <KEY extends NativeSchemaKey> void createEmptyIndex( TemporalIndexFiles.FileLayout<KEY> fileLayout ) throws IOException
Expand Down
Expand Up @@ -44,6 +44,7 @@ class TemporalIndexFiles
this.fs = fs;
File indexDirectory = directoryStructure.directoryForIndex( indexId );
dateFileLayout = new FileLayout<>( new File( indexDirectory, "date" ), DateLayout.of( descriptor ), ValueGroup.DATE );
timeFileLayout = new FileLayout<>( new File( indexDirectory, "time" ), LocalTimeLayout.of( descriptor ), ValueGroup.LOCAL_TIME );
}

Iterable<FileLayout> existing()
Expand Down Expand Up @@ -84,6 +85,11 @@ public FileLayout<DateSchemaKey> date()
return dateFileLayout;
}

public FileLayout<DateSchemaKey> time()
{
return timeFileLayout;
}

// .... we will add more explicit accessor methods later

static class FileLayout<KEY extends NativeSchemaKey>
Expand Down
Expand Up @@ -110,7 +110,7 @@ public IndexUpdater newDateTimeZoned() throws IOException
@Override
public IndexUpdater newTime() throws IOException
{
throw new UnsupportedOperationException( "too tired to write" );
return populator.time().newPopulatingUpdater( propertyAccessor );
}

@Override
Expand Down
Expand Up @@ -262,7 +262,7 @@ public PartPopulator<?> newDateTimeZoned() throws IOException
@Override
public PartPopulator<?> newTime() throws IOException
{
throw new UnsupportedOperationException( "not implementedur still" );
return create( temporalIndexFiles.time() );
}

@Override
Expand Down
Expand Up @@ -157,7 +157,7 @@ public TemporalIndexPartReader<?> newDateTimeZoned()
@Override
public TemporalIndexPartReader<?> newTime()
{
throw new UnsupportedOperationException( "Illiterate" );
return accessor.selectOrElse( ValueGroup.LOCAL_TIME, TemporalIndexAccessor.PartAccessor::newReader, null );
}

@Override
Expand Down
Expand Up @@ -103,7 +103,7 @@ public NativeSchemaIndexUpdater<?, NativeSchemaValue> newDateTimeZoned() throws
@Override
public NativeSchemaIndexUpdater<?, NativeSchemaValue> newTime() throws IOException
{
throw new UnsupportedOperationException( "ma-a-da dayo" );
return accessor.time().newUpdater( mode );
}

@Override
Expand Down

0 comments on commit 8bc6827

Please sign in to comment.