diff --git a/docs/changelog/135263.yaml b/docs/changelog/135263.yaml new file mode 100644 index 0000000000000..e67ee86f1a31c --- /dev/null +++ b/docs/changelog/135263.yaml @@ -0,0 +1,5 @@ +pr: 135263 +summary: Optimize `dotCount` in expanding dot parser +area: "Mapping" +type: enhancement +issues: [] diff --git a/server/src/main/java/org/elasticsearch/index/mapper/FieldTypeLookup.java b/server/src/main/java/org/elasticsearch/index/mapper/FieldTypeLookup.java index c2f992eb6f664..b8d28e5b8fdbf 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/FieldTypeLookup.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/FieldTypeLookup.java @@ -153,10 +153,9 @@ final class FieldTypeLookup { public static int dotCount(String path) { int dotCount = 0; - for (int i = 0; i < path.length(); i++) { - if (path.charAt(i) == '.') { - dotCount++; - } + int index = -1; + while ((index = path.indexOf('.', index + 1)) != -1) { + dotCount++; } return dotCount; } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/FieldTypeLookupTests.java b/server/src/test/java/org/elasticsearch/index/mapper/FieldTypeLookupTests.java index ae793bc3b329e..eb1d2ef551f96 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/FieldTypeLookupTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/FieldTypeLookupTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.test.ESTestCase; import org.hamcrest.Matchers; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -522,6 +523,31 @@ public void testNoRootAliasForPassThroughFieldOnConflictingField() { assertEquals(foo.fieldType(), lookup.get("foo")); } + public void testDotCount() { + assertEquals(0, FieldTypeLookup.dotCount("")); + assertEquals(1, FieldTypeLookup.dotCount(".")); + assertEquals(2, FieldTypeLookup.dotCount("..")); + assertEquals(3, FieldTypeLookup.dotCount("...")); + assertEquals(4, FieldTypeLookup.dotCount("....")); + assertEquals(0, FieldTypeLookup.dotCount("foo")); + assertEquals(1, FieldTypeLookup.dotCount("foo.bar")); + assertEquals(2, FieldTypeLookup.dotCount("foo.bar.baz")); + assertEquals(3, FieldTypeLookup.dotCount("foo.bar.baz.bob")); + assertEquals(4, FieldTypeLookup.dotCount("foo.bar.baz.bob.")); + assertEquals(4, FieldTypeLookup.dotCount("foo..bar.baz.bob")); + assertEquals(5, FieldTypeLookup.dotCount("foo..bar..baz.bob")); + assertEquals(6, FieldTypeLookup.dotCount("foo..bar..baz.bob.")); + + int times = atLeast(50); + for (int i = 0; i < times; i++) { + byte[] bytes = new byte[randomInt(1024)]; + random().nextBytes(bytes); + String s = new String(bytes, StandardCharsets.UTF_8); + int expected = s.chars().map(c -> c == '.' ? 1 : 0).sum(); + assertEquals(expected, FieldTypeLookup.dotCount(s)); + } + } + @SafeVarargs @SuppressWarnings("varargs") static List randomizedList(T... values) {