diff --git a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexQuery.java b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexQuery.java
index 523924524a2dd..566ab1995aec1 100644
--- a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexQuery.java
+++ b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexQuery.java
@@ -514,6 +514,11 @@ public String from()
return (String)from.asObject();
}
+ public Value fromAsValue()
+ {
+ return from;
+ }
+
public boolean fromInclusive()
{
return fromInclusive;
@@ -524,6 +529,11 @@ public String to()
return (String)to.asObject();
}
+ public Value toAsValue()
+ {
+ return to;
+ }
+
public boolean toInclusive()
{
return toInclusive;
diff --git a/community/kernel/pom.xml b/community/kernel/pom.xml
index 8275838d85d5b..9599eaeae909c 100644
--- a/community/kernel/pom.xml
+++ b/community/kernel/pom.xml
@@ -254,6 +254,13 @@ the relevant Commercial Agreement.
test-jartest
+
+ org.neo4j
+ neo4j-values
+ ${project.version}
+ test-jar
+ test
+ org.apache.commonscommons-compress
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringLayout.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringLayout.java
new file mode 100644
index 0000000000000..dfaeefa82b1c6
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringLayout.java
@@ -0,0 +1,105 @@
+/*
+ * 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 .
+ */
+package org.neo4j.kernel.impl.index.schema;
+
+import org.neo4j.index.internal.gbptree.Layout;
+import org.neo4j.io.pagecache.PageCursor;
+
+import static java.lang.String.format;
+
+import static org.neo4j.kernel.impl.index.schema.StringSchemaKey.ENTITY_ID_SIZE;
+
+/**
+ * {@link Layout} for numbers where numbers doesn't need to be unique.
+ */
+abstract class StringLayout extends Layout.Adapter
+{
+ @Override
+ public StringSchemaKey newKey()
+ {
+ return new StringSchemaKey();
+ }
+
+ @Override
+ public StringSchemaKey copyKey( StringSchemaKey key, StringSchemaKey into )
+ {
+ // TODO when we have reuse of byte[] take that into consideration here too
+ into.bytes = key.bytes.clone();
+ into.setEntityId( key.getEntityId() );
+ into.setEntityIdIsSpecialTieBreaker( key.getEntityIdIsSpecialTieBreaker() );
+ return into;
+ }
+
+ @Override
+ public NativeSchemaValue newValue()
+ {
+ return NativeSchemaValue.INSTANCE;
+ }
+
+ @Override
+ public int keySize( StringSchemaKey key )
+ {
+ return key.size();
+ }
+
+ @Override
+ public int valueSize( NativeSchemaValue value )
+ {
+ return NativeSchemaValue.SIZE;
+ }
+
+ @Override
+ public void writeKey( PageCursor cursor, StringSchemaKey key )
+ {
+ cursor.putLong( key.getEntityId() );
+ cursor.putBytes( key.bytes );
+ }
+
+ @Override
+ public void writeValue( PageCursor cursor, NativeSchemaValue value )
+ {
+ }
+
+ @Override
+ public void readKey( PageCursor cursor, StringSchemaKey into, int keySize )
+ {
+ // TODO consider reusing byte[] instances somehow
+ into.setEntityId( cursor.getLong() );
+ into.bytes = new byte[keySize - ENTITY_ID_SIZE];
+ cursor.getBytes( into.bytes );
+ }
+
+ @Override
+ public void readValue( PageCursor cursor, NativeSchemaValue into, int valueSize )
+ {
+ }
+
+ @Override
+ public boolean fixedSize()
+ {
+ return false;
+ }
+
+ @Override
+ public String toString()
+ {
+ return format( "%s[version:%d.%d, identifier:%d]", getClass().getSimpleName(), majorVersion(), minorVersion(), identifier() );
+ }
+}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringLayoutNonUnique.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringLayoutNonUnique.java
new file mode 100644
index 0000000000000..b63de1da04bf2
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringLayoutNonUnique.java
@@ -0,0 +1,55 @@
+/*
+ * 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 .
+ */
+package org.neo4j.kernel.impl.index.schema;
+
+import org.neo4j.index.internal.gbptree.Layout;
+
+public class StringLayoutNonUnique extends StringLayout
+{
+ private static final String IDENTIFIER_NAME = "NUSI";
+ static final int MAJOR_VERSION = 0;
+ static final int MINOR_VERSION = 1;
+ static long IDENTIFIER = Layout.namedIdentifier( IDENTIFIER_NAME, NativeSchemaValue.SIZE );
+
+ @Override
+ public long identifier()
+ {
+ return IDENTIFIER;
+ }
+
+ @Override
+ public int majorVersion()
+ {
+ return MAJOR_VERSION;
+ }
+
+ @Override
+ public int minorVersion()
+ {
+ return MINOR_VERSION;
+ }
+
+ @Override
+ public int compare( StringSchemaKey o1, StringSchemaKey o2 )
+ {
+ int comparison = o1.compareValueTo( o2 );
+ return comparison != 0 ? comparison : Long.compare( o1.getEntityId(), o2.getEntityId() );
+ }
+}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringLayoutUnique.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringLayoutUnique.java
new file mode 100644
index 0000000000000..02aa5bbd9647d
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringLayoutUnique.java
@@ -0,0 +1,66 @@
+/*
+ * 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 .
+ */
+package org.neo4j.kernel.impl.index.schema;
+
+import org.neo4j.index.internal.gbptree.Layout;
+
+/**
+ * {@link Layout} for numbers where numbers need to be unique.
+ */
+class StringLayoutUnique extends StringLayout
+{
+ private static final String IDENTIFIER_NAME = "USI";
+ static final int MAJOR_VERSION = 0;
+ static final int MINOR_VERSION = 1;
+ static long IDENTIFIER = Layout.namedIdentifier( IDENTIFIER_NAME, NativeSchemaValue.SIZE );
+
+ @Override
+ public long identifier()
+ {
+ return IDENTIFIER;
+ }
+
+ @Override
+ public int majorVersion()
+ {
+ return MAJOR_VERSION;
+ }
+
+ @Override
+ public int minorVersion()
+ {
+ return MINOR_VERSION;
+ }
+
+ @Override
+ public int compare( StringSchemaKey o1, StringSchemaKey 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.getEntityIdIsSpecialTieBreaker() || o2.getEntityIdIsSpecialTieBreaker() )
+ {
+ return Long.compare( o1.getEntityId(), o2.getEntityId() );
+ }
+ }
+ return comparison;
+ }
+}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaIndexAccessor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaIndexAccessor.java
new file mode 100644
index 0000000000000..80ba1a83054d4
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaIndexAccessor.java
@@ -0,0 +1,57 @@
+/*
+ * 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 .
+ */
+package org.neo4j.kernel.impl.index.schema;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.neo4j.index.internal.gbptree.Layout;
+import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.kernel.api.index.SchemaIndexProvider;
+import org.neo4j.kernel.api.schema.index.IndexDescriptor;
+import org.neo4j.kernel.impl.api.index.sampling.IndexSamplingConfig;
+import org.neo4j.storageengine.api.schema.IndexReader;
+
+public class StringSchemaIndexAccessor
+ extends NativeSchemaIndexAccessor
+{
+ StringSchemaIndexAccessor(
+ PageCache pageCache,
+ FileSystemAbstraction fs,
+ File storeFile,
+ Layout layout,
+ RecoveryCleanupWorkCollector recoveryCleanupWorkCollector,
+ SchemaIndexProvider.Monitor monitor,
+ IndexDescriptor descriptor,
+ long indexId,
+ IndexSamplingConfig samplingConfig ) throws IOException
+ {
+ super( pageCache, fs, storeFile, layout, recoveryCleanupWorkCollector, monitor, descriptor, indexId, samplingConfig );
+ }
+
+ @Override
+ public IndexReader newReader()
+ {
+ assertOpen();
+ return new StringSchemaIndexReader<>( tree, layout, samplingConfig, descriptor );
+ }
+}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaIndexReader.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaIndexReader.java
new file mode 100644
index 0000000000000..d0999e9eef2d9
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaIndexReader.java
@@ -0,0 +1,117 @@
+/*
+ * 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 .
+ */
+package org.neo4j.kernel.impl.index.schema;
+
+import org.neo4j.index.internal.gbptree.GBPTree;
+import org.neo4j.index.internal.gbptree.Layout;
+import org.neo4j.internal.kernel.api.IndexOrder;
+import org.neo4j.internal.kernel.api.IndexQuery;
+import org.neo4j.internal.kernel.api.IndexQuery.ExactPredicate;
+import org.neo4j.internal.kernel.api.IndexQuery.StringRangePredicate;
+import org.neo4j.kernel.api.schema.index.IndexDescriptor;
+import org.neo4j.kernel.impl.api.index.sampling.IndexSamplingConfig;
+import org.neo4j.values.storable.Value;
+import org.neo4j.values.storable.ValueGroup;
+
+class StringSchemaIndexReader extends NativeSchemaIndexReader
+{
+ StringSchemaIndexReader( GBPTree tree, Layout layout, IndexSamplingConfig samplingConfig, IndexDescriptor descriptor )
+ {
+ super( tree, layout, samplingConfig, descriptor );
+ }
+
+ @Override
+ void validateQuery( IndexOrder indexOrder, IndexQuery[] predicates )
+ {
+ if ( predicates.length != 1 )
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ if ( indexOrder != IndexOrder.NONE )
+ {
+ throw new UnsupportedOperationException( "unsupported order " + indexOrder );
+ }
+ }
+
+ @Override
+ void initializeRangeForQuery( KEY treeKeyFrom, KEY treeKeyTo, IndexQuery[] predicates )
+ {
+ // todo initialize the keys to prepare for seek
+ IndexQuery predicate = predicates[0];
+ switch ( predicate.type() )
+ {
+ case exists:
+ treeKeyFrom.initAsLowest();
+ treeKeyTo.initAsHighest();
+ break;
+ case exact:
+ ExactPredicate exactPredicate = (ExactPredicate) predicate;
+ treeKeyFrom.from( Long.MIN_VALUE, exactPredicate.value() );
+ // No need to do the String --> byte[] conversion twice, right?
+ treeKeyTo.bytes = treeKeyFrom.bytes;
+ treeKeyTo.setEntityIdIsSpecialTieBreaker( false );
+ treeKeyTo.setEntityId( Long.MAX_VALUE );
+ break;
+ case rangeString:
+ StringRangePredicate rangePredicate = (StringRangePredicate)predicate;
+ initFromForRange( rangePredicate, treeKeyFrom );
+ initToForRange( rangePredicate, treeKeyTo );
+ break;
+ default:
+ throw new IllegalArgumentException( "IndexQuery of type " + predicate.type() + " is not supported." );
+ }
+ }
+
+ private void initFromForRange( StringRangePredicate rangePredicate, KEY treeKeyFrom )
+ {
+ Value fromValue = rangePredicate.fromAsValue();
+ if ( fromValue.valueGroup() == ValueGroup.NO_VALUE )
+ {
+ treeKeyFrom.initAsLowest();
+ }
+ else
+ {
+ treeKeyFrom.from( rangePredicate.fromInclusive() ? Long.MIN_VALUE : Long.MAX_VALUE, fromValue );
+ treeKeyFrom.setEntityIdIsSpecialTieBreaker( true );
+ }
+ }
+
+ private void initToForRange( StringRangePredicate rangePredicate, KEY treeKeyTo )
+ {
+ Value toValue = rangePredicate.toAsValue();
+ if ( toValue.valueGroup() == ValueGroup.NO_VALUE )
+ {
+ treeKeyTo.initAsHighest();
+ }
+ else
+ {
+ treeKeyTo.from( rangePredicate.toInclusive() ? Long.MAX_VALUE : Long.MIN_VALUE, toValue );
+ treeKeyTo.setEntityIdIsSpecialTieBreaker( true );
+ }
+ }
+
+ @Override
+ public boolean hasFullValuePrecision( IndexQuery... predicates )
+ {
+ return false;
+ }
+ // todo implement
+}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaKey.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaKey.java
new file mode 100644
index 0000000000000..52662de699821
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringSchemaKey.java
@@ -0,0 +1,235 @@
+/*
+ * 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 .
+ */
+package org.neo4j.kernel.impl.index.schema;
+
+import java.util.Arrays;
+
+import org.neo4j.string.UTF8;
+import org.neo4j.values.storable.TextValue;
+import org.neo4j.values.storable.Value;
+import org.neo4j.values.storable.ValueWriter;
+import org.neo4j.values.storable.Values;
+
+import static java.lang.String.format;
+
+import static org.neo4j.values.storable.UTF8StringValue.codePointByteArrayCompare;
+
+/**
+ * Includes value and entity id (to be able to handle non-unique values).
+ * A value can be any {@link Number} and is represented as a {@code long} to store the raw bits and a type
+ * to say if it's a long, double or float.
+ *
+ * Distinction between double and float exists because coersions between each other and long may differ.
+ * TODO this should be figured out and potentially reduced to long, double types only.
+ */
+class StringSchemaKey extends ValueWriter.Adapter implements NativeSchemaKey
+{
+ static final int ENTITY_ID_SIZE = Long.BYTES;
+
+ private long entityId;
+ private boolean entityIdIsSpecialTieBreaker;
+
+ // TODO something better or?
+ // TODO this is UTF-8 bytes for now
+ byte[] bytes;
+
+ @Override
+ public void setEntityIdIsSpecialTieBreaker( boolean entityIdIsSpecialTieBreaker )
+ {
+ this.entityIdIsSpecialTieBreaker = entityIdIsSpecialTieBreaker;
+ }
+
+ @Override
+ public boolean getEntityIdIsSpecialTieBreaker()
+ {
+ return entityIdIsSpecialTieBreaker;
+ }
+
+ int size()
+ {
+ return ENTITY_ID_SIZE + bytes.length;
+ }
+
+ @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;
+ entityIdIsSpecialTieBreaker = false;
+ assertValidValue( values ).writeTo( this );
+ }
+
+ private TextValue assertValidValue( Value... values )
+ {
+ // TODO: support multiple values, right?
+ 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.isTextValue( values[0] ) )
+ {
+ throw new IllegalArgumentException(
+ "Key layout does only support strings, tried to create key from " + values[0] );
+ }
+ return (TextValue) values[0];
+ }
+
+ @Override
+ public String propertiesAsString()
+ {
+ return asValue().toString();
+ }
+
+ @Override
+ public Value asValue()
+ {
+ return bytes == null ? Values.NO_VALUE : Values.utf8Value( bytes );
+ }
+
+ // TODO perhaps merge into parent or something
+ @Override
+ public void initAsLowest()
+ {
+ bytes = null;
+ entityId = Long.MIN_VALUE;
+ entityIdIsSpecialTieBreaker = true;
+ }
+
+ @Override
+ public void initAsHighest()
+ {
+ bytes = null;
+ entityId = Long.MAX_VALUE;
+ entityIdIsSpecialTieBreaker = true;
+ }
+
+ private boolean isHighest()
+ {
+ return entityIdIsSpecialTieBreaker && entityId == Long.MAX_VALUE && bytes == null;
+ }
+
+ private boolean isLowest()
+ {
+ return entityIdIsSpecialTieBreaker && entityId == Long.MIN_VALUE && bytes == null;
+ }
+
+ /**
+ * 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 StringSchemaKey} to compare to.
+ * @return comparison against the {@code other} {@link StringSchemaKey}.
+ */
+ int compareValueTo( StringSchemaKey other )
+ {
+ // TODO cover all cases of bytes == null and special tie breaker and document
+ if ( bytes != other.bytes )
+ {
+ if ( bytes == null )
+ {
+ return isHighest() ? 1 : -1;
+ }
+ if ( other.bytes == null )
+ {
+ return other.isHighest() ? -1 : 1;
+ }
+ }
+ else
+ {
+ return 0;
+ }
+ try
+ {
+ // TODO change to not throw
+ return codePointByteArrayCompare( bytes, other.bytes );
+ }
+ catch ( Exception e )
+ {
+ // We can not throw here because we will visit this method inside a pageCursor.shouldRetry() block.
+ // Just return a comparison that at least will be commutative.
+ return byteArrayCompare( bytes, other.bytes );
+ }
+ }
+
+ private static int byteArrayCompare( byte[] a, byte[] b )
+ {
+ assert a != null && b != null : "Null arrays not supported.";
+
+ if ( a == b )
+ {
+ return 0;
+ }
+
+ int length = Math.min( a.length, b.length );
+ for ( int i = 0; i < length; i++ )
+ {
+ int compare = Byte.compare( a[i], b[i] );
+ if ( compare != 0 )
+ {
+ return compare;
+ }
+ }
+
+ if ( a.length < b.length )
+ {
+ return -1;
+ }
+
+ if ( a.length > b.length )
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ @Override
+ public String toString()
+ {
+ return format( "value=%s,entityId=%d,bytes=%s", asValue(), entityId, Arrays.toString( bytes ) );
+ }
+
+ @Override
+ public void writeString( String value )
+ {
+ // expensiveness
+ bytes = UTF8.encode( value );
+ }
+
+ @Override
+ public void writeString( char value )
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LayoutTestUtil.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LayoutTestUtil.java
index 9c3fecfd7cdde..01fe1e5d231fa 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LayoutTestUtil.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/LayoutTestUtil.java
@@ -22,6 +22,8 @@
import org.apache.commons.lang3.ArrayUtils;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -42,6 +44,9 @@
abstract class LayoutTestUtil
{
+ private static final Comparator> UPDATE_COMPARATOR = ( u1, u2 ) ->
+ Values.COMPARATOR.compare( u1.values()[0], u2.values()[0] );
+
final IndexDescriptor indexDescriptor;
LayoutTestUtil( IndexDescriptor indexDescriptor )
@@ -55,9 +60,7 @@ abstract class LayoutTestUtil> randomUpdateGenerator( RandomRule random );
+ Iterator> randomUpdateGenerator( RandomRule random )
+ {
+ double fractionDuplicates = fractionDuplicates();
+ return new PrefetchingIterator>()
+ {
+ private final Set