Skip to content

Commit

Permalink
Add support for local datetime 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 8bc6827 commit 9c16c6f
Show file tree
Hide file tree
Showing 9 changed files with 339 additions and 11 deletions.
@@ -0,0 +1,167 @@
/*
* 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 date times.
*/
abstract class LocalDateTimeLayout extends Layout.Adapter<LocalDateTimeSchemaKey,NativeSchemaValue>
{
public static Layout<LocalDateTimeSchemaKey,NativeSchemaValue> of( IndexDescriptor descriptor )
{
return descriptor.type() == IndexDescriptor.Type.UNIQUE ? LocalDateTimeLayout.UNIQUE : LocalDateTimeLayout.NON_UNIQUE;
}

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

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

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

@Override
public int compare( LocalDateTimeSchemaKey o1, LocalDateTimeSchemaKey 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( "NTld", NativeSchemaValue.SIZE );
public static LocalDateTimeLayout NON_UNIQUE = new LocalDateTimeLayout()
{
@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( LocalDateTimeSchemaKey o1, LocalDateTimeSchemaKey o2 )
{
int comparison = o1.compareValueTo( o2 );
return comparison != 0 ? comparison : Long.compare( o1.getEntityId(), o2.getEntityId() );
}
};

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

@Override
public LocalDateTimeSchemaKey copyKey( LocalDateTimeSchemaKey key, LocalDateTimeSchemaKey into )
{
into.epochSecond = key.epochSecond;
into.nanoOfSecond = key.nanoOfSecond;
into.setEntityId( key.getEntityId() );
into.setCompareId( key.getCompareId() );
return into;
}

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

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

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

@Override
public void writeKey( PageCursor cursor, LocalDateTimeSchemaKey key )
{
cursor.putLong( key.epochSecond );
cursor.putInt( key.nanoOfSecond );
cursor.putLong( key.getEntityId() );
}

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

@Override
public void readKey( PageCursor cursor, LocalDateTimeSchemaKey into, int keySize )
{
into.epochSecond = cursor.getLong();
into.nanoOfSecond = cursor.getInt();
into.setEntityId( cursor.getLong() );
}

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

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

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 LocalDateTimeValue}.
*/
class LocalDateTimeSchemaKey extends ValueWriter.Adapter<RuntimeException> implements NativeSchemaKey
{
static final int SIZE =
Long.BYTES + /* epochSecond */
Integer.BYTES + /* nanoOfSecond */
Long.BYTES; /* entityId */

private long entityId;
private boolean compareId;

int nanoOfSecond;
long epochSecond;

public void setCompareId( boolean compareId )
{
this.compareId = compareId;
}

public boolean getCompareId()
{
return compareId;
}

@Override
public long getEntityId()
{
return entityId;
}

@Override
public void setEntityId( long entityId )
{
this.entityId = entityId;
}

@Override
public void from( long entityId, Value... values )
{
this.entityId = entityId;
compareId = false;
assertValidValue( values ).writeTo( this );
}

private LocalDateTimeValue 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 LocalDateTimeValue) )
{
throw new IllegalArgumentException(
"Key layout does only support LocalDateTimeValue, tried to create key from " + values[0] );
}
return (LocalDateTimeValue) values[0];
}

@Override
public String propertiesAsString()
{
return asValue().toString();
}

@Override
public Value asValue()
{
return LocalDateTimeValue.localDateTime( epochSecond, nanoOfSecond );
}

@Override
public void initAsLowest()
{
epochSecond = Long.MIN_VALUE;
nanoOfSecond = Integer.MIN_VALUE;
entityId = Long.MIN_VALUE;
compareId = true;
}

@Override
public void initAsHighest()
{
epochSecond = Long.MAX_VALUE;
nanoOfSecond = Integer.MAX_VALUE;
entityId = Long.MAX_VALUE;
compareId = true;
}

/**
* 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 LocalDateTimeSchemaKey} to compare to.
* @return comparison against the {@code other} {@link LocalDateTimeSchemaKey}.
*/
int compareValueTo( LocalDateTimeSchemaKey other )
{
int compare = Long.compare( epochSecond, other.epochSecond );
if ( compare == 0 )
{
compare = Integer.compare( nanoOfSecond, other.nanoOfSecond );
}
return compare;
}

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

@Override
public void writeLocalDateTime( long epochSecond, int nano )
{
this.nanoOfSecond = nano;
this.epochSecond = epochSecond;
}
}
Expand Up @@ -236,7 +236,7 @@ public PartAccessor<?> newDate() throws IOException
@Override @Override
public PartAccessor<?> newDateTime() public PartAccessor<?> newDateTime()
{ {
throw new UnsupportedOperationException( "no comprende" ); return createPartAccessor( temporalIndexFiles.dateTime() );
} }


@Override @Override
Expand Down
Expand Up @@ -33,9 +33,9 @@ class TemporalIndexFiles
{ {
private final FileSystemAbstraction fs; private final FileSystemAbstraction fs;
private FileLayout<DateSchemaKey> dateFileLayout; private FileLayout<DateSchemaKey> dateFileLayout;
private FileLayout dateTimeFileLayout; private FileLayout<LocalDateTimeSchemaKey> dateTimeFileLayout;
private FileLayout dateTimeZonedFileLayout; private FileLayout dateTimeZonedFileLayout;
private FileLayout timeFileLayout; private FileLayout<LocalTimeSchemaKey> timeFileLayout;
private FileLayout timeZonedFileLayout; private FileLayout timeZonedFileLayout;
private FileLayout durationFileLayout; private FileLayout durationFileLayout;


Expand All @@ -45,6 +45,7 @@ class TemporalIndexFiles
File indexDirectory = directoryStructure.directoryForIndex( indexId ); File indexDirectory = directoryStructure.directoryForIndex( indexId );
dateFileLayout = new FileLayout<>( new File( indexDirectory, "date" ), DateLayout.of( descriptor ), ValueGroup.DATE ); 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 ); timeFileLayout = new FileLayout<>( new File( indexDirectory, "time" ), LocalTimeLayout.of( descriptor ), ValueGroup.LOCAL_TIME );
dateTimeFileLayout = new FileLayout<>( new File( indexDirectory, "datetime" ), LocalDateTimeLayout.of( descriptor ), ValueGroup.LOCAL_DATE_TIME );
} }


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


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


public FileLayout<LocalDateTimeSchemaKey> dateTime()
{
return dateTimeFileLayout;
}

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


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


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


@Override @Override
Expand Down
Expand Up @@ -145,7 +145,7 @@ public TemporalIndexPartReader<?> newDate() throws IOException
@Override @Override
public TemporalIndexPartReader<?> newDateTime() public TemporalIndexPartReader<?> newDateTime()
{ {
throw new UnsupportedOperationException( "Illiterate" ); return accessor.selectOrElse( ValueGroup.LOCAL_DATE_TIME, TemporalIndexAccessor.PartAccessor::newReader, null );
} }


@Override @Override
Expand Down

0 comments on commit 9c16c6f

Please sign in to comment.