diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericLayout.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericLayout.java index 0c647b6a9f193..e68aade4ad7e4 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericLayout.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericLayout.java @@ -29,7 +29,7 @@ class GenericLayout extends IndexLayout GenericLayout( int numberOfSlots, IndexSpecificSpaceFillingCurveSettingsCache spatialSettings ) { - super( "NSIL", 0, 4 ); + super( "NSIL", 0, 5 ); this.numberOfSlots = numberOfSlots; this.spatialSettings = spatialSettings; } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GeometryArrayType.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GeometryArrayType.java index 75000e68d7d2e..0564a283cfa11 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GeometryArrayType.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GeometryArrayType.java @@ -36,6 +36,7 @@ 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.hasCoordinates; import static org.neo4j.kernel.impl.index.schema.GeometryType.putCrs; import static org.neo4j.kernel.impl.index.schema.GeometryType.putPoint; import static org.neo4j.kernel.impl.index.schema.GeometryType.readCrs; @@ -61,8 +62,8 @@ class GeometryArrayType extends AbstractArrayType { super( ValueGroup.GEOMETRY_ARRAY, typeId, ( o1, o2, i ) -> GeometryType.compare( // intentional long1 and long2 - not the array versions - o1.long0Array[i], o1.long1, o1.long2, - o2.long0Array[i], o2.long1, o2.long2 ), + o1.long0Array[i], o1.long1, o1.long2, o1.long3, o1.long1Array, (int) o1.long3 * i, + o2.long0Array[i], o2.long1, o2.long2, o2.long3, o2.long1Array, (int) o2.long3 * i ), null, null, null, null, null ); } @@ -137,17 +138,9 @@ boolean readValue( PageCursor cursor, int size, GenericKey into ) @Override String toString( GenericKey state ) { + String asValueString = hasCoordinates( state ) ? asValue( state ).toString() : "NO_COORDINATES"; return format( "GeometryArray[tableId:%d, code:%d, rawValues:%s, value:%s]", - state.long1, state.long2, Arrays.toString( Arrays.copyOf( state.long0Array, state.arrayLength ) ), asValue( state ) ); - } - - @Override - void minimalSplitter( GenericKey left, GenericKey right, GenericKey into ) - { - super.minimalSplitter( left, right, into ); - // Set dimensions to 0 so that minimal splitters (i.e. point keys in internal nodes) doesn't have coordinate data, - // they don't need it since values aren't generated from internal keys anyway. - into.long3 = 0; + state.long1, state.long2, Arrays.toString( Arrays.copyOf( state.long0Array, state.arrayLength ) ), asValueString ); } private static boolean readGeometryArrayItem( PageCursor cursor, GenericKey into ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GeometryType.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GeometryType.java index 2501adcffaba6..9bfba19ca0f99 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GeometryType.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GeometryType.java @@ -103,8 +103,8 @@ static PointValue asValue( GenericKey state, CoordinateReferenceSystem crs, int int compareValue( GenericKey left, GenericKey right ) { return compare( - left.long0, left.long1, left.long2, - right.long0, right.long1, right.long2 ); + left.long0, left.long1, left.long2, left.long3, left.long1Array, 0, + right.long0, right.long1, right.long2, right.long3, right.long1Array, 0 ); } @Override @@ -123,26 +123,21 @@ boolean readValue( PageCursor cursor, int size, GenericKey into ) @Override String toString( GenericKey state ) { - return format( "Geometry[tableId:%d, code:%d, rawValue:%d, value:%s", state.long1, state.long2, state.long0, asValue( state ) ); - } - - @Override - void minimalSplitter( GenericKey left, GenericKey right, GenericKey into ) - { - super.minimalSplitter( left, right, into ); - // Set dimensions to 0 so that minimal splitters (i.e. point keys in internal nodes) doesn't have coordinate data, - // they don't need it since values aren't generated from internal keys anyway. - into.long3 = 0; + String asValueString = hasCoordinates( state ) ? asValue( state ).toString() : "NO_COORDINATES"; + return format( "Geometry[tableId:%d, code:%d, rawValue:%d, value:%s", state.long1, state.long2, state.long0, asValueString ); } /** * This method will compare along the curve, which is not a spatial comparison, but is correct * for comparison within the space filling index as long as the original spatial range has already * been decomposed into a collection of 1D curve ranges before calling down into the GPTree. + * If value on space filling curve is equal then raw comparison of serialized coordinates is done. + * This way points are only considered equal in index if they have the same coordinates and not if + * they only happen to occupy same value on space filling curve. */ static int compare( - long this_long0, long this_long1, long this_long2, - long that_long0, long that_long1, long that_long2 ) + long this_long0, long this_long1, long this_long2, long this_long3, long[] this_long1Array, int this_coordinates_offset, + long that_long0, long that_long1, long that_long2, long that_long3, long[] that_long1Array, int that_coordinates_offset ) { int tableIdComparison = Integer.compare( (int) this_long1, (int) that_long1 ); if ( tableIdComparison != 0 ) @@ -156,7 +151,26 @@ static int compare( return codeComparison; } - return Long.compare( this_long0, that_long0 ); + int derivedValueComparison = Long.compare( this_long0, that_long0 ); + if ( derivedValueComparison != 0 ) + { + return derivedValueComparison; + } + + long dimensions = Math.min( this_long3, that_long3 ); + for ( int i = 0; i < dimensions; i++ ) + { + // It's ok to compare the coordinate value here without deserializing them + // because we are only defining SOME deterministic order so that we can + // correctly separate unique points from each other, even if they collide + // on the space filling curve. + int coordinateComparison = Long.compare( this_long1Array[this_coordinates_offset + i], that_long1Array[that_coordinates_offset + i] ); + if ( coordinateComparison != 0 ) + { + return coordinateComparison; + } + } + return 0; } static void putCrs( PageCursor cursor, long long1, long long2, long long3 ) @@ -196,12 +210,22 @@ static void putPoint( PageCursor cursor, long long0, long long3, long[] long1Arr */ static void assertHasCoordinates( GenericKey state ) { - if ( state.long3 == 0 || state.long1Array == null ) + if ( !hasCoordinates( state ) ) { throw new IllegalStateException( "This geometry key doesn't have coordinates and can therefore neither be persisted nor generate point value." ); } } + static boolean hasCoordinates( GenericKey state ) + { + return state.long3 != 0 && state.long1Array != null; + } + + static void setNoCoordinates( GenericKey state ) + { + state.long3 = 0; + } + private static void put3BInt( PageCursor cursor, int value ) { cursor.putShort( (short) value ); diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericAccessorPointsTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericAccessorPointsTest.java new file mode 100644 index 0000000000000..033eb12a5b479 --- /dev/null +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericAccessorPointsTest.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.neo4j.gis.spatial.index.curves.SpaceFillingCurve; +import org.neo4j.gis.spatial.index.curves.StandardConfiguration; +import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector; +import org.neo4j.internal.kernel.api.IndexOrder; +import org.neo4j.internal.kernel.api.IndexQuery; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotApplicableKernelException; +import org.neo4j.io.fs.DefaultFileSystemAbstraction; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; +import org.neo4j.kernel.api.index.IndexDirectoryStructure; +import org.neo4j.kernel.api.index.IndexEntryUpdate; +import org.neo4j.kernel.api.schema.index.TestIndexDescriptorFactory; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.impl.api.index.IndexUpdateMode; +import org.neo4j.kernel.impl.index.schema.config.ConfiguredSpaceFillingCurveSettingsCache; +import org.neo4j.kernel.impl.index.schema.config.IndexSpecificSpaceFillingCurveSettingsCache; +import org.neo4j.storageengine.api.schema.IndexReader; +import org.neo4j.storageengine.api.schema.SimpleNodeValueClient; +import org.neo4j.storageengine.api.schema.StoreIndexDescriptor; +import org.neo4j.test.rule.PageCacheRule; +import org.neo4j.test.rule.RandomRule; +import org.neo4j.test.rule.TestDirectory; +import org.neo4j.test.rule.fs.DefaultFileSystemRule; +import org.neo4j.values.storable.CoordinateReferenceSystem; +import org.neo4j.values.storable.PointArray; +import org.neo4j.values.storable.PointValue; +import org.neo4j.values.storable.Value; +import org.neo4j.values.storable.Values; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.rules.RuleChain.outerRule; +import static org.neo4j.kernel.api.index.IndexProvider.Monitor.EMPTY; +import static org.neo4j.test.rule.PageCacheRule.config; +import static org.neo4j.values.storable.CoordinateReferenceSystem.WGS84; + +public class GenericAccessorPointsTest +{ + private static final CoordinateReferenceSystem crs = CoordinateReferenceSystem.WGS84; + private static final ConfiguredSpaceFillingCurveSettingsCache configuredSettings = new ConfiguredSpaceFillingCurveSettingsCache( Config.defaults() ); + private static final IndexSpecificSpaceFillingCurveSettingsCache indexSettings = + new IndexSpecificSpaceFillingCurveSettingsCache( configuredSettings, new HashMap<>() ); + private static final SpaceFillingCurve curve = indexSettings.forCrs( crs, true ); + + private final DefaultFileSystemRule fs = new DefaultFileSystemRule(); + private final TestDirectory directory = TestDirectory.testDirectory( getClass(), fs.get() ); + private final PageCacheRule pageCacheRule = new PageCacheRule( config().withAccessChecks( true ) ); + private final RandomRule random = new RandomRule(); + @Rule + public final RuleChain rules = outerRule( random ).around( fs ).around( directory ).around( pageCacheRule ); + + private NativeIndexAccessor accessor; + private StoreIndexDescriptor descriptor; + + @Before + public void setup() + { + DefaultFileSystemAbstraction fs = this.fs.get(); + PageCache pc = pageCacheRule.getPageCache( fs ); + File file = directory.file( "index" ); + GenericLayout layout = new GenericLayout( 1, indexSettings ); + RecoveryCleanupWorkCollector collector = RecoveryCleanupWorkCollector.ignore(); + descriptor = TestIndexDescriptorFactory.forLabel( 1, 1 ).withId( 1 ); + IndexDirectoryStructure.Factory factory = IndexDirectoryStructure.directoriesByProvider( directory.storeDir() ); + IndexDirectoryStructure structure = factory.forProvider( GenericNativeIndexProvider.DESCRIPTOR ); + accessor = new GenericNativeIndexAccessor( pc, fs, file, layout, collector, EMPTY, descriptor, indexSettings, structure, new StandardConfiguration() ); + } + + @After + public void tearDown() + { + accessor.close(); + } + + /** + * This test verify that we correctly handle unique points that all belong to the same tile on the space filling curve. + * All points share at least one dimension coordinate with another point to exercise minimal splitter. + * We verify this by asserting that we always get exactly one hit on an exact match and that the value is what we expect. + */ + @Test + public void mustHandlePointsWithinSameTile() throws IndexEntryConflictException, IndexNotApplicableKernelException + { + // given + // Many random points that all are close enough to each other to belong to the same tile on the space filling curve. + int nbrOfValues = 10000; + PointValue origin = Values.pointValue( WGS84, 0.0, 0.0 ); + Long derivedValueForCenterPoint = curve.derivedValueFor( origin.coordinate() ); + double[] centerPoint = curve.centerPointFor( derivedValueForCenterPoint ); + double xWidthMultiplier = curve.getTileWidth( 0, curve.getMaxLevel() ) / 2; + double yWidthMultiplier = curve.getTileWidth( 1, curve.getMaxLevel() ) / 2; + + List pointValues = new ArrayList<>(); + List> updates = new ArrayList<>(); + long nodeId = 1; + for ( int i = 0; i < nbrOfValues / 4; i++ ) + { + double x1 = (random.nextDouble() * 2 - 1) * xWidthMultiplier; + double x2 = (random.nextDouble() * 2 - 1) * xWidthMultiplier; + double y1 = (random.nextDouble() * 2 - 1) * yWidthMultiplier; + double y2 = (random.nextDouble() * 2 - 1) * yWidthMultiplier; + PointValue value11 = Values.pointValue( WGS84, centerPoint[0] + x1, centerPoint[1] + y1 ); + PointValue value12 = Values.pointValue( WGS84, centerPoint[0] + x1, centerPoint[1] + y2 ); + PointValue value21 = Values.pointValue( WGS84, centerPoint[0] + x2, centerPoint[1] + y1 ); + PointValue value22 = Values.pointValue( WGS84, centerPoint[0] + x2, centerPoint[1] + y2 ); + assertDerivedValue( derivedValueForCenterPoint, value11, value12, value21, value22 ); + + nodeId = addPointsToLists( pointValues, updates, nodeId, value11, value12, value21, value22 ); + } + + processAll( updates ); + + // then + exactMatchOnAllValues( pointValues ); + } + + /** + * This test verify that we correctly handle unique point arrays where every point in every array belong to the same tile on the space filling curve. + * We verify this by asserting that we always get exactly one hit on an exact match and that the value is what we expect. + */ + @Test + public void mustHandlePointArraysWithinSameTile() throws IndexEntryConflictException, IndexNotApplicableKernelException + { + // given + // Many random points that all are close enough to each other to belong to the same tile on the space filling curve. + int nbrOfValues = 10000; + PointValue origin = Values.pointValue( WGS84, 0.0, 0.0 ); + Long derivedValueForCenterPoint = curve.derivedValueFor( origin.coordinate() ); + double[] centerPoint = curve.centerPointFor( derivedValueForCenterPoint ); + double xWidthMultiplier = curve.getTileWidth( 0, curve.getMaxLevel() ) / 2; + double yWidthMultiplier = curve.getTileWidth( 1, curve.getMaxLevel() ) / 2; + + List pointArrays = new ArrayList<>(); + List> updates = new ArrayList<>(); + for ( int i = 0; i < nbrOfValues; i++ ) + { + int arrayLength = random.nextInt( 5 ) + 1; + PointValue[] pointValues = new PointValue[arrayLength]; + for ( int j = 0; j < arrayLength; j++ ) + { + double x = (random.nextDouble() * 2 - 1) * xWidthMultiplier; + double y = (random.nextDouble() * 2 - 1) * yWidthMultiplier; + PointValue value = Values.pointValue( WGS84, centerPoint[0] + x, centerPoint[1] + y ); + + assertDerivedValue( derivedValueForCenterPoint, value ); + pointValues[j] = value; + } + PointArray array = Values.pointArray( pointValues ); + pointArrays.add( array ); + updates.add( IndexEntryUpdate.add( i, descriptor, array ) ); + } + + processAll( updates ); + + // then + exactMatchOnAllValues( pointArrays ); + } + + private long addPointsToLists( List pointValues, List> updates, long nodeId, PointValue... values ) + { + for ( PointValue value : values ) + { + pointValues.add( value ); + updates.add( IndexEntryUpdate.add( nodeId++, descriptor, value ) ); + } + return nodeId; + } + + private void assertDerivedValue( Long targetDerivedValue, PointValue... values ) + { + for ( PointValue value : values ) + { + Long derivedValueForValue = curve.derivedValueFor( value.coordinate() ); + assertEquals( targetDerivedValue, derivedValueForValue, "expected random value to belong to same tile as center point" ); + } + } + + private void processAll( List> updates ) throws IndexEntryConflictException + { + try ( NativeIndexUpdater updater = accessor.newUpdater( IndexUpdateMode.ONLINE ) ) + { + for ( IndexEntryUpdate update : updates ) + { + //noinspection unchecked + updater.process( update ); + } + } + } + + private void exactMatchOnAllValues( List values ) throws IndexNotApplicableKernelException + { + try ( IndexReader indexReader = accessor.newReader() ) + { + SimpleNodeValueClient client = new SimpleNodeValueClient(); + for ( Value value : values ) + { + IndexQuery.ExactPredicate exact = IndexQuery.exact( descriptor.schema().getPropertyId(), value ); + indexReader.query( client, IndexOrder.NONE, true, exact ); + + // then + assertTrue( client.next() ); + assertEquals( value, client.values[0] ); + assertFalse( client.next() ); + } + } + } +} diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericKeyStateTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericKeyStateTest.java index a49f106e26720..5cded9e371544 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericKeyStateTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericKeyStateTest.java @@ -39,6 +39,7 @@ import java.util.List; import java.util.stream.Stream; +import org.neo4j.gis.spatial.index.curves.SpaceFillingCurve; import org.neo4j.io.pagecache.ByteArrayPageCursor; import org.neo4j.io.pagecache.PageCache; import org.neo4j.io.pagecache.PageCursor; @@ -49,11 +50,14 @@ import org.neo4j.test.extension.Inject; import org.neo4j.test.extension.RandomExtension; import org.neo4j.test.rule.RandomRule; +import org.neo4j.values.AnyValues; +import org.neo4j.values.storable.CoordinateReferenceSystem; import org.neo4j.values.storable.DateTimeValue; import org.neo4j.values.storable.DateValue; import org.neo4j.values.storable.DurationValue; import org.neo4j.values.storable.LocalDateTimeValue; import org.neo4j.values.storable.LocalTimeValue; +import org.neo4j.values.storable.PointArray; import org.neo4j.values.storable.PointValue; import org.neo4j.values.storable.RandomValues; import org.neo4j.values.storable.TimeValue; @@ -198,7 +202,6 @@ void copyShouldCopyExtremeValues() void compareToMustAlignWithValuesCompareTo( ValueGenerator valueGenerator ) { // Given - random.reset(); List values = new ArrayList<>(); List states = new ArrayList<>(); for ( int i = 0; i < 10; i++ ) @@ -221,6 +224,65 @@ void compareToMustAlignWithValuesCompareTo( ValueGenerator valueGenerator ) } } + @Test + void comparePointsMustOnlyReturnZeroForEqualPoints() + { + PointValue firstPoint = random.randomValues().nextPointValue(); + PointValue equalPoint = Values.point( firstPoint ); + CoordinateReferenceSystem crs = firstPoint.getCoordinateReferenceSystem(); + SpaceFillingCurve curve = noSpecificIndexSettings.forCrs( crs, false ); + Long spaceFillingCurveValue = curve.derivedValueFor( firstPoint.coordinate() ); + PointValue centerPoint = Values.pointValue( crs, curve.centerPointFor( spaceFillingCurveValue ) ); + + GenericKey firstKey = newKeyState(); + firstKey.writeValue( firstPoint, NEUTRAL ); + GenericKey equalKey = newKeyState(); + equalKey.writeValue( equalPoint, NEUTRAL ); + GenericKey centerKey = newKeyState(); + centerKey.writeValue( centerPoint, NEUTRAL ); + GenericKey noCoordsKey = newKeyState(); + noCoordsKey.writeValue( equalPoint, NEUTRAL ); + GeometryType.setNoCoordinates( noCoordsKey ); + + assertEquals( 0, firstKey.compareValueTo( equalKey ), "expected keys to be equal" ); + assertEquals( firstPoint.compareTo( centerPoint ) != 0, firstKey.compareValueTo( centerKey ) != 0, + "expected keys to be equal if and only if source points are equal" ); + assertEquals( 0, firstKey.compareValueTo( noCoordsKey ), "expected keys to be equal" ); + } + + @Test + void comparePointArraysMustOnlyReturnZeroForEqualArrays() + { + PointArray firstArray = random.randomValues().nextPointArray(); + PointValue[] sourcePointValues = firstArray.asObjectCopy(); + PointArray equalArray = Values.pointArray( sourcePointValues ); + PointValue[] centerPointValues = new PointValue[sourcePointValues.length]; + for ( int i = 0; i < sourcePointValues.length; i++ ) + { + PointValue sourcePointValue = sourcePointValues[i]; + CoordinateReferenceSystem crs = sourcePointValue.getCoordinateReferenceSystem(); + SpaceFillingCurve curve = noSpecificIndexSettings.forCrs( crs, false ); + Long spaceFillingCurveValue = curve.derivedValueFor( sourcePointValue.coordinate() ); + centerPointValues[i] = Values.pointValue( crs, curve.centerPointFor( spaceFillingCurveValue ) ); + } + PointArray centerArray = Values.pointArray( centerPointValues ); + + GenericKey firstKey = newKeyState(); + firstKey.writeValue( firstArray, NEUTRAL ); + GenericKey equalKey = newKeyState(); + equalKey.writeValue( equalArray, NEUTRAL ); + GenericKey centerKey = newKeyState(); + centerKey.writeValue( centerArray, NEUTRAL ); + GenericKey noCoordsKey = newKeyState(); + noCoordsKey.writeValue( equalArray, NEUTRAL ); + GeometryType.setNoCoordinates( noCoordsKey ); + + assertEquals( 0, firstKey.compareValueTo( equalKey ), "expected keys to be equal" ); + assertEquals( firstArray.compareToSequence( centerArray, AnyValues.COMPARATOR ) != 0, firstKey.compareValueTo( centerKey ) != 0, + "expected keys to be equal if and only if source points are equal" ); + assertEquals( 0, firstKey.compareValueTo( noCoordsKey ), "expected keys to be equal" ); + } + // The reason this test doesn't test incomparable values is that it relies on ordering being same as that of the Values module. @ParameterizedTest @MethodSource( "validComparableValueGenerators" ) @@ -538,7 +600,7 @@ private void assertValidMinimalSplitterForEqualValues( Value value ) assertEquals( 0, leftState.compareValueTo( minimalSplitter ), "left state not equal to minimal splitter, leftState=" + leftState + ", rightState=" + rightState + ", minimalSplitter=" + minimalSplitter ); assertEquals( 0, rightState.compareValueTo( minimalSplitter ), - "right state equal to minimal splitter, leftState=" + leftState + ", rightState=" + rightState + ", minimalSplitter=" + minimalSplitter ); + "right state not equal to minimal splitter, leftState=" + leftState + ", rightState=" + rightState + ", minimalSplitter=" + minimalSplitter ); } private static Value nextValidValue( boolean includeIncomparable ) diff --git a/community/kernel/src/test/resources/org/neo4j/kernel/impl/index/schema/current-generic-key-state-format.zip b/community/kernel/src/test/resources/org/neo4j/kernel/impl/index/schema/current-generic-key-state-format.zip index 104a87f6a9dfc..cefb4b83a2645 100644 Binary files a/community/kernel/src/test/resources/org/neo4j/kernel/impl/index/schema/current-generic-key-state-format.zip and b/community/kernel/src/test/resources/org/neo4j/kernel/impl/index/schema/current-generic-key-state-format.zip differ