diff --git a/community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/SimpleIndexAccessorCompatibility.java b/community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/SimpleIndexAccessorCompatibility.java index a7622150045d2..2dc7e8df244e8 100644 --- a/community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/SimpleIndexAccessorCompatibility.java +++ b/community/community-it/kernel-it/src/test/java/org/neo4j/kernel/api/index/SimpleIndexAccessorCompatibility.java @@ -26,6 +26,7 @@ import java.time.ZoneId; import java.time.ZoneOffset; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -427,6 +428,48 @@ public void testIndexEndsWithWithDuplicated() throws Exception assertThat( query( stringSuffix( 1, "apa*" ) ), equalTo( Collections.emptyList() ) ); assertThat( query( stringSuffix( 1, "" ) ), equalTo( asList( 1L, 2L, 3L, 4L, 5L, 6L ) ) ); } + + @Test + public void testIndexShouldHandleLargeAmountOfDuplicatesString() throws Exception + { + doTestShouldHandleLargeAmountOfDuplicates( "this is a semi-long string that will need to be split" ); + } + + @Test + public void testIndexShouldHandleLargeAmountOfDuplicatesStringArray() throws Exception + { + Value arrayValue = nextRandomValidArrayValue(); + doTestShouldHandleLargeAmountOfDuplicates( arrayValue ); + } + + private void doTestShouldHandleLargeAmountOfDuplicates( Object value ) throws Exception + { + List> updates = new ArrayList<>(); + List nodeIds = new ArrayList<>(); + for ( long i = 0; i < 1000; i++ ) + { + nodeIds.add( i ); + updates.add( add( i, descriptor.schema(), value ) ); + } + updateAndCommit( updates ); + + assertThat( query( exists( 1 ) ), equalTo( nodeIds ) ); + } + + private Value nextRandomValidArrayValue() + { + Value value; + while ( true ) + { + value = random.randomValues().nextArray(); + // todo remove when spatial is supported by all + if ( testSuite.supportsSpatial() || !Values.isGeometryArray( value ) ) + { + break; + } + } + return value; + } } // This behaviour is expected by Unique indexes diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericKeyState.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericKeyState.java index df423d363db8b..15092962e51ef 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericKeyState.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/GenericKeyState.java @@ -473,7 +473,8 @@ private static void minimalSplitterArray( GenericKeyState left, GenericKeyState // Convert from last equal index to first index to differ +1 // Convert from index to length +1 // Total +2 - arrayCopier.copyArray( into, right, lastEqualIndex + 2 ); + int length = Math.min( right.arrayLength, lastEqualIndex + 2 ); + arrayCopier.copyArray( into, right, length ); } private static void minimalSplitterText( GenericKeyState left, GenericKeyState right, GenericKeyState into ) @@ -481,7 +482,7 @@ private static void minimalSplitterText( GenericKeyState left, GenericKeyState r int length = 0; if ( left.type == Type.TEXT ) { - length = StringLayout.firstPosToDiffer( left.byteArray, (int) left.long0, right.byteArray, (int) right.long0 ); + length = StringLayout.minimalLengthFromRightNeededToDifferentiateFromLeft( left.byteArray, (int) left.long0, right.byteArray, (int) right.long0 ); } into.writeUTF8( right.byteArray, 0, length ); } 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 index 83a3914211b9d..832c31165fd2d 100644 --- 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 @@ -22,7 +22,7 @@ import org.neo4j.index.internal.gbptree.Layout; import org.neo4j.io.pagecache.PageCursor; -import static java.lang.Integer.min; +import static java.lang.Math.min; import static java.lang.String.format; import static org.neo4j.kernel.impl.index.schema.StringIndexKey.ENTITY_ID_SIZE; @@ -87,11 +87,11 @@ public boolean fixedSize() @Override public void minimalSplitter( StringIndexKey left, StringIndexKey right, StringIndexKey into ) { - int targetLength = firstPosToDiffer( left.bytes, left.bytesLength, right.bytes, right.bytesLength ); + int targetLength = minimalLengthFromRightNeededToDifferentiateFromLeft( left.bytes, left.bytesLength, right.bytes, right.bytesLength ); into.copyFrom( right, targetLength ); } - static int firstPosToDiffer( byte[] leftBytes, int leftLength, byte[] rightBytes, int rightLength ) + static int minimalLengthFromRightNeededToDifferentiateFromLeft( byte[] leftBytes, int leftLength, byte[] rightBytes, int rightLength ) { int lastEqualIndex = -1; int maxLength = min( leftLength, rightLength ); @@ -106,7 +106,7 @@ static int firstPosToDiffer( byte[] leftBytes, int leftLength, byte[] rightBytes // Convert from last equal index to first index to differ +1 // Convert from index to length +1 // Total +2 - return lastEqualIndex + 2; + return Math.min( rightLength, lastEqualIndex + 2 ); } @Override 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 fdc19511612ea..f4290f061ddf7 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 @@ -259,6 +259,13 @@ void mustProduceValidMinimalSplitters( ValueGenerator valueGenerator ) assertValidMinimalSplitter( left, right ); } + @ParameterizedTest + @MethodSource( "validValueGenerators" ) + void mustProduceValidMinimalSplittersWhenValuesAreEqual( ValueGenerator valueGenerator ) + { + assertValidMinimalSplitterForEqualValues( valueGenerator.next() ); + } + @ParameterizedTest @MethodSource( "validValueGenerators" ) void mustReportCorrectSize( ValueGenerator valueGenerator ) @@ -457,6 +464,22 @@ private void assertValidMinimalSplitter( Value left, Value right ) "right state not less than minimal splitter, leftState=" + leftState + ", rightState=" + rightState + ", minimalSplitter=" + minimalSplitter ); } + private void assertValidMinimalSplitterForEqualValues( Value value ) + { + GenericKeyState leftState = new GenericKeyState(); + leftState.writeValue( value, NEUTRAL ); + GenericKeyState rightState = new GenericKeyState(); + rightState.writeValue( value, NEUTRAL ); + + GenericKeyState minimalSplitter = new GenericKeyState(); + GenericKeyState.minimalSplitter( leftState, rightState, minimalSplitter ); + + assertTrue( leftState.compareValueTo( minimalSplitter ) == 0, + "left state not equal to minimal splitter, leftState=" + leftState + ", rightState=" + rightState + ", minimalSplitter=" + minimalSplitter ); + assertTrue( rightState.compareValueTo( minimalSplitter ) == 0, + "right state equal to minimal splitter, leftState=" + leftState + ", rightState=" + rightState + ", minimalSplitter=" + minimalSplitter ); + } + private static Value nextValidValue() { Value value;