Upgrade to Lucene 9.12.0 (#113333)

This commit upgrades to Lucene 9.12.0.

Co-authored-by: Adrien Grand <jpountz@gmail.com>
Co-authored-by: Armin Braun <me@obrown.io>
Co-authored-by: Benjamin Trent <ben.w.trent@gmail.com>
Co-authored-by: Chris Hegarty <chegar999@gmail.com>
Co-authored-by: John Wagster <john.wagster@elastic.co>
Co-authored-by: Luca Cavanna <javanna@apache.org>
Co-authored-by: Mayya Sharipova <mayya.sharipova@elastic.co>
This commit is contained in:
Chris Hegarty 2024-10-01 08:39:27 +01:00 committed by GitHub
parent 4471e82dcc
commit 32dde26e49
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
81 changed files with 1435 additions and 350 deletions

View file

@ -1,5 +1,5 @@
elasticsearch = 9.0.0 elasticsearch = 9.0.0
lucene = 9.11.1 lucene = 9.12.0
bundled_jdk_vendor = openjdk bundled_jdk_vendor = openjdk
bundled_jdk = 22.0.1+8@c7ec1332f7bb44aeba2eb341ae18aca4 bundled_jdk = 22.0.1+8@c7ec1332f7bb44aeba2eb341ae18aca4

View file

@ -1,8 +1,8 @@
include::{docs-root}/shared/versions/stack/{source_branch}.asciidoc[] include::{docs-root}/shared/versions/stack/{source_branch}.asciidoc[]
:lucene_version: 9.11.1 :lucene_version: 9.12.0
:lucene_version_path: 9_11_1 :lucene_version_path: 9_12_0
:jdk: 11.0.2 :jdk: 11.0.2
:jdk_major: 11 :jdk_major: 11
:build_type: tar :build_type: tar

View file

@ -0,0 +1,5 @@
pr: 111465
summary: Add range and regexp Intervals
area: Search
type: enhancement
issues: []

View file

@ -0,0 +1,6 @@
pr: 112826
summary: "Multi term intervals: increase max_expansions"
area: Search
type: enhancement
issues:
- 110491

View file

@ -0,0 +1,5 @@
pr: 113333
summary: Upgrade to Lucene 9.12
area: Search
type: upgrade
issues: []

View file

@ -13,16 +13,10 @@ There are several thread pools, but the important ones include:
[[search-threadpool]] [[search-threadpool]]
`search`:: `search`::
For coordination of count/search operations at the shard level whose computation For count/search operations at the shard level. Used also by fetch and other search
is offloaded to the search_worker thread pool. Used also by fetch and other search
related operations Thread pool type is `fixed` with a size of `int((`<<node.processors, related operations Thread pool type is `fixed` with a size of `int((`<<node.processors,
`# of allocated processors`>>`pass:[ * ]3) / 2) + 1`, and queue_size of `1000`. `# of allocated processors`>>`pass:[ * ]3) / 2) + 1`, and queue_size of `1000`.
`search_worker`::
For the heavy workload of count/search operations that may be executed concurrently
across segments within the same shard when possible. Thread pool type is `fixed`
with a size of `int((`<<node.processors, `# of allocated processors`>>`pass:[ * ]3) / 2) + 1`, and unbounded queue_size .
[[search-throttled]]`search_throttled`:: [[search-throttled]]`search_throttled`::
For count/search/suggest/get operations on `search_throttled indices`. For count/search/suggest/get operations on `search_throttled indices`.
Thread pool type is `fixed` with a size of `1`, and queue_size of `100`. Thread pool type is `fixed` with a size of `1`, and queue_size of `100`.

View file

@ -73,7 +73,9 @@ Valid rules include:
* <<intervals-match,`match`>> * <<intervals-match,`match`>>
* <<intervals-prefix,`prefix`>> * <<intervals-prefix,`prefix`>>
* <<intervals-wildcard,`wildcard`>> * <<intervals-wildcard,`wildcard`>>
* <<intervals-regexp,`regexp`>>
* <<intervals-fuzzy,`fuzzy`>> * <<intervals-fuzzy,`fuzzy`>>
* <<intervals-range,`range`>>
* <<intervals-all_of,`all_of`>> * <<intervals-all_of,`all_of`>>
* <<intervals-any_of,`any_of`>> * <<intervals-any_of,`any_of`>>
-- --
@ -122,8 +124,9 @@ unstemmed ones.
==== `prefix` rule parameters ==== `prefix` rule parameters
The `prefix` rule matches terms that start with a specified set of characters. The `prefix` rule matches terms that start with a specified set of characters.
This prefix can expand to match at most 128 terms. If the prefix matches more This prefix can expand to match at most `indices.query.bool.max_clause_count`
than 128 terms, {es} returns an error. You can use the <<search-settings,search setting>> terms. If the prefix matches more terms,
{es} returns an error. You can use the
<<index-prefixes,`index-prefixes`>> option in the field mapping to avoid this <<index-prefixes,`index-prefixes`>> option in the field mapping to avoid this
limit. limit.
@ -149,7 +152,8 @@ separate `analyzer` is specified.
==== `wildcard` rule parameters ==== `wildcard` rule parameters
The `wildcard` rule matches terms using a wildcard pattern. This pattern can The `wildcard` rule matches terms using a wildcard pattern. This pattern can
expand to match at most 128 terms. If the pattern matches more than 128 terms, expand to match at most `indices.query.bool.max_clause_count`
<<search-settings,search setting>> terms. If the pattern matches more terms,
{es} returns an error. {es} returns an error.
`pattern`:: `pattern`::
@ -178,12 +182,44 @@ The `pattern` is normalized using the search analyzer from this field, unless
`analyzer` is specified separately. `analyzer` is specified separately.
-- --
[[intervals-regexp]]
==== `regexp` rule parameters
The `regexp` rule matches terms using a regular expression pattern.
This pattern can expand to match at most `indices.query.bool.max_clause_count`
<<search-settings,search setting>> terms.
If the pattern matches more terms,{es} returns an error.
`pattern`::
(Required, string) Regexp pattern used to find matching terms.
For a list of operators supported by the
`regexp` pattern, see <<regexp-syntax, Regular expression syntax>>.
WARNING: Avoid using wildcard patterns, such as `.*` or `.*?+``. This can
increase the iterations needed to find matching terms and slow search
performance.
--
`analyzer`::
(Optional, string) <<analysis, analyzer>> used to normalize the `pattern`.
Defaults to the top-level `<field>`'s analyzer.
--
`use_field`::
+
--
(Optional, string) If specified, match intervals from this field rather than the
top-level `<field>`.
The `pattern` is normalized using the search analyzer from this field, unless
`analyzer` is specified separately.
--
[[intervals-fuzzy]] [[intervals-fuzzy]]
==== `fuzzy` rule parameters ==== `fuzzy` rule parameters
The `fuzzy` rule matches terms that are similar to the provided term, within an The `fuzzy` rule matches terms that are similar to the provided term, within an
edit distance defined by <<fuzziness>>. If the fuzzy expansion matches more than edit distance defined by <<fuzziness>>. If the fuzzy expansion matches more than
128 terms, {es} returns an error. `indices.query.bool.max_clause_count`
<<search-settings,search setting>> terms, {es} returns an error.
`term`:: `term`::
(Required, string) The term to match (Required, string) The term to match
@ -214,6 +250,41 @@ The `term` is normalized using the search analyzer from this field, unless
`analyzer` is specified separately. `analyzer` is specified separately.
-- --
[[intervals-range]]
==== `range` rule parameters
The `range` rule matches terms contained within a provided range.
This range can expand to match at most `indices.query.bool.max_clause_count`
<<search-settings,search setting>> terms.
If the range matches more terms,{es} returns an error.
`gt`::
(Optional, string) Greater than: match terms greater than the provided term.
`gte`::
(Optional, string) Greater than or equal to: match terms greater than or
equal to the provided term.
`lt`::
(Optional, string) Less than: match terms less than the provided term.
`lte`::
(Optional, string) Less than or equal to: match terms less than or
equal to the provided term.
NOTE: It is required to provide one of `gt` or `gte` params.
It is required to provide one of `lt` or `lte` params.
`analyzer`::
(Optional, string) <<analysis, analyzer>> used to normalize the `pattern`.
Defaults to the top-level `<field>`'s analyzer.
`use_field`::
(Optional, string) If specified, match intervals from this field rather than the
top-level `<field>`.
[[intervals-all_of]] [[intervals-all_of]]
==== `all_of` rule parameters ==== `all_of` rule parameters

View file

@ -2814,124 +2814,129 @@
<sha256 value="015d5c229f3cd5c0ebf175c1da08d596d94043362ae9d92637d88848c90537c8" origin="Generated by Gradle"/> <sha256 value="015d5c229f3cd5c0ebf175c1da08d596d94043362ae9d92637d88848c90537c8" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-analysis-common" version="9.11.1"> <component group="org.apache.lucene" name="lucene-analysis-common" version="9.12.0">
<artifact name="lucene-analysis-common-9.11.1.jar"> <artifact name="lucene-analysis-common-9.12.0.jar">
<sha256 value="8dfda34e75bc53906a611bae1b5870b38fd5aa3779a390565da40aa71e98d5d9" origin="Generated by Gradle"/> <sha256 value="8c79d8741f711cc6d7501805b03f7b5f505805d09cab1beb95f0be24b6d27655" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-analysis-icu" version="9.11.1"> <component group="org.apache.lucene" name="lucene-analysis-icu" version="9.12.0">
<artifact name="lucene-analysis-icu-9.11.1.jar"> <artifact name="lucene-analysis-icu-9.12.0.jar">
<sha256 value="caf464abb2cabf9b875dabc0354b43636239170fbe299e103662b267eded136c" origin="Generated by Gradle"/> <sha256 value="52e914cc2d50beec8af0d07f50e5dab803cb9ce8e675ffe43a2aae6102fe6e25" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-analysis-kuromoji" version="9.11.1"> <component group="org.apache.lucene" name="lucene-analysis-kuromoji" version="9.12.0">
<artifact name="lucene-analysis-kuromoji-9.11.1.jar"> <artifact name="lucene-analysis-kuromoji-9.12.0.jar">
<sha256 value="e48721f62cf61ab2084c6ad504194a1bfcf87ba7d3d0274a50466d9a2c5e0f4f" origin="Generated by Gradle"/> <sha256 value="3da0bd68c68cfe518d0e87ea49af1a9560579bca6b9a7fdf642169cb76dd6887" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-analysis-morfologik" version="9.11.1"> <component group="org.apache.lucene" name="lucene-analysis-morfologik" version="9.12.0">
<artifact name="lucene-analysis-morfologik-9.11.1.jar"> <artifact name="lucene-analysis-morfologik-9.12.0.jar">
<sha256 value="ba7f25bcb2face90824d3fe9f9ae6cf258d512b6f2b3810e4a7f68e52261dd97" origin="Generated by Gradle"/> <sha256 value="920358e33d806b5c3feb3566795714e1f36a616520bd2bd5da1e8b639c1f8549" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-analysis-nori" version="9.11.1"> <component group="org.apache.lucene" name="lucene-analysis-nori" version="9.12.0">
<artifact name="lucene-analysis-nori-9.11.1.jar"> <artifact name="lucene-analysis-nori-9.12.0.jar">
<sha256 value="de2092a41c4aeb135e8e240b977dabddf44e558926a836af0c8f3d81b99042dd" origin="Generated by Gradle"/> <sha256 value="26cacbe1d75e138ac0ba1e392edb1877c10a422f98524bf51d5d77492272889d" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-analysis-phonetic" version="9.11.1"> <component group="org.apache.lucene" name="lucene-analysis-phonetic" version="9.12.0">
<artifact name="lucene-analysis-phonetic-9.11.1.jar"> <artifact name="lucene-analysis-phonetic-9.12.0.jar">
<sha256 value="eb769dd82361eb9bc749d7789f45417f62e70ca4551928bece68e01a2978dbaa" origin="Generated by Gradle"/> <sha256 value="4bb24414425e04ccd28c835e8dd7f52259185f8f671aea6432460b4b7afa5cfa" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-analysis-smartcn" version="9.11.1"> <component group="org.apache.lucene" name="lucene-analysis-smartcn" version="9.12.0">
<artifact name="lucene-analysis-smartcn-9.11.1.jar"> <artifact name="lucene-analysis-smartcn-9.12.0.jar">
<sha256 value="d574ad04cf5c0154a3f6d59b5ffcd1428304da5bf16f6f94dd48ebd1c6934f51" origin="Generated by Gradle"/> <sha256 value="ffd68804d3387968784b7f52b7c14c7439fa474ffbf1b36d93fa1d2b04101f7a" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-analysis-stempel" version="9.11.1"> <component group="org.apache.lucene" name="lucene-analysis-stempel" version="9.12.0">
<artifact name="lucene-analysis-stempel-9.11.1.jar"> <artifact name="lucene-analysis-stempel-9.12.0.jar">
<sha256 value="21a96efd877ef74fbd090f95bcc89a3999d703806cf56ac16433812957de6142" origin="Generated by Gradle"/> <sha256 value="c4911b5697c0403b694aa84d3f63090067c74ffa8c95cb088d0b80e91166df4a" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-backward-codecs" version="9.11.1"> <component group="org.apache.lucene" name="lucene-backward-codecs" version="9.12.0">
<artifact name="lucene-backward-codecs-9.11.1.jar"> <artifact name="lucene-backward-codecs-9.12.0.jar">
<sha256 value="d542520817bc9e30b4ece418b36696cfa913425ba6ccdabcb1b5250c08316556" origin="Generated by Gradle"/> <sha256 value="a42c17f304c9bf1e8411ebdcf91baf2df2d00c7bfa13bca9e41ccf381d78cfba" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-codecs" version="9.11.1"> <component group="org.apache.lucene" name="lucene-codecs" version="9.12.0">
<artifact name="lucene-codecs-9.11.1.jar"> <artifact name="lucene-codecs-9.12.0.jar">
<sha256 value="3259aa9e06ea96cfb57b1929eb35a0228da51a334f452b9bbcc8f009eae6dc6d" origin="Generated by Gradle"/> <sha256 value="8173a45b87df23a6dd279916cf16b361c0dcfeff5927121b014d210b19f17555" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-core" version="9.11.1"> <component group="org.apache.lucene" name="lucene-core" version="9.12.0">
<artifact name="lucene-core-9.11.1.jar"> <artifact name="lucene-core-9.12.0.jar">
<sha256 value="cb7a9b121bce4ce054ab690ab43ac13ee11ae516d6cef67650130066beee7c9b" origin="Generated by Gradle"/> <sha256 value="6c7b774b75cd8f369e246f365a47caa54ae991cae6afa49c7f339e9921ca58a0" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-expressions" version="9.11.1"> <component group="org.apache.lucene" name="lucene-expressions" version="9.12.0">
<artifact name="lucene-expressions-9.11.1.jar"> <artifact name="lucene-expressions-9.12.0.jar">
<sha256 value="40637d9b983d4dd004b4e40b1facac6d446e6b85095b2caa499840c6eb62c693" origin="Generated by Gradle"/> <sha256 value="9d14919a3d0f07420c1d57a68de52180ae2477d6255d7f693f44515a4fda9c9b" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-grouping" version="9.11.1"> <component group="org.apache.lucene" name="lucene-facet" version="9.12.0">
<artifact name="lucene-grouping-9.11.1.jar"> <artifact name="lucene-facet-9.12.0.jar">
<sha256 value="53fcb43e1a032001fc5ad207b8bf43d4fa13a88309f21761cb12caa0f61027fb" origin="Generated by Gradle"/> <sha256 value="3c07754f0a1f45ba42a637faa8568c18507defa6b77a1ed71af291cd4e7ac603" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-highlighter" version="9.11.1"> <component group="org.apache.lucene" name="lucene-grouping" version="9.12.0">
<artifact name="lucene-highlighter-9.11.1.jar"> <artifact name="lucene-grouping-9.12.0.jar">
<sha256 value="af91d9a44a6519e6283ec004cf751dbd429018ae80881dd5073b3862fdfd404d" origin="Generated by Gradle"/> <sha256 value="ffc02d047f4f036c5a5f409baaf8e19abca165deccc3e57e7a422560f4de1219" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-join" version="9.11.1"> <component group="org.apache.lucene" name="lucene-highlighter" version="9.12.0">
<artifact name="lucene-join-9.11.1.jar"> <artifact name="lucene-highlighter-9.12.0.jar">
<sha256 value="f2c751e8e1dbc788dc890c25f81930af95fa082f4ced4f977058385c39d92d15" origin="Generated by Gradle"/> <sha256 value="5094e1b38f02eb72500a4283ac0f654e2e81943c5359b629567dd7900c935a59" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-memory" version="9.11.1"> <component group="org.apache.lucene" name="lucene-join" version="9.12.0">
<artifact name="lucene-memory-9.11.1.jar"> <artifact name="lucene-join-9.12.0.jar">
<sha256 value="3ed0343383ae05a27adba87a841856a7c4ff230ae6d3c388d21e49e03c0524de" origin="Generated by Gradle"/> <sha256 value="c49cb1be843295b2c7158c03755f222416fad9915114bc7f74649d20f4fc43d6" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-misc" version="9.11.1"> <component group="org.apache.lucene" name="lucene-memory" version="9.12.0">
<artifact name="lucene-misc-9.11.1.jar"> <artifact name="lucene-memory-9.12.0.jar">
<sha256 value="03dc8018b503fb32e7c0c0e36dbefeaf4cdd20cccf8fb2766b81a383d6963c1b" origin="Generated by Gradle"/> <sha256 value="fdbb9c9b3270ce06370d094f1a80dbbbf81b2638b55fa14cc817469092899f01" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-queries" version="9.11.1"> <component group="org.apache.lucene" name="lucene-misc" version="9.12.0">
<artifact name="lucene-queries-9.11.1.jar"> <artifact name="lucene-misc-9.12.0.jar">
<sha256 value="b3787d26fcadf362f3be77aa4da42ad41e1cf5bf49f040eee731ea95d28acd5a" origin="Generated by Gradle"/> <sha256 value="7989d094b4b168ce9ef2949e13f6bfe820829944e50831881be1adeddbe02c04" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-queryparser" version="9.11.1"> <component group="org.apache.lucene" name="lucene-queries" version="9.12.0">
<artifact name="lucene-queryparser-9.11.1.jar"> <artifact name="lucene-queries-9.12.0.jar">
<sha256 value="63e3f7d0cd7a08975d0cba31d2898de7fa00dc9f3677e37820f4413824538898" origin="Generated by Gradle"/> <sha256 value="3b4da6b1673e4edd141c7b86538cc61f6ff3386106f1f5e179c9fe2d30ea2fca" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-sandbox" version="9.11.1"> <component group="org.apache.lucene" name="lucene-queryparser" version="9.12.0">
<artifact name="lucene-sandbox-9.11.1.jar"> <artifact name="lucene-queryparser-9.12.0.jar">
<sha256 value="a1a9118709d5cc52ca160f439c32280f3ba54ec3ba6e3a0db0c1a73a3d790fdb" origin="Generated by Gradle"/> <sha256 value="fc866b7bbfc199436feb24be7dd5c575445342eb283bfe07dd519eab131d3675" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-spatial-extras" version="9.11.1"> <component group="org.apache.lucene" name="lucene-sandbox" version="9.12.0">
<artifact name="lucene-spatial-extras-9.11.1.jar"> <artifact name="lucene-sandbox-9.12.0.jar">
<sha256 value="72760a0247b64869a27bf754473a67f32374ca3214bf47053f54f716c8666755" origin="Generated by Gradle"/> <sha256 value="2262f39a241f435dba61f230e7528205c34f4bf0008faa57f0808476bb782996" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-spatial3d" version="9.11.1"> <component group="org.apache.lucene" name="lucene-spatial-extras" version="9.12.0">
<artifact name="lucene-spatial3d-9.11.1.jar"> <artifact name="lucene-spatial-extras-9.12.0.jar">
<sha256 value="3e485ee6b2b40898118c87f7b7c9376e8cf787966272aac57e1ef70528e5d7ab" origin="Generated by Gradle"/> <sha256 value="ea41de3b2da573e488033b8b1c2033e17c27ce39957cf6acb8904297ae6e1747" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-suggest" version="9.11.1"> <component group="org.apache.lucene" name="lucene-spatial3d" version="9.12.0">
<artifact name="lucene-suggest-9.11.1.jar"> <artifact name="lucene-spatial3d-9.12.0.jar">
<sha256 value="ce03d7178182694a14ed0433cff532a44b3647cec8d9c1ac541afe7887972105" origin="Generated by Gradle"/> <sha256 value="4263e163937024838a8761618b1ce4eba06857adca0c9385d1ac014ec2937de6" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.lucene" name="lucene-test-framework" version="9.11.1"> <component group="org.apache.lucene" name="lucene-suggest" version="9.12.0">
<artifact name="lucene-test-framework-9.11.1.jar"> <artifact name="lucene-suggest-9.12.0.jar">
<sha256 value="4a395a7e2a037c49ca869dd9e98693d91ea190c1390de626ccaf83e3afed0811" origin="Generated by Gradle"/> <sha256 value="1606fb9c7cf57d88b2824916c00280c92e14bcde798a6b78b27e0db8f13227f0" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.apache.lucene" name="lucene-test-framework" version="9.12.0">
<artifact name="lucene-test-framework-9.12.0.jar">
<sha256 value="47d4e1f47ebd26117d47af8fbff471c116c571fd7d7bd6cb199d89f8454e5cf0" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.apache.maven" name="maven-artifact" version="3.6.1"> <component group="org.apache.maven" name="maven-artifact" version="3.6.1">

View file

@ -237,6 +237,8 @@ public class VectorScorerFactoryTests extends AbstractVectorTestCase {
try (Directory dir = new MMapDirectory(createTempDir("testRandom"), maxChunkSize)) { try (Directory dir = new MMapDirectory(createTempDir("testRandom"), maxChunkSize)) {
for (var sim : List.of(COSINE, DOT_PRODUCT, EUCLIDEAN, MAXIMUM_INNER_PRODUCT)) { for (var sim : List.of(COSINE, DOT_PRODUCT, EUCLIDEAN, MAXIMUM_INNER_PRODUCT)) {
// Use the random supplier for COSINE, which returns values in the normalized range
floatArraySupplier = sim == COSINE ? FLOAT_ARRAY_RANDOM_FUNC : floatArraySupplier;
final int dims = randomIntBetween(1, 4096); final int dims = randomIntBetween(1, 4096);
final int size = randomIntBetween(2, 100); final int size = randomIntBetween(2, 100);
final float[][] vectors = new float[size][]; final float[][] vectors = new float[size][];

View file

@ -21,6 +21,7 @@ import org.apache.lucene.queries.intervals.Intervals;
import org.apache.lucene.queries.intervals.IntervalsSource; import org.apache.lucene.queries.intervals.IntervalsSource;
import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.PrefixQuery;
@ -270,7 +271,11 @@ public class MatchOnlyTextFieldMapper extends FieldMapper {
@Override @Override
public IntervalsSource prefixIntervals(BytesRef term, SearchExecutionContext context) { public IntervalsSource prefixIntervals(BytesRef term, SearchExecutionContext context) {
return toIntervalsSource(Intervals.prefix(term), new PrefixQuery(new Term(name(), term)), context); return toIntervalsSource(
Intervals.prefix(term, IndexSearcher.getMaxClauseCount()),
new PrefixQuery(new Term(name(), term)),
context
);
} }
@Override @Override
@ -285,23 +290,47 @@ public class MatchOnlyTextFieldMapper extends FieldMapper {
new Term(name(), term), new Term(name(), term),
maxDistance, maxDistance,
prefixLength, prefixLength,
128, IndexSearcher.getMaxClauseCount(),
transpositions, transpositions,
MultiTermQuery.CONSTANT_SCORE_BLENDED_REWRITE MultiTermQuery.CONSTANT_SCORE_BLENDED_REWRITE
); );
IntervalsSource fuzzyIntervals = Intervals.multiterm(fuzzyQuery.getAutomata(), term); IntervalsSource fuzzyIntervals = Intervals.multiterm(fuzzyQuery.getAutomata(), IndexSearcher.getMaxClauseCount(), term);
return toIntervalsSource(fuzzyIntervals, fuzzyQuery, context); return toIntervalsSource(fuzzyIntervals, fuzzyQuery, context);
} }
@Override @Override
public IntervalsSource wildcardIntervals(BytesRef pattern, SearchExecutionContext context) { public IntervalsSource wildcardIntervals(BytesRef pattern, SearchExecutionContext context) {
return toIntervalsSource( return toIntervalsSource(
Intervals.wildcard(pattern), Intervals.wildcard(pattern, IndexSearcher.getMaxClauseCount()),
new MatchAllDocsQuery(), // wildcard queries can be expensive, what should the approximation be? new MatchAllDocsQuery(), // wildcard queries can be expensive, what should the approximation be?
context context
); );
} }
@Override
public IntervalsSource regexpIntervals(BytesRef pattern, SearchExecutionContext context) {
return toIntervalsSource(
Intervals.regexp(pattern, IndexSearcher.getMaxClauseCount()),
new MatchAllDocsQuery(), // regexp queries can be expensive, what should the approximation be?
context
);
}
@Override
public IntervalsSource rangeIntervals(
BytesRef lowerTerm,
BytesRef upperTerm,
boolean includeLower,
boolean includeUpper,
SearchExecutionContext context
) {
return toIntervalsSource(
Intervals.range(lowerTerm, upperTerm, includeLower, includeUpper, IndexSearcher.getMaxClauseCount()),
new MatchAllDocsQuery(), // range queries can be expensive, what should the approximation be?
context
);
}
@Override @Override
public Query phraseQuery(TokenStream stream, int slop, boolean enablePosIncrements, SearchExecutionContext queryShardContext) public Query phraseQuery(TokenStream stream, int slop, boolean enablePosIncrements, SearchExecutionContext queryShardContext)
throws IOException { throws IOException {

View file

@ -14,6 +14,7 @@ import org.apache.lucene.queries.intervals.Intervals;
import org.apache.lucene.queries.intervals.IntervalsSource; import org.apache.lucene.queries.intervals.IntervalsSource;
import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MultiPhraseQuery; import org.apache.lucene.search.MultiPhraseQuery;
import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.PhraseQuery;
@ -152,30 +153,56 @@ public class MatchOnlyTextFieldTypeTests extends FieldTypeTestCase {
assertNotEquals(new MatchAllDocsQuery(), SourceConfirmedTextQuery.approximate(delegate)); assertNotEquals(new MatchAllDocsQuery(), SourceConfirmedTextQuery.approximate(delegate));
} }
public void testTermIntervals() throws IOException { public void testTermIntervals() {
MappedFieldType ft = new MatchOnlyTextFieldType("field"); MappedFieldType ft = new MatchOnlyTextFieldType("field");
IntervalsSource termIntervals = ft.termIntervals(new BytesRef("foo"), MOCK_CONTEXT); IntervalsSource termIntervals = ft.termIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertThat(termIntervals, Matchers.instanceOf(SourceIntervalsSource.class)); assertThat(termIntervals, Matchers.instanceOf(SourceIntervalsSource.class));
assertEquals(Intervals.term(new BytesRef("foo")), ((SourceIntervalsSource) termIntervals).getIntervalsSource()); assertEquals(Intervals.term(new BytesRef("foo")), ((SourceIntervalsSource) termIntervals).getIntervalsSource());
} }
public void testPrefixIntervals() throws IOException { public void testPrefixIntervals() {
MappedFieldType ft = new MatchOnlyTextFieldType("field"); MappedFieldType ft = new MatchOnlyTextFieldType("field");
IntervalsSource prefixIntervals = ft.prefixIntervals(new BytesRef("foo"), MOCK_CONTEXT); IntervalsSource prefixIntervals = ft.prefixIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertThat(prefixIntervals, Matchers.instanceOf(SourceIntervalsSource.class)); assertThat(prefixIntervals, Matchers.instanceOf(SourceIntervalsSource.class));
assertEquals(Intervals.prefix(new BytesRef("foo")), ((SourceIntervalsSource) prefixIntervals).getIntervalsSource()); assertEquals(
Intervals.prefix(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()),
((SourceIntervalsSource) prefixIntervals).getIntervalsSource()
);
} }
public void testWildcardIntervals() throws IOException { public void testWildcardIntervals() {
MappedFieldType ft = new MatchOnlyTextFieldType("field"); MappedFieldType ft = new MatchOnlyTextFieldType("field");
IntervalsSource wildcardIntervals = ft.wildcardIntervals(new BytesRef("foo"), MOCK_CONTEXT); IntervalsSource wildcardIntervals = ft.wildcardIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertThat(wildcardIntervals, Matchers.instanceOf(SourceIntervalsSource.class)); assertThat(wildcardIntervals, Matchers.instanceOf(SourceIntervalsSource.class));
assertEquals(Intervals.wildcard(new BytesRef("foo")), ((SourceIntervalsSource) wildcardIntervals).getIntervalsSource()); assertEquals(
Intervals.wildcard(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()),
((SourceIntervalsSource) wildcardIntervals).getIntervalsSource()
);
} }
public void testFuzzyIntervals() throws IOException { public void testRegexpIntervals() {
MappedFieldType ft = new MatchOnlyTextFieldType("field");
IntervalsSource regexpIntervals = ft.regexpIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertThat(regexpIntervals, Matchers.instanceOf(SourceIntervalsSource.class));
assertEquals(
Intervals.regexp(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()),
((SourceIntervalsSource) regexpIntervals).getIntervalsSource()
);
}
public void testFuzzyIntervals() {
MappedFieldType ft = new MatchOnlyTextFieldType("field"); MappedFieldType ft = new MatchOnlyTextFieldType("field");
IntervalsSource fuzzyIntervals = ft.fuzzyIntervals("foo", 1, 2, true, MOCK_CONTEXT); IntervalsSource fuzzyIntervals = ft.fuzzyIntervals("foo", 1, 2, true, MOCK_CONTEXT);
assertThat(fuzzyIntervals, Matchers.instanceOf(SourceIntervalsSource.class)); assertThat(fuzzyIntervals, Matchers.instanceOf(SourceIntervalsSource.class));
} }
public void testRangeIntervals() {
MappedFieldType ft = new MatchOnlyTextFieldType("field");
IntervalsSource rangeIntervals = ft.rangeIntervals(new BytesRef("foo"), new BytesRef("foo1"), true, true, MOCK_CONTEXT);
assertThat(rangeIntervals, Matchers.instanceOf(SourceIntervalsSource.class));
assertEquals(
Intervals.range(new BytesRef("foo"), new BytesRef("foo1"), true, true, IndexSearcher.getMaxClauseCount()),
((SourceIntervalsSource) rangeIntervals).getIntervalsSource()
);
}
} }

View file

@ -10,7 +10,7 @@ apply plugin: 'elasticsearch.internal-yaml-rest-test'
restResources { restResources {
restApi { restApi {
include '_common', 'bulk', 'count', 'cluster', 'field_caps', 'get', 'knn_search', 'index', 'indices', 'msearch', include 'capabilities', '_common', 'bulk', 'count', 'cluster', 'field_caps', 'get', 'knn_search', 'index', 'indices', 'msearch',
'search', 'async_search', 'graph', '*_point_in_time', 'info', 'scroll', 'clear_scroll', 'search_mvt', 'eql', 'sql' 'search', 'async_search', 'graph', '*_point_in_time', 'info', 'scroll', 'clear_scroll', 'search_mvt', 'eql', 'sql'
} }
restTests { restTests {

View file

@ -476,3 +476,53 @@ setup:
- match: { hits.hits.0._id: "6" } - match: { hits.hits.0._id: "6" }
- match: { hits.hits.1._id: "5" } - match: { hits.hits.1._id: "5" }
---
"Test regexp":
- requires:
capabilities:
- method: POST
path: /_search
capabilities: [ range_regexp_interval_queries ]
test_runner_features: capabilities
reason: "Support for range and regexp interval queries capability required"
- do:
search:
index: test
body:
query:
intervals:
text:
all_of:
intervals:
- match:
query: cold
- regexp:
pattern: ou.*ide
- match: { hits.total.value: 3 }
---
"Test range":
- requires:
capabilities:
- method: POST
path: /_search
capabilities: [ range_regexp_interval_queries ]
test_runner_features: capabilities
reason: "Support for range and regexp interval queries capability required"
- do:
search:
index: test
body:
query:
intervals:
text:
all_of:
intervals:
- match:
query: cold
- range:
gte: out
lte: ouu
- match: { hits.total.value: 3 }

View file

@ -44,6 +44,7 @@ dependencies {
api "org.apache.lucene:lucene-core:${versions.lucene}" api "org.apache.lucene:lucene-core:${versions.lucene}"
api "org.apache.lucene:lucene-analysis-common:${versions.lucene}" api "org.apache.lucene:lucene-analysis-common:${versions.lucene}"
api "org.apache.lucene:lucene-backward-codecs:${versions.lucene}" api "org.apache.lucene:lucene-backward-codecs:${versions.lucene}"
api "org.apache.lucene:lucene-facet:${versions.lucene}"
api "org.apache.lucene:lucene-grouping:${versions.lucene}" api "org.apache.lucene:lucene-grouping:${versions.lucene}"
api "org.apache.lucene:lucene-highlighter:${versions.lucene}" api "org.apache.lucene:lucene-highlighter:${versions.lucene}"
api "org.apache.lucene:lucene-join:${versions.lucene}" api "org.apache.lucene:lucene-join:${versions.lucene}"

View file

@ -7,7 +7,6 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
import org.elasticsearch.index.codec.Elasticsearch814Codec;
import org.elasticsearch.index.codec.tsdb.ES87TSDBDocValuesFormat; import org.elasticsearch.index.codec.tsdb.ES87TSDBDocValuesFormat;
import org.elasticsearch.plugins.internal.RestExtension; import org.elasticsearch.plugins.internal.RestExtension;
@ -455,7 +454,10 @@ module org.elasticsearch.server {
org.elasticsearch.index.codec.vectors.ES815HnswBitVectorsFormat, org.elasticsearch.index.codec.vectors.ES815HnswBitVectorsFormat,
org.elasticsearch.index.codec.vectors.ES815BitFlatVectorFormat; org.elasticsearch.index.codec.vectors.ES815BitFlatVectorFormat;
provides org.apache.lucene.codecs.Codec with Elasticsearch814Codec; provides org.apache.lucene.codecs.Codec
with
org.elasticsearch.index.codec.Elasticsearch814Codec,
org.elasticsearch.index.codec.Elasticsearch816Codec;
provides org.apache.logging.log4j.core.util.ContextDataProvider with org.elasticsearch.common.logging.DynamicContextDataProvider; provides org.apache.logging.log4j.core.util.ContextDataProvider with org.elasticsearch.common.logging.DynamicContextDataProvider;

View file

@ -230,6 +230,7 @@ public class TransportVersions {
public static final TransportVersion ADD_DATA_STREAM_OPTIONS = def(8_754_00_0); public static final TransportVersion ADD_DATA_STREAM_OPTIONS = def(8_754_00_0);
public static final TransportVersion CCS_REMOTE_TELEMETRY_STATS = def(8_755_00_0); public static final TransportVersion CCS_REMOTE_TELEMETRY_STATS = def(8_755_00_0);
public static final TransportVersion ESQL_CCS_EXECUTION_INFO = def(8_756_00_0); public static final TransportVersion ESQL_CCS_EXECUTION_INFO = def(8_756_00_0);
public static final TransportVersion REGEX_AND_RANGE_INTERVAL_QUERIES = def(8_757_00_0);
/* /*
* STOP! READ THIS FIRST! No, really, * STOP! READ THIS FIRST! No, really,

View file

@ -13,6 +13,7 @@ import org.apache.logging.log4j.Logger;
import org.apache.lucene.backward_codecs.lucene50.Lucene50PostingsFormat; import org.apache.lucene.backward_codecs.lucene50.Lucene50PostingsFormat;
import org.apache.lucene.backward_codecs.lucene84.Lucene84PostingsFormat; import org.apache.lucene.backward_codecs.lucene84.Lucene84PostingsFormat;
import org.apache.lucene.backward_codecs.lucene90.Lucene90PostingsFormat; import org.apache.lucene.backward_codecs.lucene90.Lucene90PostingsFormat;
import org.apache.lucene.backward_codecs.lucene99.Lucene99PostingsFormat;
import org.apache.lucene.codecs.DocValuesProducer; import org.apache.lucene.codecs.DocValuesProducer;
import org.apache.lucene.codecs.FieldsProducer; import org.apache.lucene.codecs.FieldsProducer;
import org.apache.lucene.codecs.KnnVectorsReader; import org.apache.lucene.codecs.KnnVectorsReader;
@ -20,7 +21,7 @@ import org.apache.lucene.codecs.NormsProducer;
import org.apache.lucene.codecs.PointsReader; import org.apache.lucene.codecs.PointsReader;
import org.apache.lucene.codecs.StoredFieldsReader; import org.apache.lucene.codecs.StoredFieldsReader;
import org.apache.lucene.codecs.TermVectorsReader; import org.apache.lucene.codecs.TermVectorsReader;
import org.apache.lucene.codecs.lucene99.Lucene99PostingsFormat; import org.apache.lucene.codecs.lucene912.Lucene912PostingsFormat;
import org.apache.lucene.index.BinaryDocValues; import org.apache.lucene.index.BinaryDocValues;
import org.apache.lucene.index.ByteVectorValues; import org.apache.lucene.index.ByteVectorValues;
import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.DirectoryReader;
@ -304,6 +305,9 @@ final class IndexDiskUsageAnalyzer {
private static BlockTermState getBlockTermState(TermsEnum termsEnum, BytesRef term) throws IOException { private static BlockTermState getBlockTermState(TermsEnum termsEnum, BytesRef term) throws IOException {
if (term != null && termsEnum.seekExact(term)) { if (term != null && termsEnum.seekExact(term)) {
final TermState termState = termsEnum.termState(); final TermState termState = termsEnum.termState();
if (termState instanceof final Lucene912PostingsFormat.IntBlockTermState blockTermState) {
return new BlockTermState(blockTermState.docStartFP, blockTermState.posStartFP, blockTermState.payStartFP);
}
if (termState instanceof final ES812PostingsFormat.IntBlockTermState blockTermState) { if (termState instanceof final ES812PostingsFormat.IntBlockTermState blockTermState) {
return new BlockTermState(blockTermState.docStartFP, blockTermState.posStartFP, blockTermState.payStartFP); return new BlockTermState(blockTermState.docStartFP, blockTermState.posStartFP, blockTermState.payStartFP);
} }

View file

@ -89,7 +89,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
public class Lucene { public class Lucene {
public static final String LATEST_CODEC = "Lucene99"; public static final String LATEST_CODEC = "Lucene912";
public static final String SOFT_DELETES_FIELD = "__soft_deletes"; public static final String SOFT_DELETES_FIELD = "__soft_deletes";
@ -242,7 +242,7 @@ public class Lucene {
@Override @Override
protected Object doBody(String segmentFileName) throws IOException { protected Object doBody(String segmentFileName) throws IOException {
try (IndexInput input = directory.openInput(segmentFileName, IOContext.READ)) { try (IndexInput input = directory.openInput(segmentFileName, IOContext.READONCE)) {
CodecUtil.checksumEntireFile(input); CodecUtil.checksumEntireFile(input);
} }
return null; return null;

View file

@ -116,6 +116,7 @@ public class IndexVersions {
public static final IndexVersion LENIENT_UPDATEABLE_SYNONYMS = def(8_513_00_0, Version.LUCENE_9_11_1); public static final IndexVersion LENIENT_UPDATEABLE_SYNONYMS = def(8_513_00_0, Version.LUCENE_9_11_1);
public static final IndexVersion ENABLE_IGNORE_MALFORMED_LOGSDB = def(8_514_00_0, Version.LUCENE_9_11_1); public static final IndexVersion ENABLE_IGNORE_MALFORMED_LOGSDB = def(8_514_00_0, Version.LUCENE_9_11_1);
public static final IndexVersion MERGE_ON_RECOVERY_VERSION = def(8_515_00_0, Version.LUCENE_9_11_1); public static final IndexVersion MERGE_ON_RECOVERY_VERSION = def(8_515_00_0, Version.LUCENE_9_11_1);
public static final IndexVersion UPGRADE_TO_LUCENE_9_12 = def(8_516_00_0, Version.LUCENE_9_12_0);
/* /*
* STOP! READ THIS FIRST! No, really, * STOP! READ THIS FIRST! No, really,

View file

@ -12,7 +12,7 @@ package org.elasticsearch.index.codec;
import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.Codec;
import org.apache.lucene.codecs.FieldInfosFormat; import org.apache.lucene.codecs.FieldInfosFormat;
import org.apache.lucene.codecs.FilterCodec; import org.apache.lucene.codecs.FilterCodec;
import org.apache.lucene.codecs.lucene99.Lucene99Codec; import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.FeatureFlag; import org.elasticsearch.common.util.FeatureFlag;
import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Nullable;
@ -46,7 +46,7 @@ public class CodecService implements CodecProvider {
public CodecService(@Nullable MapperService mapperService, BigArrays bigArrays) { public CodecService(@Nullable MapperService mapperService, BigArrays bigArrays) {
final var codecs = new HashMap<String, Codec>(); final var codecs = new HashMap<String, Codec>();
Codec legacyBestSpeedCodec = new LegacyPerFieldMapperCodec(Lucene99Codec.Mode.BEST_SPEED, mapperService, bigArrays); Codec legacyBestSpeedCodec = new LegacyPerFieldMapperCodec(Lucene912Codec.Mode.BEST_SPEED, mapperService, bigArrays);
if (ZSTD_STORED_FIELDS_FEATURE_FLAG.isEnabled()) { if (ZSTD_STORED_FIELDS_FEATURE_FLAG.isEnabled()) {
codecs.put(DEFAULT_CODEC, new PerFieldMapperCodec(Zstd814StoredFieldsFormat.Mode.BEST_SPEED, mapperService, bigArrays)); codecs.put(DEFAULT_CODEC, new PerFieldMapperCodec(Zstd814StoredFieldsFormat.Mode.BEST_SPEED, mapperService, bigArrays));
} else { } else {
@ -58,7 +58,7 @@ public class CodecService implements CodecProvider {
BEST_COMPRESSION_CODEC, BEST_COMPRESSION_CODEC,
new PerFieldMapperCodec(Zstd814StoredFieldsFormat.Mode.BEST_COMPRESSION, mapperService, bigArrays) new PerFieldMapperCodec(Zstd814StoredFieldsFormat.Mode.BEST_COMPRESSION, mapperService, bigArrays)
); );
Codec legacyBestCompressionCodec = new LegacyPerFieldMapperCodec(Lucene99Codec.Mode.BEST_COMPRESSION, mapperService, bigArrays); Codec legacyBestCompressionCodec = new LegacyPerFieldMapperCodec(Lucene912Codec.Mode.BEST_COMPRESSION, mapperService, bigArrays);
codecs.put(LEGACY_BEST_COMPRESSION_CODEC, legacyBestCompressionCodec); codecs.put(LEGACY_BEST_COMPRESSION_CODEC, legacyBestCompressionCodec);
codecs.put(LUCENE_DEFAULT_CODEC, Codec.getDefault()); codecs.put(LUCENE_DEFAULT_CODEC, Codec.getDefault());

View file

@ -9,14 +9,14 @@
package org.elasticsearch.index.codec; package org.elasticsearch.index.codec;
import org.apache.lucene.backward_codecs.lucene99.Lucene99Codec;
import org.apache.lucene.backward_codecs.lucene99.Lucene99PostingsFormat;
import org.apache.lucene.codecs.DocValuesFormat; import org.apache.lucene.codecs.DocValuesFormat;
import org.apache.lucene.codecs.KnnVectorsFormat; import org.apache.lucene.codecs.KnnVectorsFormat;
import org.apache.lucene.codecs.PostingsFormat; import org.apache.lucene.codecs.PostingsFormat;
import org.apache.lucene.codecs.StoredFieldsFormat; import org.apache.lucene.codecs.StoredFieldsFormat;
import org.apache.lucene.codecs.lucene90.Lucene90DocValuesFormat; import org.apache.lucene.codecs.lucene90.Lucene90DocValuesFormat;
import org.apache.lucene.codecs.lucene99.Lucene99Codec;
import org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat; import org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat;
import org.apache.lucene.codecs.lucene99.Lucene99PostingsFormat;
import org.apache.lucene.codecs.perfield.PerFieldDocValuesFormat; import org.apache.lucene.codecs.perfield.PerFieldDocValuesFormat;
import org.apache.lucene.codecs.perfield.PerFieldKnnVectorsFormat; import org.apache.lucene.codecs.perfield.PerFieldKnnVectorsFormat;
import org.apache.lucene.codecs.perfield.PerFieldPostingsFormat; import org.apache.lucene.codecs.perfield.PerFieldPostingsFormat;

View file

@ -0,0 +1,131 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
package org.elasticsearch.index.codec;
import org.apache.lucene.codecs.DocValuesFormat;
import org.apache.lucene.codecs.KnnVectorsFormat;
import org.apache.lucene.codecs.PostingsFormat;
import org.apache.lucene.codecs.StoredFieldsFormat;
import org.apache.lucene.codecs.lucene90.Lucene90DocValuesFormat;
import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.apache.lucene.codecs.lucene912.Lucene912PostingsFormat;
import org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat;
import org.apache.lucene.codecs.perfield.PerFieldDocValuesFormat;
import org.apache.lucene.codecs.perfield.PerFieldKnnVectorsFormat;
import org.apache.lucene.codecs.perfield.PerFieldPostingsFormat;
import org.elasticsearch.index.codec.zstd.Zstd814StoredFieldsFormat;
/**
* Elasticsearch codec as of 8.16. This extends the Lucene 9.12 codec to compressed stored fields with ZSTD instead of LZ4/DEFLATE. See
* {@link Zstd814StoredFieldsFormat}.
*/
public class Elasticsearch816Codec extends CodecService.DeduplicateFieldInfosCodec {
private final StoredFieldsFormat storedFieldsFormat;
private final PostingsFormat defaultPostingsFormat;
private final PostingsFormat postingsFormat = new PerFieldPostingsFormat() {
@Override
public PostingsFormat getPostingsFormatForField(String field) {
return Elasticsearch816Codec.this.getPostingsFormatForField(field);
}
};
private final DocValuesFormat defaultDVFormat;
private final DocValuesFormat docValuesFormat = new PerFieldDocValuesFormat() {
@Override
public DocValuesFormat getDocValuesFormatForField(String field) {
return Elasticsearch816Codec.this.getDocValuesFormatForField(field);
}
};
private final KnnVectorsFormat defaultKnnVectorsFormat;
private final KnnVectorsFormat knnVectorsFormat = new PerFieldKnnVectorsFormat() {
@Override
public KnnVectorsFormat getKnnVectorsFormatForField(String field) {
return Elasticsearch816Codec.this.getKnnVectorsFormatForField(field);
}
};
/** Public no-arg constructor, needed for SPI loading at read-time. */
public Elasticsearch816Codec() {
this(Zstd814StoredFieldsFormat.Mode.BEST_SPEED);
}
/**
* Constructor. Takes a {@link Zstd814StoredFieldsFormat.Mode} that describes whether to optimize for retrieval speed at the expense of
* worse space-efficiency or vice-versa.
*/
public Elasticsearch816Codec(Zstd814StoredFieldsFormat.Mode mode) {
super("Elasticsearch816", new Lucene912Codec());
this.storedFieldsFormat = new Zstd814StoredFieldsFormat(mode);
this.defaultPostingsFormat = new Lucene912PostingsFormat();
this.defaultDVFormat = new Lucene90DocValuesFormat();
this.defaultKnnVectorsFormat = new Lucene99HnswVectorsFormat();
}
@Override
public StoredFieldsFormat storedFieldsFormat() {
return storedFieldsFormat;
}
@Override
public final PostingsFormat postingsFormat() {
return postingsFormat;
}
@Override
public final DocValuesFormat docValuesFormat() {
return docValuesFormat;
}
@Override
public final KnnVectorsFormat knnVectorsFormat() {
return knnVectorsFormat;
}
/**
* Returns the postings format that should be used for writing new segments of <code>field</code>.
*
* <p>The default implementation always returns "Lucene912".
*
* <p><b>WARNING:</b> if you subclass, you are responsible for index backwards compatibility:
* future version of Lucene are only guaranteed to be able to read the default implementation,
*/
public PostingsFormat getPostingsFormatForField(String field) {
return defaultPostingsFormat;
}
/**
* Returns the docvalues format that should be used for writing new segments of <code>field</code>
* .
*
* <p>The default implementation always returns "Lucene912".
*
* <p><b>WARNING:</b> if you subclass, you are responsible for index backwards compatibility:
* future version of Lucene are only guaranteed to be able to read the default implementation.
*/
public DocValuesFormat getDocValuesFormatForField(String field) {
return defaultDVFormat;
}
/**
* Returns the vectors format that should be used for writing new segments of <code>field</code>
*
* <p>The default implementation always returns "Lucene912".
*
* <p><b>WARNING:</b> if you subclass, you are responsible for index backwards compatibility:
* future version of Lucene are only guaranteed to be able to read the default implementation.
*/
public KnnVectorsFormat getKnnVectorsFormatForField(String field) {
return defaultKnnVectorsFormat;
}
}

View file

@ -13,7 +13,7 @@ import org.apache.lucene.codecs.Codec;
import org.apache.lucene.codecs.DocValuesFormat; import org.apache.lucene.codecs.DocValuesFormat;
import org.apache.lucene.codecs.KnnVectorsFormat; import org.apache.lucene.codecs.KnnVectorsFormat;
import org.apache.lucene.codecs.PostingsFormat; import org.apache.lucene.codecs.PostingsFormat;
import org.apache.lucene.codecs.lucene99.Lucene99Codec; import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
@ -22,11 +22,11 @@ import org.elasticsearch.index.mapper.MapperService;
* Legacy version of {@link PerFieldMapperCodec}. This codec is preserved to give an escape hatch in case we encounter issues with new * Legacy version of {@link PerFieldMapperCodec}. This codec is preserved to give an escape hatch in case we encounter issues with new
* changes in {@link PerFieldMapperCodec}. * changes in {@link PerFieldMapperCodec}.
*/ */
public final class LegacyPerFieldMapperCodec extends Lucene99Codec { public final class LegacyPerFieldMapperCodec extends Lucene912Codec {
private final PerFieldFormatSupplier formatSupplier; private final PerFieldFormatSupplier formatSupplier;
public LegacyPerFieldMapperCodec(Lucene99Codec.Mode compressionMode, MapperService mapperService, BigArrays bigArrays) { public LegacyPerFieldMapperCodec(Lucene912Codec.Mode compressionMode, MapperService mapperService, BigArrays bigArrays) {
super(compressionMode); super(compressionMode);
this.formatSupplier = new PerFieldFormatSupplier(mapperService, bigArrays); this.formatSupplier = new PerFieldFormatSupplier(mapperService, bigArrays);
// If the below assertion fails, it is a sign that Lucene released a new codec. You must create a copy of the current Elasticsearch // If the below assertion fails, it is a sign that Lucene released a new codec. You must create a copy of the current Elasticsearch

View file

@ -26,7 +26,7 @@ import org.elasticsearch.index.mapper.MapperService;
* per index in real time via the mapping API. If no specific postings format or vector format is * per index in real time via the mapping API. If no specific postings format or vector format is
* configured for a specific field the default postings or vector format is used. * configured for a specific field the default postings or vector format is used.
*/ */
public final class PerFieldMapperCodec extends Elasticsearch814Codec { public final class PerFieldMapperCodec extends Elasticsearch816Codec {
private final PerFieldFormatSupplier formatSupplier; private final PerFieldFormatSupplier formatSupplier;

View file

@ -66,7 +66,7 @@ public class ES813FlatVectorFormat extends KnnVectorsFormat {
@Override @Override
public KnnFieldVectorsWriter<?> addField(FieldInfo fieldInfo) throws IOException { public KnnFieldVectorsWriter<?> addField(FieldInfo fieldInfo) throws IOException {
return writer.addField(fieldInfo, null); return writer.addField(fieldInfo);
} }
@Override @Override

View file

@ -74,7 +74,7 @@ public class ES813Int8FlatVectorFormat extends KnnVectorsFormat {
@Override @Override
public KnnFieldVectorsWriter<?> addField(FieldInfo fieldInfo) throws IOException { public KnnFieldVectorsWriter<?> addField(FieldInfo fieldInfo) throws IOException {
return writer.addField(fieldInfo, null); return writer.addField(fieldInfo);
} }
@Override @Override

View file

@ -9,7 +9,6 @@
package org.elasticsearch.index.codec.vectors; package org.elasticsearch.index.codec.vectors;
import org.apache.lucene.codecs.KnnFieldVectorsWriter;
import org.apache.lucene.codecs.hnsw.DefaultFlatVectorScorer; import org.apache.lucene.codecs.hnsw.DefaultFlatVectorScorer;
import org.apache.lucene.codecs.hnsw.FlatFieldVectorsWriter; import org.apache.lucene.codecs.hnsw.FlatFieldVectorsWriter;
import org.apache.lucene.codecs.hnsw.FlatVectorsFormat; import org.apache.lucene.codecs.hnsw.FlatVectorsFormat;
@ -67,6 +66,7 @@ public class ES814ScalarQuantizedVectorsFormat extends FlatVectorsFormat {
private final boolean compress; private final boolean compress;
public ES814ScalarQuantizedVectorsFormat(Float confidenceInterval, int bits, boolean compress) { public ES814ScalarQuantizedVectorsFormat(Float confidenceInterval, int bits, boolean compress) {
super(NAME);
if (confidenceInterval != null if (confidenceInterval != null
&& confidenceInterval != DYNAMIC_CONFIDENCE_INTERVAL && confidenceInterval != DYNAMIC_CONFIDENCE_INTERVAL
&& (confidenceInterval < MINIMUM_CONFIDENCE_INTERVAL || confidenceInterval > MAXIMUM_CONFIDENCE_INTERVAL)) { && (confidenceInterval < MINIMUM_CONFIDENCE_INTERVAL || confidenceInterval > MAXIMUM_CONFIDENCE_INTERVAL)) {
@ -137,8 +137,8 @@ public class ES814ScalarQuantizedVectorsFormat extends FlatVectorsFormat {
} }
@Override @Override
public FlatFieldVectorsWriter<?> addField(FieldInfo fieldInfo, KnnFieldVectorsWriter<?> knnFieldVectorsWriter) throws IOException { public FlatFieldVectorsWriter<?> addField(FieldInfo fieldInfo) throws IOException {
return delegate.addField(fieldInfo, knnFieldVectorsWriter); return delegate.addField(fieldInfo);
} }
@Override @Override

View file

@ -29,6 +29,10 @@ class ES815BitFlatVectorsFormat extends FlatVectorsFormat {
private final FlatVectorsFormat delegate = new Lucene99FlatVectorsFormat(FlatBitVectorScorer.INSTANCE); private final FlatVectorsFormat delegate = new Lucene99FlatVectorsFormat(FlatBitVectorScorer.INSTANCE);
protected ES815BitFlatVectorsFormat() {
super("ES815BitFlatVectorsFormat");
}
@Override @Override
public FlatVectorsWriter fieldsWriter(SegmentWriteState segmentWriteState) throws IOException { public FlatVectorsWriter fieldsWriter(SegmentWriteState segmentWriteState) throws IOException {
return delegate.fieldsWriter(segmentWriteState); return delegate.fieldsWriter(segmentWriteState);

View file

@ -345,7 +345,7 @@ public class CompletionFieldMapper extends FieldMapper {
} }
static PostingsFormat postingsFormat() { static PostingsFormat postingsFormat() {
return PostingsFormat.forName("Completion99"); return PostingsFormat.forName("Completion912");
} }
@Override @Override

View file

@ -440,6 +440,30 @@ public abstract class MappedFieldType {
); );
} }
/**
* Create a regexp {@link IntervalsSource} for the given pattern.
*/
public IntervalsSource regexpIntervals(BytesRef pattern, SearchExecutionContext context) {
throw new IllegalArgumentException(
"Can only use interval queries on text fields - not on [" + name + "] which is of type [" + typeName() + "]"
);
}
/**
* Create a range {@link IntervalsSource} for the given ranges
*/
public IntervalsSource rangeIntervals(
BytesRef lowerTerm,
BytesRef upperTerm,
boolean includeLower,
boolean includeUpper,
SearchExecutionContext context
) {
throw new IllegalArgumentException(
"Can only use interval queries on text fields - not on [" + name + "] which is of type [" + typeName() + "]"
);
}
/** /**
* An enum used to describe the relation between the range of terms in a * An enum used to describe the relation between the range of terms in a
* shard when compared with a query range * shard when compared with a query range

View file

@ -248,6 +248,22 @@ public class PlaceHolderFieldMapper extends FieldMapper {
throw new QueryShardException(context, fail("wildcard intervals query")); throw new QueryShardException(context, fail("wildcard intervals query"));
} }
@Override
public IntervalsSource regexpIntervals(BytesRef pattern, SearchExecutionContext context) {
throw new QueryShardException(context, fail("regexp intervals query"));
}
@Override
public IntervalsSource rangeIntervals(
BytesRef lowerTerm,
BytesRef upperTerm,
boolean includeLower,
boolean includeUpper,
SearchExecutionContext context
) {
throw new QueryShardException(context, fail("range intervals query"));
}
@Override @Override
public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) {
throw new IllegalArgumentException(fail("aggregation or sorts")); throw new IllegalArgumentException(fail("aggregation or sorts"));

View file

@ -36,6 +36,7 @@ import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MultiPhraseQuery; import org.apache.lucene.search.MultiPhraseQuery;
import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.PhraseQuery;
@ -620,7 +621,10 @@ public final class TextFieldMapper extends FieldMapper {
return Intervals.fixField(name(), Intervals.term(term)); return Intervals.fixField(name(), Intervals.term(term));
} }
String wildcardTerm = term.utf8ToString() + "?".repeat(Math.max(0, minChars - term.length)); String wildcardTerm = term.utf8ToString() + "?".repeat(Math.max(0, minChars - term.length));
return Intervals.or(Intervals.fixField(name(), Intervals.wildcard(new BytesRef(wildcardTerm))), Intervals.term(term)); return Intervals.or(
Intervals.fixField(name(), Intervals.wildcard(new BytesRef(wildcardTerm), IndexSearcher.getMaxClauseCount())),
Intervals.term(term)
);
} }
@Override @Override
@ -822,7 +826,7 @@ public final class TextFieldMapper extends FieldMapper {
if (prefixFieldType != null) { if (prefixFieldType != null) {
return prefixFieldType.intervals(term); return prefixFieldType.intervals(term);
} }
return Intervals.prefix(term); return Intervals.prefix(term, IndexSearcher.getMaxClauseCount());
} }
@Override @Override
@ -836,8 +840,14 @@ public final class TextFieldMapper extends FieldMapper {
if (getTextSearchInfo().hasPositions() == false) { if (getTextSearchInfo().hasPositions() == false) {
throw new IllegalArgumentException("Cannot create intervals over field [" + name() + "] with no positions indexed"); throw new IllegalArgumentException("Cannot create intervals over field [" + name() + "] with no positions indexed");
} }
FuzzyQuery fq = new FuzzyQuery(new Term(name(), term), maxDistance, prefixLength, 128, transpositions); FuzzyQuery fq = new FuzzyQuery(
return Intervals.multiterm(fq.getAutomata(), term); new Term(name(), term),
maxDistance,
prefixLength,
IndexSearcher.getMaxClauseCount(),
transpositions
);
return Intervals.multiterm(fq.getAutomata(), IndexSearcher.getMaxClauseCount(), term);
} }
@Override @Override
@ -845,7 +855,29 @@ public final class TextFieldMapper extends FieldMapper {
if (getTextSearchInfo().hasPositions() == false) { if (getTextSearchInfo().hasPositions() == false) {
throw new IllegalArgumentException("Cannot create intervals over field [" + name() + "] with no positions indexed"); throw new IllegalArgumentException("Cannot create intervals over field [" + name() + "] with no positions indexed");
} }
return Intervals.wildcard(pattern); return Intervals.wildcard(pattern, IndexSearcher.getMaxClauseCount());
}
@Override
public IntervalsSource regexpIntervals(BytesRef pattern, SearchExecutionContext context) {
if (getTextSearchInfo().hasPositions() == false) {
throw new IllegalArgumentException("Cannot create intervals over field [" + name() + "] with no positions indexed");
}
return Intervals.regexp(pattern, IndexSearcher.getMaxClauseCount());
}
@Override
public IntervalsSource rangeIntervals(
BytesRef lowerTerm,
BytesRef upperTerm,
boolean includeLower,
boolean includeUpper,
SearchExecutionContext context
) {
if (getTextSearchInfo().hasPositions() == false) {
throw new IllegalArgumentException("Cannot create intervals over field [" + name() + "] with no positions indexed");
}
return Intervals.range(lowerTerm, upperTerm, includeLower, includeUpper, IndexSearcher.getMaxClauseCount());
} }
private void checkForPositions() { private void checkForPositions() {

View file

@ -14,11 +14,13 @@ import org.apache.lucene.queries.intervals.IntervalIterator;
import org.apache.lucene.queries.intervals.Intervals; import org.apache.lucene.queries.intervals.Intervals;
import org.apache.lucene.queries.intervals.IntervalsSource; import org.apache.lucene.queries.intervals.IntervalsSource;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.elasticsearch.TransportVersion;
import org.elasticsearch.TransportVersions; import org.elasticsearch.TransportVersions;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.NamedWriteable; import org.elasticsearch.common.io.stream.NamedWriteable;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.VersionedNamedWriteable;
import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.analysis.NamedAnalyzer; import org.elasticsearch.index.analysis.NamedAnalyzer;
@ -77,10 +79,16 @@ public abstract class IntervalsSourceProvider implements NamedWriteable, ToXCont
return Wildcard.fromXContent(parser); return Wildcard.fromXContent(parser);
case "fuzzy": case "fuzzy":
return Fuzzy.fromXContent(parser); return Fuzzy.fromXContent(parser);
case "regexp":
return Regexp.fromXContent(parser);
case "range":
return Range.fromXContent(parser);
} }
throw new ParsingException( throw new ParsingException(
parser.getTokenLocation(), parser.getTokenLocation(),
"Unknown interval type [" + parser.currentName() + "], expecting one of [match, any_of, all_of, prefix, wildcard]" "Unknown interval type ["
+ parser.currentName()
+ "], expecting one of [match, any_of, all_of, prefix, wildcard, regexp, range]"
); );
} }
@ -747,6 +755,129 @@ public abstract class IntervalsSourceProvider implements NamedWriteable, ToXCont
} }
} }
public static class Regexp extends IntervalsSourceProvider implements VersionedNamedWriteable {
public static final String NAME = "regexp";
private final String pattern;
private final String analyzer;
private final String useField;
public Regexp(String pattern, String analyzer, String useField) {
this.pattern = pattern;
this.analyzer = analyzer;
this.useField = useField;
}
public Regexp(StreamInput in) throws IOException {
this.pattern = in.readString();
this.analyzer = in.readOptionalString();
this.useField = in.readOptionalString();
}
@Override
public IntervalsSource getSource(SearchExecutionContext context, MappedFieldType fieldType) {
NamedAnalyzer analyzer = null;
if (this.analyzer != null) {
analyzer = context.getIndexAnalyzers().get(this.analyzer);
}
if (useField != null) {
fieldType = context.getFieldType(useField);
assert fieldType != null;
}
if (analyzer == null) {
analyzer = fieldType.getTextSearchInfo().searchAnalyzer();
}
BytesRef normalizedPattern = analyzer.normalize(fieldType.name(), pattern);
IntervalsSource source = fieldType.regexpIntervals(normalizedPattern, context);
if (useField != null) {
source = Intervals.fixField(useField, source);
}
return source;
}
@Override
public void extractFields(Set<String> fields) {
if (useField != null) {
fields.add(useField);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Regexp regexp = (Regexp) o;
return Objects.equals(pattern, regexp.pattern)
&& Objects.equals(analyzer, regexp.analyzer)
&& Objects.equals(useField, regexp.useField);
}
@Override
public int hashCode() {
return Objects.hash(pattern, analyzer, useField);
}
@Override
public String getWriteableName() {
return NAME;
}
@Override
public TransportVersion getMinimalSupportedVersion() {
return TransportVersions.REGEX_AND_RANGE_INTERVAL_QUERIES;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(pattern);
out.writeOptionalString(analyzer);
out.writeOptionalString(useField);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
builder.field("pattern", pattern);
if (analyzer != null) {
builder.field("analyzer", analyzer);
}
if (useField != null) {
builder.field("use_field", useField);
}
builder.endObject();
return builder;
}
private static final ConstructingObjectParser<Regexp, Void> PARSER = new ConstructingObjectParser<>(NAME, args -> {
String term = (String) args[0];
String analyzer = (String) args[1];
String useField = (String) args[2];
return new Regexp(term, analyzer, useField);
});
static {
PARSER.declareString(constructorArg(), new ParseField("pattern"));
PARSER.declareString(optionalConstructorArg(), new ParseField("analyzer"));
PARSER.declareString(optionalConstructorArg(), new ParseField("use_field"));
}
public static Regexp fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}
String getPattern() {
return pattern;
}
String getAnalyzer() {
return analyzer;
}
String getUseField() {
return useField;
}
}
public static class Fuzzy extends IntervalsSourceProvider { public static class Fuzzy extends IntervalsSourceProvider {
public static final String NAME = "fuzzy"; public static final String NAME = "fuzzy";
@ -908,6 +1039,190 @@ public abstract class IntervalsSourceProvider implements NamedWriteable, ToXCont
} }
} }
public static class Range extends IntervalsSourceProvider implements VersionedNamedWriteable {
public static final String NAME = "range";
private final String lowerTerm;
private final String upperTerm;
private final boolean includeLower;
private final boolean includeUpper;
private final String analyzer;
private final String useField;
public Range(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, String analyzer, String useField) {
this.lowerTerm = lowerTerm;
this.upperTerm = upperTerm;
this.includeLower = includeLower;
this.includeUpper = includeUpper;
this.analyzer = analyzer;
this.useField = useField;
}
public Range(StreamInput in) throws IOException {
this.lowerTerm = in.readString();
this.upperTerm = in.readString();
this.includeLower = in.readBoolean();
this.includeUpper = in.readBoolean();
this.analyzer = in.readOptionalString();
this.useField = in.readOptionalString();
}
@Override
public IntervalsSource getSource(SearchExecutionContext context, MappedFieldType fieldType) {
NamedAnalyzer analyzer = null;
if (this.analyzer != null) {
analyzer = context.getIndexAnalyzers().get(this.analyzer);
}
if (useField != null) {
fieldType = context.getFieldType(useField);
assert fieldType != null;
}
if (analyzer == null) {
analyzer = fieldType.getTextSearchInfo().searchAnalyzer();
}
BytesRef normalizedLowerTerm = analyzer.normalize(fieldType.name(), lowerTerm);
BytesRef normalizedUpperTerm = analyzer.normalize(fieldType.name(), upperTerm);
IntervalsSource source = fieldType.rangeIntervals(
normalizedLowerTerm,
normalizedUpperTerm,
includeLower,
includeUpper,
context
);
if (useField != null) {
source = Intervals.fixField(useField, source);
}
return source;
}
@Override
public void extractFields(Set<String> fields) {
if (useField != null) {
fields.add(useField);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Range range = (Range) o;
return includeLower == range.includeLower
&& includeUpper == range.includeUpper
&& Objects.equals(lowerTerm, range.lowerTerm)
&& Objects.equals(upperTerm, range.upperTerm)
&& Objects.equals(analyzer, range.analyzer)
&& Objects.equals(useField, range.useField);
}
@Override
public int hashCode() {
return Objects.hash(lowerTerm, upperTerm, includeLower, includeUpper, analyzer, useField);
}
@Override
public String getWriteableName() {
return NAME;
}
@Override
public TransportVersion getMinimalSupportedVersion() {
return TransportVersions.REGEX_AND_RANGE_INTERVAL_QUERIES;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(lowerTerm);
out.writeString(upperTerm);
out.writeBoolean(includeLower);
out.writeBoolean(includeUpper);
out.writeOptionalString(analyzer);
out.writeOptionalString(useField);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
if (includeLower) {
builder.field("gte", lowerTerm);
} else {
builder.field("gt", lowerTerm);
}
if (includeUpper) {
builder.field("lte", upperTerm);
} else {
builder.field("lt", upperTerm);
}
if (analyzer != null) {
builder.field("analyzer", analyzer);
}
if (useField != null) {
builder.field("use_field", useField);
}
builder.endObject();
return builder;
}
private static final ConstructingObjectParser<Range, Void> PARSER = new ConstructingObjectParser<>(NAME, args -> {
String gte = (String) args[0];
String gt = (String) args[1];
String lte = (String) args[2];
String lt = (String) args[3];
if ((gte == null && gt == null) || (gte != null && gt != null)) {
throw new IllegalArgumentException("Either [gte] or [gt], one of them must be provided");
}
if ((lte == null && lt == null) || (lte != null && lt != null)) {
throw new IllegalArgumentException("Either [lte] or [lt], one of them must be provided");
}
boolean includeLower = gte != null ? true : false;
String lowerTerm = gte != null ? gte : gt;
boolean includeUpper = lte != null ? true : false;
String upperTerm = lte != null ? lte : lt;
String analyzer = (String) args[4];
String useField = (String) args[5];
return new Range(lowerTerm, upperTerm, includeLower, includeUpper, analyzer, useField);
});
static {
PARSER.declareString(optionalConstructorArg(), new ParseField("gte"));
PARSER.declareString(optionalConstructorArg(), new ParseField("gt"));
PARSER.declareString(optionalConstructorArg(), new ParseField("lte"));
PARSER.declareString(optionalConstructorArg(), new ParseField("lt"));
PARSER.declareString(optionalConstructorArg(), new ParseField("analyzer"));
PARSER.declareString(optionalConstructorArg(), new ParseField("use_field"));
}
public static Range fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}
String getLowerTerm() {
return lowerTerm;
}
String getUpperTerm() {
return upperTerm;
}
boolean getIncludeLower() {
return includeLower;
}
boolean getIncludeUpper() {
return includeUpper;
}
String getAnalyzer() {
return analyzer;
}
String getUseField() {
return useField;
}
}
static class ScriptFilterSource extends FilteredIntervalsSource { static class ScriptFilterSource extends FilteredIntervalsSource {
final IntervalFilterScript script; final IntervalFilterScript script;

View file

@ -57,6 +57,7 @@ public enum LuceneFilesExtensions {
NVM("nvm", "Norms Metadata", true, false), NVM("nvm", "Norms Metadata", true, false),
PAY("pay", "Payloads", false, false), PAY("pay", "Payloads", false, false),
POS("pos", "Positions", false, false), POS("pos", "Positions", false, false),
PSM("psm", "Postings Metadata", true, false),
SI("si", "Segment Info", true, false), SI("si", "Segment Info", true, false),
// Term dictionaries are typically performance-sensitive and hot in the page // Term dictionaries are typically performance-sensitive and hot in the page
// cache, so we use mmap, which provides better performance. // cache, so we use mmap, which provides better performance.

View file

@ -1376,7 +1376,7 @@ public class RecoverySourceHandler {
// we already have the file contents on heap no need to open the file again // we already have the file contents on heap no need to open the file again
currentInput = null; currentInput = null;
} else { } else {
currentInput = store.directory().openInput(md.name(), IOContext.READONCE); currentInput = store.directory().openInput(md.name(), IOContext.READ);
} }
} }

View file

@ -43,7 +43,7 @@ public final class LegacyBM25Similarity extends Similarity {
* </ul> * </ul>
*/ */
public LegacyBM25Similarity() { public LegacyBM25Similarity() {
this.bm25Similarity = new BM25Similarity(); this(new BM25Similarity());
} }
/** /**
@ -54,7 +54,12 @@ public final class LegacyBM25Similarity extends Similarity {
* not within the range {@code [0..1]} * not within the range {@code [0..1]}
*/ */
public LegacyBM25Similarity(float k1, float b, boolean discountOverlaps) { public LegacyBM25Similarity(float k1, float b, boolean discountOverlaps) {
this.bm25Similarity = new BM25Similarity(k1, b, discountOverlaps); this(new BM25Similarity(k1, b, discountOverlaps));
}
private LegacyBM25Similarity(BM25Similarity bm25Similarity) {
super(bm25Similarity.getDiscountOverlaps());
this.bm25Similarity = bm25Similarity;
} }
@Override @Override
@ -81,13 +86,6 @@ public final class LegacyBM25Similarity extends Similarity {
return bm25Similarity.getB(); return bm25Similarity.getB();
} }
/**
* Returns true if overlap tokens are discounted from the document's length.
*/
public boolean getDiscountOverlaps() {
return bm25Similarity.getDiscountOverlaps();
}
@Override @Override
public String toString() { public String toString() {
return bm25Similarity.toString(); return bm25Similarity.toString();

View file

@ -77,6 +77,19 @@ public final class CombinedBitSet extends BitSet implements Bits {
return next; return next;
} }
@Override
public int nextSetBit(int index, int upperBound) {
assert index >= 0 && index < length : "index=" + index + " numBits=" + length();
int next = first.nextSetBit(index, upperBound);
while (next != DocIdSetIterator.NO_MORE_DOCS && second.get(next) == false) {
if (next == length() - 1) {
return DocIdSetIterator.NO_MORE_DOCS;
}
next = first.nextSetBit(next + 1, upperBound);
}
return next;
}
@Override @Override
public long ramBytesUsed() { public long ramBytesUsed() {
return first.ramBytesUsed(); return first.ramBytesUsed();

View file

@ -69,6 +69,12 @@ public final class MatchAllBitSet extends BitSet {
return index; return index;
} }
@Override
public int nextSetBit(int index, int upperBound) {
assert index < upperBound;
return index;
}
@Override @Override
public long ramBytesUsed() { public long ramBytesUsed() {
return RAM_BYTES_USED; return RAM_BYTES_USED;

View file

@ -85,6 +85,7 @@ import org.elasticsearch.core.IOUtils;
import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.TimeValue;
import org.elasticsearch.core.Tuple; import org.elasticsearch.core.Tuple;
import org.elasticsearch.core.UpdateForV9;
import org.elasticsearch.discovery.DiscoveryModule; import org.elasticsearch.discovery.DiscoveryModule;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.env.NodeEnvironment;
@ -509,6 +510,7 @@ class NodeConstruction {
for (final ExecutorBuilder<?> builder : threadPool.builders()) { for (final ExecutorBuilder<?> builder : threadPool.builders()) {
additionalSettings.addAll(builder.getRegisteredSettings()); additionalSettings.addAll(builder.getRegisteredSettings());
} }
addBwcSearchWorkerSettings(additionalSettings);
SettingsExtension.load().forEach(e -> additionalSettings.addAll(e.getSettings())); SettingsExtension.load().forEach(e -> additionalSettings.addAll(e.getSettings()));
// this is as early as we can validate settings at this point. we already pass them to ThreadPool // this is as early as we can validate settings at this point. we already pass them to ThreadPool
@ -539,6 +541,17 @@ class NodeConstruction {
return settingsModule; return settingsModule;
} }
@UpdateForV9
private static void addBwcSearchWorkerSettings(List<Setting<?>> additionalSettings) {
// TODO remove the below settings, they are unused and only here to enable BwC for deployments that still use them
additionalSettings.add(
Setting.intSetting("thread_pool.search_worker.queue_size", 0, Setting.Property.NodeScope, Setting.Property.DeprecatedWarning)
);
additionalSettings.add(
Setting.intSetting("thread_pool.search_worker.size", 0, Setting.Property.NodeScope, Setting.Property.DeprecatedWarning)
);
}
private SearchModule createSearchModule(Settings settings, ThreadPool threadPool, TelemetryProvider telemetryProvider) { private SearchModule createSearchModule(Settings settings, ThreadPool threadPool, TelemetryProvider telemetryProvider) {
IndexSearcher.setMaxClauseCount(SearchUtils.calculateMaxClauseValue(threadPool)); IndexSearcher.setMaxClauseCount(SearchUtils.calculateMaxClauseValue(threadPool));
return new SearchModule(settings, pluginsService.filterPlugins(SearchPlugin.class).toList(), telemetryProvider); return new SearchModule(settings, pluginsService.filterPlugins(SearchPlugin.class).toList(), telemetryProvider);

View file

@ -4030,7 +4030,7 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp
final String file = fileInfo.physicalName(); final String file = fileInfo.physicalName();
try ( try (
Releasable ignored = context.withCommitRef(); Releasable ignored = context.withCommitRef();
IndexInput indexInput = store.openVerifyingInput(file, IOContext.READONCE, fileInfo.metadata()) IndexInput indexInput = store.openVerifyingInput(file, IOContext.READ, fileInfo.metadata())
) { ) {
for (int i = 0; i < fileInfo.numberOfParts(); i++) { for (int i = 0; i < fileInfo.numberOfParts(); i++) {
final long partBytes = fileInfo.partBytes(i); final long partBytes = fileInfo.partBytes(i);

View file

@ -95,6 +95,11 @@ public class RestSearchAction extends BaseRestHandler {
); );
} }
@Override
public Set<String> supportedCapabilities() {
return SearchCapabilities.CAPABILITIES;
}
@Override @Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {

View file

@ -0,0 +1,25 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
package org.elasticsearch.rest.action.search;
import java.util.Set;
/**
* A {@link Set} of "capabilities" supported by the {@link RestSearchAction}.
*/
public final class SearchCapabilities {
private SearchCapabilities() {}
/** Support regex and range match rules in interval queries. */
private static final String RANGE_REGEX_INTERVAL_QUERY_CAPABILITY = "range_regexp_interval_queries";
public static final Set<String> CAPABILITIES = Set.of(RANGE_REGEX_INTERVAL_QUERY_CAPABILITY);
}

View file

@ -180,7 +180,14 @@ final class DefaultSearchContext extends SearchContext {
this.indexShard = readerContext.indexShard(); this.indexShard = readerContext.indexShard();
Engine.Searcher engineSearcher = readerContext.acquireSearcher("search"); Engine.Searcher engineSearcher = readerContext.acquireSearcher("search");
if (executor == null) { int maximumNumberOfSlices = determineMaximumNumberOfSlices(
executor,
request,
resultsType,
enableQueryPhaseParallelCollection,
field -> getFieldCardinality(field, readerContext.indexService(), engineSearcher.getDirectoryReader())
);
if (executor == null || maximumNumberOfSlices <= 1) {
this.searcher = new ContextIndexSearcher( this.searcher = new ContextIndexSearcher(
engineSearcher.getIndexReader(), engineSearcher.getIndexReader(),
engineSearcher.getSimilarity(), engineSearcher.getSimilarity(),
@ -196,13 +203,7 @@ final class DefaultSearchContext extends SearchContext {
engineSearcher.getQueryCachingPolicy(), engineSearcher.getQueryCachingPolicy(),
lowLevelCancellation, lowLevelCancellation,
executor, executor,
determineMaximumNumberOfSlices( maximumNumberOfSlices,
executor,
request,
resultsType,
enableQueryPhaseParallelCollection,
field -> getFieldCardinality(field, readerContext.indexService(), engineSearcher.getDirectoryReader())
),
minimumDocsPerSlice minimumDocsPerSlice
); );
} }
@ -290,6 +291,7 @@ final class DefaultSearchContext extends SearchContext {
ToLongFunction<String> fieldCardinality ToLongFunction<String> fieldCardinality
) { ) {
return executor instanceof ThreadPoolExecutor tpe return executor instanceof ThreadPoolExecutor tpe
&& tpe.getQueue().isEmpty()
&& isParallelCollectionSupportedForResults(resultsType, request.source(), fieldCardinality, enableQueryPhaseParallelCollection) && isParallelCollectionSupportedForResults(resultsType, request.source(), fieldCardinality, enableQueryPhaseParallelCollection)
? tpe.getMaximumPoolSize() ? tpe.getMaximumPoolSize()
: 1; : 1;

View file

@ -1263,6 +1263,16 @@ public class SearchModule {
IntervalsSourceProvider.class, IntervalsSourceProvider.class,
IntervalsSourceProvider.Fuzzy.NAME, IntervalsSourceProvider.Fuzzy.NAME,
IntervalsSourceProvider.Fuzzy::new IntervalsSourceProvider.Fuzzy::new
),
new NamedWriteableRegistry.Entry(
IntervalsSourceProvider.class,
IntervalsSourceProvider.Regexp.NAME,
IntervalsSourceProvider.Regexp::new
),
new NamedWriteableRegistry.Entry(
IntervalsSourceProvider.class,
IntervalsSourceProvider.Range.NAME,
IntervalsSourceProvider.Range::new
) )
); );
} }

View file

@ -142,7 +142,6 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -228,7 +227,8 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
"search.worker_threads_enabled", "search.worker_threads_enabled",
true, true,
Property.NodeScope, Property.NodeScope,
Property.Dynamic Property.Dynamic,
Property.DeprecatedWarning
); );
public static final Setting<Boolean> QUERY_PHASE_PARALLEL_COLLECTION_ENABLED = Setting.boolSetting( public static final Setting<Boolean> QUERY_PHASE_PARALLEL_COLLECTION_ENABLED = Setting.boolSetting(
@ -279,7 +279,7 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
private final FetchPhase fetchPhase; private final FetchPhase fetchPhase;
private final RankFeatureShardPhase rankFeatureShardPhase; private final RankFeatureShardPhase rankFeatureShardPhase;
private volatile boolean enableSearchWorkerThreads; private volatile Executor searchExecutor;
private volatile boolean enableQueryPhaseParallelCollection; private volatile boolean enableQueryPhaseParallelCollection;
private volatile long defaultKeepAlive; private volatile long defaultKeepAlive;
@ -373,7 +373,10 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
clusterService.getClusterSettings() clusterService.getClusterSettings()
.addSettingsUpdateConsumer(ENABLE_REWRITE_AGGS_TO_FILTER_BY_FILTER, this::setEnableRewriteAggsToFilterByFilter); .addSettingsUpdateConsumer(ENABLE_REWRITE_AGGS_TO_FILTER_BY_FILTER, this::setEnableRewriteAggsToFilterByFilter);
enableSearchWorkerThreads = SEARCH_WORKER_THREADS_ENABLED.get(settings); if (SEARCH_WORKER_THREADS_ENABLED.get(settings)) {
searchExecutor = threadPool.executor(Names.SEARCH);
}
clusterService.getClusterSettings().addSettingsUpdateConsumer(SEARCH_WORKER_THREADS_ENABLED, this::setEnableSearchWorkerThreads); clusterService.getClusterSettings().addSettingsUpdateConsumer(SEARCH_WORKER_THREADS_ENABLED, this::setEnableSearchWorkerThreads);
enableQueryPhaseParallelCollection = QUERY_PHASE_PARALLEL_COLLECTION_ENABLED.get(settings); enableQueryPhaseParallelCollection = QUERY_PHASE_PARALLEL_COLLECTION_ENABLED.get(settings);
@ -382,7 +385,11 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
} }
private void setEnableSearchWorkerThreads(boolean enableSearchWorkerThreads) { private void setEnableSearchWorkerThreads(boolean enableSearchWorkerThreads) {
this.enableSearchWorkerThreads = enableSearchWorkerThreads; if (enableSearchWorkerThreads) {
searchExecutor = threadPool.executor(Names.SEARCH);
} else {
searchExecutor = null;
}
} }
private void setEnableQueryPhaseParallelCollection(boolean enableQueryPhaseParallelCollection) { private void setEnableQueryPhaseParallelCollection(boolean enableQueryPhaseParallelCollection) {
@ -1111,7 +1118,6 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
reader.indexShard().shardId(), reader.indexShard().shardId(),
request.getClusterAlias() request.getClusterAlias()
); );
ExecutorService executor = this.enableSearchWorkerThreads ? threadPool.executor(Names.SEARCH_WORKER) : null;
searchContext = new DefaultSearchContext( searchContext = new DefaultSearchContext(
reader, reader,
request, request,
@ -1120,7 +1126,7 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
timeout, timeout,
fetchPhase, fetchPhase,
lowLevelCancellation, lowLevelCancellation,
executor, searchExecutor,
resultsType, resultsType,
enableQueryPhaseParallelCollection, enableQueryPhaseParallelCollection,
minimumDocsPerSlice minimumDocsPerSlice

View file

@ -22,7 +22,6 @@ import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder; import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.PriorityQueue; import org.apache.lucene.util.PriorityQueue;
import org.apache.lucene.util.ThreadInterruptedException;
import org.elasticsearch.cluster.metadata.DataStream; import org.elasticsearch.cluster.metadata.DataStream;
import org.elasticsearch.common.lucene.search.function.MinScoreScorer; import org.elasticsearch.common.lucene.search.function.MinScoreScorer;
import org.elasticsearch.index.mapper.DataStreamTimestampFieldMapper; import org.elasticsearch.index.mapper.DataStreamTimestampFieldMapper;
@ -38,9 +37,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.function.IntSupplier; import java.util.function.IntSupplier;
import static org.elasticsearch.index.IndexSortConfig.TIME_SERIES_SORT; import static org.elasticsearch.index.IndexSortConfig.TIME_SERIES_SORT;
@ -68,10 +64,7 @@ public class TimeSeriesIndexSearcher {
searcher.getSimilarity(), searcher.getSimilarity(),
searcher.getQueryCache(), searcher.getQueryCache(),
searcher.getQueryCachingPolicy(), searcher.getQueryCachingPolicy(),
false, false
searcher.getExecutor(),
1,
-1
); );
} catch (IOException e) { } catch (IOException e) {
// IOException from wrapping the index searcher which should never happen. // IOException from wrapping the index searcher which should never happen.
@ -94,28 +87,8 @@ public class TimeSeriesIndexSearcher {
public void search(Query query, BucketCollector bucketCollector) throws IOException { public void search(Query query, BucketCollector bucketCollector) throws IOException {
query = searcher.rewrite(query); query = searcher.rewrite(query);
Weight weight = searcher.createWeight(query, bucketCollector.scoreMode(), 1); Weight weight = searcher.createWeight(query, bucketCollector.scoreMode(), 1);
if (searcher.getExecutor() == null) {
search(bucketCollector, weight); search(bucketCollector, weight);
bucketCollector.postCollection(); bucketCollector.postCollection();
return;
}
// offload to the search worker thread pool whenever possible. It will be null only when search.worker_threads_enabled is false
RunnableFuture<Void> task = new FutureTask<>(() -> {
search(bucketCollector, weight);
bucketCollector.postCollection();
return null;
});
searcher.getExecutor().execute(task);
try {
task.get();
} catch (InterruptedException e) {
throw new ThreadInterruptedException(e);
} catch (ExecutionException e) {
if (e.getCause() instanceof RuntimeException runtimeException) {
throw runtimeException;
}
throw new RuntimeException(e.getCause());
}
} }
private void search(BucketCollector bucketCollector, Weight weight) throws IOException { private void search(BucketCollector bucketCollector, Weight weight) throws IOException {

View file

@ -322,12 +322,9 @@ public class ContextIndexSearcher extends IndexSearcher implements Releasable {
} }
/** /**
* Similar to the lucene implementation, with the following changes made: * Same implementation as the default one in Lucene, with an additional call to postCollection in cased there are no segments.
* 1) postCollection is performed after each segment is collected. This is needed for aggregations, performed by search worker threads * The rest is a plain copy from Lucene.
* so it can be parallelized. Also, it needs to happen in the same thread where doc_values are read, as it consumes them and Lucene */
* does not allow consuming them from a different thread.
* 2) handles the ES TimeExceededException
* */
private <C extends Collector, T> T search(Weight weight, CollectorManager<C, T> collectorManager, C firstCollector) throws IOException { private <C extends Collector, T> T search(Weight weight, CollectorManager<C, T> collectorManager, C firstCollector) throws IOException {
LeafSlice[] leafSlices = getSlices(); LeafSlice[] leafSlices = getSlices();
if (leafSlices.length == 0) { if (leafSlices.length == 0) {
@ -359,14 +356,18 @@ public class ContextIndexSearcher extends IndexSearcher implements Releasable {
} }
} }
/**
* Similar to the lucene implementation, with the following changes made:
* 1) postCollection is performed after each segment is collected. This is needed for aggregations, performed by search threads
* so it can be parallelized. Also, it needs to happen in the same thread where doc_values are read, as it consumes them and Lucene
* does not allow consuming them from a different thread.
* 2) handles the ES TimeExceededException
*/
@Override @Override
public void search(List<LeafReaderContext> leaves, Weight weight, Collector collector) throws IOException { public void search(List<LeafReaderContext> leaves, Weight weight, Collector collector) throws IOException {
collector.setWeight(weight);
boolean success = false; boolean success = false;
try { try {
for (LeafReaderContext ctx : leaves) { // search each subreader super.search(leaves, weight, collector);
searchLeaf(ctx, weight, collector);
}
success = true; success = true;
} catch (@SuppressWarnings("unused") TimeExceededException e) { } catch (@SuppressWarnings("unused") TimeExceededException e) {
timeExceeded = true; timeExceeded = true;
@ -410,13 +411,8 @@ public class ContextIndexSearcher extends IndexSearcher implements Releasable {
// This exception should never be re-thrown, but we fill in the stacktrace to be able to trace where it does not get properly caught // This exception should never be re-thrown, but we fill in the stacktrace to be able to trace where it does not get properly caught
} }
/** @Override
* Lower-level search API. protected void searchLeaf(LeafReaderContext ctx, Weight weight, Collector collector) throws IOException {
*
* {@link LeafCollector#collect(int)} is called for every matching document in
* the provided <code>ctx</code>.
*/
private void searchLeaf(LeafReaderContext ctx, Weight weight, Collector collector) throws IOException {
cancellable.checkCancelled(); cancellable.checkCancelled();
final LeafCollector leafCollector; final LeafCollector leafCollector;
try { try {

View file

@ -72,16 +72,6 @@ public class DefaultBuiltInExecutorBuilders implements BuiltInExecutorBuilders {
new EsExecutors.TaskTrackingConfig(true, searchAutoscalingEWMA) new EsExecutors.TaskTrackingConfig(true, searchAutoscalingEWMA)
) )
); );
result.put(
ThreadPool.Names.SEARCH_WORKER,
new FixedExecutorBuilder(
settings,
ThreadPool.Names.SEARCH_WORKER,
searchOrGetThreadPoolSize,
-1,
EsExecutors.TaskTrackingConfig.DEFAULT
)
);
result.put( result.put(
ThreadPool.Names.SEARCH_COORDINATION, ThreadPool.Names.SEARCH_COORDINATION,
new FixedExecutorBuilder( new FixedExecutorBuilder(

View file

@ -88,7 +88,6 @@ public class ThreadPool implements ReportingService<ThreadPoolInfo>, Scheduler {
public static final String ANALYZE = "analyze"; public static final String ANALYZE = "analyze";
public static final String WRITE = "write"; public static final String WRITE = "write";
public static final String SEARCH = "search"; public static final String SEARCH = "search";
public static final String SEARCH_WORKER = "search_worker";
public static final String SEARCH_COORDINATION = "search_coordination"; public static final String SEARCH_COORDINATION = "search_coordination";
public static final String AUTO_COMPLETE = "auto_complete"; public static final String AUTO_COMPLETE = "auto_complete";
public static final String SEARCH_THROTTLED = "search_throttled"; public static final String SEARCH_THROTTLED = "search_throttled";
@ -158,7 +157,6 @@ public class ThreadPool implements ReportingService<ThreadPoolInfo>, Scheduler {
entry(Names.ANALYZE, ThreadPoolType.FIXED), entry(Names.ANALYZE, ThreadPoolType.FIXED),
entry(Names.WRITE, ThreadPoolType.FIXED), entry(Names.WRITE, ThreadPoolType.FIXED),
entry(Names.SEARCH, ThreadPoolType.FIXED), entry(Names.SEARCH, ThreadPoolType.FIXED),
entry(Names.SEARCH_WORKER, ThreadPoolType.FIXED),
entry(Names.SEARCH_COORDINATION, ThreadPoolType.FIXED), entry(Names.SEARCH_COORDINATION, ThreadPoolType.FIXED),
entry(Names.AUTO_COMPLETE, ThreadPoolType.FIXED), entry(Names.AUTO_COMPLETE, ThreadPoolType.FIXED),
entry(Names.MANAGEMENT, ThreadPoolType.SCALING), entry(Names.MANAGEMENT, ThreadPoolType.SCALING),

View file

@ -1 +1,2 @@
org.elasticsearch.index.codec.Elasticsearch814Codec org.elasticsearch.index.codec.Elasticsearch814Codec
org.elasticsearch.index.codec.Elasticsearch816Codec

View file

@ -13,7 +13,7 @@ import org.apache.lucene.codecs.DocValuesFormat;
import org.apache.lucene.codecs.KnnVectorsFormat; import org.apache.lucene.codecs.KnnVectorsFormat;
import org.apache.lucene.codecs.PostingsFormat; import org.apache.lucene.codecs.PostingsFormat;
import org.apache.lucene.codecs.lucene90.Lucene90DocValuesFormat; import org.apache.lucene.codecs.lucene90.Lucene90DocValuesFormat;
import org.apache.lucene.codecs.lucene99.Lucene99Codec; import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat; import org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat;
import org.apache.lucene.codecs.perfield.PerFieldDocValuesFormat; import org.apache.lucene.codecs.perfield.PerFieldDocValuesFormat;
import org.apache.lucene.codecs.perfield.PerFieldKnnVectorsFormat; import org.apache.lucene.codecs.perfield.PerFieldKnnVectorsFormat;
@ -54,7 +54,7 @@ import org.apache.lucene.search.QueryVisitor;
import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.ScoreMode;
import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Weight; import org.apache.lucene.search.Weight;
import org.apache.lucene.search.suggest.document.Completion99PostingsFormat; import org.apache.lucene.search.suggest.document.Completion912PostingsFormat;
import org.apache.lucene.search.suggest.document.CompletionPostingsFormat; import org.apache.lucene.search.suggest.document.CompletionPostingsFormat;
import org.apache.lucene.search.suggest.document.SuggestField; import org.apache.lucene.search.suggest.document.SuggestField;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
@ -327,11 +327,11 @@ public class IndexDiskUsageAnalyzerTests extends ESTestCase {
public void testCompletionField() throws Exception { public void testCompletionField() throws Exception {
IndexWriterConfig config = new IndexWriterConfig().setCommitOnClose(true) IndexWriterConfig config = new IndexWriterConfig().setCommitOnClose(true)
.setUseCompoundFile(false) .setUseCompoundFile(false)
.setCodec(new Lucene99Codec(Lucene99Codec.Mode.BEST_SPEED) { .setCodec(new Lucene912Codec(Lucene912Codec.Mode.BEST_SPEED) {
@Override @Override
public PostingsFormat getPostingsFormatForField(String field) { public PostingsFormat getPostingsFormatForField(String field) {
if (field.startsWith("suggest_")) { if (field.startsWith("suggest_")) {
return new Completion99PostingsFormat(randomFrom(CompletionPostingsFormat.FSTLoadMode.values())); return new Completion912PostingsFormat(randomFrom(CompletionPostingsFormat.FSTLoadMode.values()));
} else { } else {
return super.postingsFormat(); return super.postingsFormat();
} }
@ -414,25 +414,25 @@ public class IndexDiskUsageAnalyzerTests extends ESTestCase {
enum CodecMode { enum CodecMode {
BEST_SPEED { BEST_SPEED {
@Override @Override
Lucene99Codec.Mode mode() { Lucene912Codec.Mode mode() {
return Lucene99Codec.Mode.BEST_SPEED; return Lucene912Codec.Mode.BEST_SPEED;
} }
}, },
BEST_COMPRESSION { BEST_COMPRESSION {
@Override @Override
Lucene99Codec.Mode mode() { Lucene912Codec.Mode mode() {
return Lucene99Codec.Mode.BEST_COMPRESSION; return Lucene912Codec.Mode.BEST_COMPRESSION;
} }
}; };
abstract Lucene99Codec.Mode mode(); abstract Lucene912Codec.Mode mode();
} }
static void indexRandomly(Directory directory, CodecMode codecMode, int numDocs, Consumer<Document> addFields) throws IOException { static void indexRandomly(Directory directory, CodecMode codecMode, int numDocs, Consumer<Document> addFields) throws IOException {
IndexWriterConfig config = new IndexWriterConfig().setCommitOnClose(true) IndexWriterConfig config = new IndexWriterConfig().setCommitOnClose(true)
.setUseCompoundFile(randomBoolean()) .setUseCompoundFile(randomBoolean())
.setCodec(new Lucene99Codec(codecMode.mode())); .setCodec(new Lucene912Codec(codecMode.mode()));
try (IndexWriter writer = new IndexWriter(directory, config)) { try (IndexWriter writer = new IndexWriter(directory, config)) {
for (int i = 0; i < numDocs; i++) { for (int i = 0; i < numDocs; i++) {
final Document doc = new Document(); final Document doc = new Document();
@ -640,7 +640,7 @@ public class IndexDiskUsageAnalyzerTests extends ESTestCase {
try (DirectoryReader reader = DirectoryReader.open(source)) { try (DirectoryReader reader = DirectoryReader.open(source)) {
IndexWriterConfig config = new IndexWriterConfig().setSoftDeletesField(Lucene.SOFT_DELETES_FIELD) IndexWriterConfig config = new IndexWriterConfig().setSoftDeletesField(Lucene.SOFT_DELETES_FIELD)
.setUseCompoundFile(randomBoolean()) .setUseCompoundFile(randomBoolean())
.setCodec(new Lucene99Codec(mode.mode()) { .setCodec(new Lucene912Codec(mode.mode()) {
@Override @Override
public PostingsFormat getPostingsFormatForField(String field) { public PostingsFormat getPostingsFormatForField(String field) {
return new ES812PostingsFormat(); return new ES812PostingsFormat();

View file

@ -52,7 +52,7 @@ public class CodecTests extends ESTestCase {
assumeTrue("Only when zstd_stored_fields feature flag is enabled", CodecService.ZSTD_STORED_FIELDS_FEATURE_FLAG.isEnabled()); assumeTrue("Only when zstd_stored_fields feature flag is enabled", CodecService.ZSTD_STORED_FIELDS_FEATURE_FLAG.isEnabled());
CodecService codecService = createCodecService(); CodecService codecService = createCodecService();
assertThat(codecService.codec("default"), instanceOf(PerFieldMapperCodec.class)); assertThat(codecService.codec("default"), instanceOf(PerFieldMapperCodec.class));
assertThat(codecService.codec("default"), instanceOf(Elasticsearch814Codec.class)); assertThat(codecService.codec("default"), instanceOf(Elasticsearch816Codec.class));
} }
public void testDefault() throws Exception { public void testDefault() throws Exception {

View file

@ -11,7 +11,7 @@ package org.elasticsearch.index.codec.vectors;
import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.Codec;
import org.apache.lucene.codecs.KnnVectorsFormat; import org.apache.lucene.codecs.KnnVectorsFormat;
import org.apache.lucene.codecs.lucene99.Lucene99Codec; import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.apache.lucene.tests.index.BaseKnnVectorsFormatTestCase; import org.apache.lucene.tests.index.BaseKnnVectorsFormatTestCase;
import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.logging.LogConfigurator;
@ -24,7 +24,7 @@ public class ES813FlatVectorFormatTests extends BaseKnnVectorsFormatTestCase {
@Override @Override
protected Codec getCodec() { protected Codec getCodec() {
return new Lucene99Codec() { return new Lucene912Codec() {
@Override @Override
public KnnVectorsFormat getKnnVectorsFormatForField(String field) { public KnnVectorsFormat getKnnVectorsFormatForField(String field) {
return new ES813FlatVectorFormat(); return new ES813FlatVectorFormat();

View file

@ -11,7 +11,7 @@ package org.elasticsearch.index.codec.vectors;
import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.Codec;
import org.apache.lucene.codecs.KnnVectorsFormat; import org.apache.lucene.codecs.KnnVectorsFormat;
import org.apache.lucene.codecs.lucene99.Lucene99Codec; import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.apache.lucene.tests.index.BaseKnnVectorsFormatTestCase; import org.apache.lucene.tests.index.BaseKnnVectorsFormatTestCase;
import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.logging.LogConfigurator;
@ -24,7 +24,7 @@ public class ES813Int8FlatVectorFormatTests extends BaseKnnVectorsFormatTestCase
@Override @Override
protected Codec getCodec() { protected Codec getCodec() {
return new Lucene99Codec() { return new Lucene912Codec() {
@Override @Override
public KnnVectorsFormat getKnnVectorsFormatForField(String field) { public KnnVectorsFormat getKnnVectorsFormatForField(String field) {
return new ES813Int8FlatVectorFormat(); return new ES813Int8FlatVectorFormat();

View file

@ -11,7 +11,7 @@ package org.elasticsearch.index.codec.vectors;
import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.Codec;
import org.apache.lucene.codecs.KnnVectorsFormat; import org.apache.lucene.codecs.KnnVectorsFormat;
import org.apache.lucene.codecs.lucene99.Lucene99Codec; import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field; import org.apache.lucene.document.Field;
import org.apache.lucene.document.KnnFloatVectorField; import org.apache.lucene.document.KnnFloatVectorField;
@ -41,7 +41,7 @@ public class ES814HnswScalarQuantizedVectorsFormatTests extends BaseKnnVectorsFo
@Override @Override
protected Codec getCodec() { protected Codec getCodec() {
return new Lucene99Codec() { return new Lucene912Codec() {
@Override @Override
public KnnVectorsFormat getKnnVectorsFormatForField(String field) { public KnnVectorsFormat getKnnVectorsFormatForField(String field) {
return new ES814HnswScalarQuantizedVectorsFormat(); return new ES814HnswScalarQuantizedVectorsFormat();

View file

@ -11,7 +11,7 @@ package org.elasticsearch.index.codec.vectors;
import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.Codec;
import org.apache.lucene.codecs.KnnVectorsFormat; import org.apache.lucene.codecs.KnnVectorsFormat;
import org.apache.lucene.codecs.lucene99.Lucene99Codec; import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.apache.lucene.index.VectorSimilarityFunction; import org.apache.lucene.index.VectorSimilarityFunction;
import org.junit.Before; import org.junit.Before;
@ -19,7 +19,7 @@ public class ES815BitFlatVectorFormatTests extends BaseKnnBitVectorsFormatTestCa
@Override @Override
protected Codec getCodec() { protected Codec getCodec() {
return new Lucene99Codec() { return new Lucene912Codec() {
@Override @Override
public KnnVectorsFormat getKnnVectorsFormatForField(String field) { public KnnVectorsFormat getKnnVectorsFormatForField(String field) {
return new ES815BitFlatVectorFormat(); return new ES815BitFlatVectorFormat();

View file

@ -11,7 +11,7 @@ package org.elasticsearch.index.codec.vectors;
import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.Codec;
import org.apache.lucene.codecs.KnnVectorsFormat; import org.apache.lucene.codecs.KnnVectorsFormat;
import org.apache.lucene.codecs.lucene99.Lucene99Codec; import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.apache.lucene.index.VectorSimilarityFunction; import org.apache.lucene.index.VectorSimilarityFunction;
import org.junit.Before; import org.junit.Before;
@ -19,7 +19,7 @@ public class ES815HnswBitVectorsFormatTests extends BaseKnnBitVectorsFormatTestC
@Override @Override
protected Codec getCodec() { protected Codec getCodec() {
return new Lucene99Codec() { return new Lucene912Codec() {
@Override @Override
public KnnVectorsFormat getKnnVectorsFormatForField(String field) { public KnnVectorsFormat getKnnVectorsFormatForField(String field) {
return new ES815HnswBitVectorsFormat(); return new ES815HnswBitVectorsFormat();

View file

@ -10,7 +10,7 @@
package org.elasticsearch.index.codec.zstd; package org.elasticsearch.index.codec.zstd;
import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.Codec;
import org.apache.lucene.codecs.lucene99.Lucene99Codec; import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.document.StoredField; import org.apache.lucene.document.StoredField;
import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.DirectoryReader;
@ -35,13 +35,13 @@ public class StoredFieldCodecDuelTests extends ESTestCase {
private static final String DOUBLE_FIELD = "double_field_5"; private static final String DOUBLE_FIELD = "double_field_5";
public void testDuelBestSpeed() throws IOException { public void testDuelBestSpeed() throws IOException {
var baseline = new LegacyPerFieldMapperCodec(Lucene99Codec.Mode.BEST_SPEED, null, BigArrays.NON_RECYCLING_INSTANCE); var baseline = new LegacyPerFieldMapperCodec(Lucene912Codec.Mode.BEST_SPEED, null, BigArrays.NON_RECYCLING_INSTANCE);
var contender = new PerFieldMapperCodec(Zstd814StoredFieldsFormat.Mode.BEST_SPEED, null, BigArrays.NON_RECYCLING_INSTANCE); var contender = new PerFieldMapperCodec(Zstd814StoredFieldsFormat.Mode.BEST_SPEED, null, BigArrays.NON_RECYCLING_INSTANCE);
doTestDuel(baseline, contender); doTestDuel(baseline, contender);
} }
public void testDuelBestCompression() throws IOException { public void testDuelBestCompression() throws IOException {
var baseline = new LegacyPerFieldMapperCodec(Lucene99Codec.Mode.BEST_COMPRESSION, null, BigArrays.NON_RECYCLING_INSTANCE); var baseline = new LegacyPerFieldMapperCodec(Lucene912Codec.Mode.BEST_COMPRESSION, null, BigArrays.NON_RECYCLING_INSTANCE);
var contender = new PerFieldMapperCodec(Zstd814StoredFieldsFormat.Mode.BEST_COMPRESSION, null, BigArrays.NON_RECYCLING_INSTANCE); var contender = new PerFieldMapperCodec(Zstd814StoredFieldsFormat.Mode.BEST_COMPRESSION, null, BigArrays.NON_RECYCLING_INSTANCE);
doTestDuel(baseline, contender); doTestDuel(baseline, contender);
} }

View file

@ -11,11 +11,11 @@ package org.elasticsearch.index.codec.zstd;
import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.Codec;
import org.apache.lucene.tests.index.BaseStoredFieldsFormatTestCase; import org.apache.lucene.tests.index.BaseStoredFieldsFormatTestCase;
import org.elasticsearch.index.codec.Elasticsearch814Codec; import org.elasticsearch.index.codec.Elasticsearch816Codec;
public class Zstd814BestCompressionStoredFieldsFormatTests extends BaseStoredFieldsFormatTestCase { public class Zstd814BestCompressionStoredFieldsFormatTests extends BaseStoredFieldsFormatTestCase {
private final Codec codec = new Elasticsearch814Codec(Zstd814StoredFieldsFormat.Mode.BEST_COMPRESSION); private final Codec codec = new Elasticsearch816Codec(Zstd814StoredFieldsFormat.Mode.BEST_COMPRESSION);
@Override @Override
protected Codec getCodec() { protected Codec getCodec() {

View file

@ -11,11 +11,11 @@ package org.elasticsearch.index.codec.zstd;
import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.Codec;
import org.apache.lucene.tests.index.BaseStoredFieldsFormatTestCase; import org.apache.lucene.tests.index.BaseStoredFieldsFormatTestCase;
import org.elasticsearch.index.codec.Elasticsearch814Codec; import org.elasticsearch.index.codec.Elasticsearch816Codec;
public class Zstd814BestSpeedStoredFieldsFormatTests extends BaseStoredFieldsFormatTestCase { public class Zstd814BestSpeedStoredFieldsFormatTests extends BaseStoredFieldsFormatTestCase {
private final Codec codec = new Elasticsearch814Codec(Zstd814StoredFieldsFormat.Mode.BEST_SPEED); private final Codec codec = new Elasticsearch816Codec(Zstd814StoredFieldsFormat.Mode.BEST_SPEED);
@Override @Override
protected Codec getCodec() { protected Codec getCodec() {

View file

@ -9,12 +9,12 @@
package org.elasticsearch.index.engine; package org.elasticsearch.index.engine;
import org.apache.lucene.codecs.PostingsFormat; import org.apache.lucene.codecs.PostingsFormat;
import org.apache.lucene.codecs.lucene99.Lucene99Codec; import org.apache.lucene.codecs.lucene912.Lucene912Codec;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.search.suggest.document.Completion99PostingsFormat; import org.apache.lucene.search.suggest.document.Completion912PostingsFormat;
import org.apache.lucene.search.suggest.document.SuggestField; import org.apache.lucene.search.suggest.document.SuggestField;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
@ -44,8 +44,8 @@ public class CompletionStatsCacheTests extends ESTestCase {
public void testCompletionStatsCache() throws IOException, InterruptedException { public void testCompletionStatsCache() throws IOException, InterruptedException {
final IndexWriterConfig indexWriterConfig = newIndexWriterConfig(); final IndexWriterConfig indexWriterConfig = newIndexWriterConfig();
final PostingsFormat postingsFormat = new Completion99PostingsFormat(); final PostingsFormat postingsFormat = new Completion912PostingsFormat();
indexWriterConfig.setCodec(new Lucene99Codec() { indexWriterConfig.setCodec(new Lucene912Codec() {
@Override @Override
public PostingsFormat getPostingsFormatForField(String field) { public PostingsFormat getPostingsFormatForField(String field) {
return postingsFormat; // all fields are suggest fields return postingsFormat; // all fields are suggest fields

View file

@ -16,7 +16,7 @@ import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.IndexableField;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.suggest.document.Completion99PostingsFormat; import org.apache.lucene.search.suggest.document.Completion912PostingsFormat;
import org.apache.lucene.search.suggest.document.CompletionAnalyzer; import org.apache.lucene.search.suggest.document.CompletionAnalyzer;
import org.apache.lucene.search.suggest.document.ContextSuggestField; import org.apache.lucene.search.suggest.document.ContextSuggestField;
import org.apache.lucene.search.suggest.document.FuzzyCompletionQuery; import org.apache.lucene.search.suggest.document.FuzzyCompletionQuery;
@ -151,7 +151,7 @@ public class CompletionFieldMapperTests extends MapperTestCase {
Codec codec = codecService.codec("default"); Codec codec = codecService.codec("default");
if (CodecService.ZSTD_STORED_FIELDS_FEATURE_FLAG.isEnabled()) { if (CodecService.ZSTD_STORED_FIELDS_FEATURE_FLAG.isEnabled()) {
assertThat(codec, instanceOf(PerFieldMapperCodec.class)); assertThat(codec, instanceOf(PerFieldMapperCodec.class));
assertThat(((PerFieldMapperCodec) codec).getPostingsFormatForField("field"), instanceOf(Completion99PostingsFormat.class)); assertThat(((PerFieldMapperCodec) codec).getPostingsFormatForField("field"), instanceOf(Completion912PostingsFormat.class));
} else { } else {
if (codec instanceof CodecService.DeduplicateFieldInfosCodec deduplicateFieldInfosCodec) { if (codec instanceof CodecService.DeduplicateFieldInfosCodec deduplicateFieldInfosCodec) {
codec = deduplicateFieldInfosCodec.delegate(); codec = deduplicateFieldInfosCodec.delegate();
@ -159,7 +159,7 @@ public class CompletionFieldMapperTests extends MapperTestCase {
assertThat(codec, instanceOf(LegacyPerFieldMapperCodec.class)); assertThat(codec, instanceOf(LegacyPerFieldMapperCodec.class));
assertThat( assertThat(
((LegacyPerFieldMapperCodec) codec).getPostingsFormatForField("field"), ((LegacyPerFieldMapperCodec) codec).getPostingsFormatForField("field"),
instanceOf(Completion99PostingsFormat.class) instanceOf(Completion912PostingsFormat.class)
); );
} }
} }

View file

@ -16,6 +16,7 @@ import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.RegexpQuery; import org.apache.lucene.search.RegexpQuery;
@ -231,20 +232,26 @@ public class ConstantScoreTextFieldTypeTests extends FieldTypeTestCase {
public void testPrefixIntervals() throws IOException { public void testPrefixIntervals() throws IOException {
MappedFieldType ft = createFieldType(); MappedFieldType ft = createFieldType();
IntervalsSource prefixIntervals = ft.prefixIntervals(new BytesRef("foo"), MOCK_CONTEXT); IntervalsSource prefixIntervals = ft.prefixIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertEquals(Intervals.prefix(new BytesRef("foo")), prefixIntervals); assertEquals(Intervals.prefix(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()), prefixIntervals);
} }
public void testWildcardIntervals() throws IOException { public void testWildcardIntervals() throws IOException {
MappedFieldType ft = createFieldType(); MappedFieldType ft = createFieldType();
IntervalsSource wildcardIntervals = ft.wildcardIntervals(new BytesRef("foo"), MOCK_CONTEXT); IntervalsSource wildcardIntervals = ft.wildcardIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertEquals(Intervals.wildcard(new BytesRef("foo")), wildcardIntervals); assertEquals(Intervals.wildcard(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()), wildcardIntervals);
}
public void testRegexpIntervals() {
MappedFieldType ft = createFieldType();
IntervalsSource regexpIntervals = ft.regexpIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertEquals(Intervals.regexp(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()), regexpIntervals);
} }
public void testFuzzyIntervals() throws IOException { public void testFuzzyIntervals() throws IOException {
MappedFieldType ft = createFieldType(); MappedFieldType ft = createFieldType();
IntervalsSource fuzzyIntervals = ft.fuzzyIntervals("foo", 1, 2, true, MOCK_CONTEXT); IntervalsSource fuzzyIntervals = ft.fuzzyIntervals("foo", 1, 2, true, MOCK_CONTEXT);
FuzzyQuery fq = new FuzzyQuery(new Term("field", "foo"), 1, 2, 128, true); FuzzyQuery fq = new FuzzyQuery(new Term("field", "foo"), 1, 2, 128, true);
IntervalsSource expectedIntervals = Intervals.multiterm(fq.getAutomata(), "foo"); IntervalsSource expectedIntervals = Intervals.multiterm(fq.getAutomata(), IndexSearcher.getMaxClauseCount(), "foo");
assertEquals(expectedIntervals, fuzzyIntervals); assertEquals(expectedIntervals, fuzzyIntervals);
} }
@ -259,6 +266,15 @@ public class ConstantScoreTextFieldTypeTests extends FieldTypeTestCase {
ConstantScoreTextFieldType ft = createFieldType(); ConstantScoreTextFieldType ft = createFieldType();
ft.setIndexPrefixes(1, 4); ft.setIndexPrefixes(1, 4);
IntervalsSource wildcardIntervals = ft.wildcardIntervals(new BytesRef("foo"), MOCK_CONTEXT); IntervalsSource wildcardIntervals = ft.wildcardIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertEquals(Intervals.wildcard(new BytesRef("foo")), wildcardIntervals); assertEquals(Intervals.wildcard(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()), wildcardIntervals);
}
public void testRangeIntervals() {
MappedFieldType ft = createFieldType();
IntervalsSource rangeIntervals = ft.rangeIntervals(new BytesRef("foo"), new BytesRef("foo1"), true, true, MOCK_CONTEXT);
assertEquals(
Intervals.range(new BytesRef("foo"), new BytesRef("foo1"), true, true, IndexSearcher.getMaxClauseCount()),
rangeIntervals
);
} }
} }

View file

@ -16,6 +16,7 @@ import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
@ -243,20 +244,26 @@ public class TextFieldTypeTests extends FieldTypeTestCase {
public void testPrefixIntervals() throws IOException { public void testPrefixIntervals() throws IOException {
MappedFieldType ft = createFieldType(); MappedFieldType ft = createFieldType();
IntervalsSource prefixIntervals = ft.prefixIntervals(new BytesRef("foo"), MOCK_CONTEXT); IntervalsSource prefixIntervals = ft.prefixIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertEquals(Intervals.prefix(new BytesRef("foo")), prefixIntervals); assertEquals(Intervals.prefix(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()), prefixIntervals);
} }
public void testWildcardIntervals() throws IOException { public void testWildcardIntervals() {
MappedFieldType ft = createFieldType(); MappedFieldType ft = createFieldType();
IntervalsSource wildcardIntervals = ft.wildcardIntervals(new BytesRef("foo"), MOCK_CONTEXT); IntervalsSource wildcardIntervals = ft.wildcardIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertEquals(Intervals.wildcard(new BytesRef("foo")), wildcardIntervals); assertEquals(Intervals.wildcard(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()), wildcardIntervals);
} }
public void testFuzzyIntervals() throws IOException { public void testRegexpIntervals() {
MappedFieldType ft = createFieldType();
IntervalsSource regexpIntervals = ft.regexpIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertEquals(Intervals.regexp(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()), regexpIntervals);
}
public void testFuzzyIntervals() {
MappedFieldType ft = createFieldType(); MappedFieldType ft = createFieldType();
IntervalsSource fuzzyIntervals = ft.fuzzyIntervals("foo", 1, 2, true, MOCK_CONTEXT); IntervalsSource fuzzyIntervals = ft.fuzzyIntervals("foo", 1, 2, true, MOCK_CONTEXT);
FuzzyQuery fq = new FuzzyQuery(new Term("field", "foo"), 1, 2, 128, true); FuzzyQuery fq = new FuzzyQuery(new Term("field", "foo"), 1, 2, 128, true);
IntervalsSource expectedIntervals = Intervals.multiterm(fq.getAutomata(), "foo"); IntervalsSource expectedIntervals = Intervals.multiterm(fq.getAutomata(), IndexSearcher.getMaxClauseCount(), "foo");
assertEquals(expectedIntervals, fuzzyIntervals); assertEquals(expectedIntervals, fuzzyIntervals);
} }
@ -271,6 +278,15 @@ public class TextFieldTypeTests extends FieldTypeTestCase {
TextFieldType ft = createFieldType(); TextFieldType ft = createFieldType();
ft.setIndexPrefixes(1, 4); ft.setIndexPrefixes(1, 4);
IntervalsSource wildcardIntervals = ft.wildcardIntervals(new BytesRef("foo"), MOCK_CONTEXT); IntervalsSource wildcardIntervals = ft.wildcardIntervals(new BytesRef("foo"), MOCK_CONTEXT);
assertEquals(Intervals.wildcard(new BytesRef("foo")), wildcardIntervals); assertEquals(Intervals.wildcard(new BytesRef("foo"), IndexSearcher.getMaxClauseCount()), wildcardIntervals);
}
public void testRangeIntervals() {
MappedFieldType ft = createFieldType();
IntervalsSource rangeIntervals = ft.rangeIntervals(new BytesRef("foo"), new BytesRef("foo1"), true, true, MOCK_CONTEXT);
assertEquals(
Intervals.range(new BytesRef("foo"), new BytesRef("foo1"), true, true, IndexSearcher.getMaxClauseCount()),
rangeIntervals
);
} }
} }

View file

@ -9,14 +9,22 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import org.apache.lucene.analysis.core.KeywordAnalyzer;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
import org.apache.lucene.queries.intervals.IntervalQuery; import org.apache.lucene.queries.intervals.IntervalQuery;
import org.apache.lucene.queries.intervals.Intervals; import org.apache.lucene.queries.intervals.Intervals;
import org.apache.lucene.queries.intervals.IntervalsSource; import org.apache.lucene.queries.intervals.IntervalsSource;
import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreMode;
import org.apache.lucene.store.Directory;
import org.apache.lucene.tests.index.RandomIndexWriter;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
@ -34,7 +42,9 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static java.util.Collections.singleton;
import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
@ -606,7 +616,7 @@ public class IntervalQueryBuilderTests extends AbstractQueryTestCase<IntervalQue
} }
}""", TEXT_FIELD_NAME); }""", TEXT_FIELD_NAME);
IntervalQueryBuilder builder = (IntervalQueryBuilder) parseQuery(json); IntervalQueryBuilder builder = (IntervalQueryBuilder) parseQuery(json);
Query expected = new IntervalQuery(TEXT_FIELD_NAME, Intervals.prefix(new BytesRef("term"))); Query expected = new IntervalQuery(TEXT_FIELD_NAME, Intervals.prefix(new BytesRef("term"), IndexSearcher.getMaxClauseCount()));
assertEquals(expected, builder.toQuery(createSearchExecutionContext())); assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
String no_positions_json = Strings.format(""" String no_positions_json = Strings.format("""
@ -667,7 +677,13 @@ public class IntervalQueryBuilderTests extends AbstractQueryTestCase<IntervalQue
builder = (IntervalQueryBuilder) parseQuery(short_prefix_json); builder = (IntervalQueryBuilder) parseQuery(short_prefix_json);
expected = new IntervalQuery( expected = new IntervalQuery(
PREFIXED_FIELD, PREFIXED_FIELD,
Intervals.or(Intervals.fixField(PREFIXED_FIELD + "._index_prefix", Intervals.wildcard(new BytesRef("t?"))), Intervals.term("t")) Intervals.or(
Intervals.fixField(
PREFIXED_FIELD + "._index_prefix",
Intervals.wildcard(new BytesRef("t?"), IndexSearcher.getMaxClauseCount())
),
Intervals.term("t")
)
); );
assertEquals(expected, builder.toQuery(createSearchExecutionContext())); assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
@ -726,8 +742,109 @@ public class IntervalQueryBuilderTests extends AbstractQueryTestCase<IntervalQue
assertEquals(expected, builder.toQuery(createSearchExecutionContext())); assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
} }
public void testWildcard() throws IOException { public void testRegexp() throws IOException {
String json = Strings.format("""
{
"intervals": {
"%s": {
"regexp": {
"pattern": "Te.*m"
}
}
}
}""", TEXT_FIELD_NAME);
IntervalQueryBuilder builder = (IntervalQueryBuilder) parseQuery(json);
Query expected = new IntervalQuery(TEXT_FIELD_NAME, Intervals.regexp(new BytesRef("te.*m"), IndexSearcher.getMaxClauseCount()));
assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
String no_positions_json = Strings.format("""
{
"intervals": {
"%s": {
"regexp": {
"pattern": "Te.*m"
}
}
}
}
""", NO_POSITIONS_FIELD);
expectThrows(IllegalArgumentException.class, () -> {
IntervalQueryBuilder builder1 = (IntervalQueryBuilder) parseQuery(no_positions_json);
builder1.toQuery(createSearchExecutionContext());
});
String fixed_field_json = Strings.format("""
{
"intervals": {
"%s": {
"regexp": {
"pattern": "Te.*m",
"use_field": "masked_field"
}
}
}
}""", TEXT_FIELD_NAME);
builder = (IntervalQueryBuilder) parseQuery(fixed_field_json);
expected = new IntervalQuery(
TEXT_FIELD_NAME,
Intervals.fixField(MASKED_FIELD, Intervals.regexp(new BytesRef("te.*m"), IndexSearcher.getMaxClauseCount()))
);
assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
String fixed_field_json_no_positions = Strings.format("""
{
"intervals": {
"%s": {
"regexp": {
"pattern": "Te.*m",
"use_field": "%s"
}
}
}
}""", TEXT_FIELD_NAME, NO_POSITIONS_FIELD);
expectThrows(IllegalArgumentException.class, () -> {
IntervalQueryBuilder builder1 = (IntervalQueryBuilder) parseQuery(fixed_field_json_no_positions);
builder1.toQuery(createSearchExecutionContext());
});
}
public void testMaxExpansionExceptionFailure() throws Exception {
IntervalsSourceProvider provider1 = new IntervalsSourceProvider.Prefix("bar", "keyword", null);
IntervalsSourceProvider provider2 = new IntervalsSourceProvider.Wildcard("bar*", "keyword", null);
IntervalsSourceProvider provider3 = new IntervalsSourceProvider.Fuzzy("bar", 0, true, Fuzziness.fromEdits(1), "keyword", null);
IntervalsSourceProvider provider4 = new IntervalsSourceProvider.Regexp("bar.*", "keyword", null);
IntervalsSourceProvider provider5 = new IntervalsSourceProvider.Range("bar", "bar2", true, true, "keyword", null);
IntervalsSourceProvider provider = randomFrom(provider1, provider2, provider3, provider4, provider5);
try (Directory directory = newDirectory()) {
try (RandomIndexWriter iw = new RandomIndexWriter(random(), directory, new KeywordAnalyzer())) {
for (int i = 0; i < 3; i++) {
iw.addDocument(singleton(new TextField(TEXT_FIELD_NAME, "bar" + i, Field.Store.NO)));
}
try (IndexReader reader = iw.getReader()) {
int origBoolMaxClauseCount = IndexSearcher.getMaxClauseCount();
IndexSearcher.setMaxClauseCount(1);
try {
IntervalQueryBuilder queryBuilder = new IntervalQueryBuilder(TEXT_FIELD_NAME, provider);
IndexSearcher searcher = newSearcher(reader);
Query query = queryBuilder.toQuery(createSearchExecutionContext(searcher));
RuntimeException exc = expectThrows(
RuntimeException.class,
() -> query.createWeight(searcher, ScoreMode.COMPLETE, 1.0f).scorer(searcher.getLeafContexts().get(0))
);
assertThat(exc.getMessage(), containsString("expanded to too many terms (limit 1)"));
} finally {
IndexSearcher.setMaxClauseCount(origBoolMaxClauseCount);
}
}
}
}
}
public void testWildcard() throws IOException {
String json = Strings.format(""" String json = Strings.format("""
{ {
"intervals": { "intervals": {
@ -740,7 +857,7 @@ public class IntervalQueryBuilderTests extends AbstractQueryTestCase<IntervalQue
}""", TEXT_FIELD_NAME); }""", TEXT_FIELD_NAME);
IntervalQueryBuilder builder = (IntervalQueryBuilder) parseQuery(json); IntervalQueryBuilder builder = (IntervalQueryBuilder) parseQuery(json);
Query expected = new IntervalQuery(TEXT_FIELD_NAME, Intervals.wildcard(new BytesRef("te?m"))); Query expected = new IntervalQuery(TEXT_FIELD_NAME, Intervals.wildcard(new BytesRef("te?m"), IndexSearcher.getMaxClauseCount()));
assertEquals(expected, builder.toQuery(createSearchExecutionContext())); assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
String no_positions_json = Strings.format(""" String no_positions_json = Strings.format("""
@ -772,7 +889,7 @@ public class IntervalQueryBuilderTests extends AbstractQueryTestCase<IntervalQue
}""", TEXT_FIELD_NAME); }""", TEXT_FIELD_NAME);
builder = (IntervalQueryBuilder) parseQuery(keyword_json); builder = (IntervalQueryBuilder) parseQuery(keyword_json);
expected = new IntervalQuery(TEXT_FIELD_NAME, Intervals.wildcard(new BytesRef("Te?m"))); expected = new IntervalQuery(TEXT_FIELD_NAME, Intervals.wildcard(new BytesRef("Te?m"), IndexSearcher.getMaxClauseCount()));
assertEquals(expected, builder.toQuery(createSearchExecutionContext())); assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
String fixed_field_json = Strings.format(""" String fixed_field_json = Strings.format("""
@ -788,7 +905,10 @@ public class IntervalQueryBuilderTests extends AbstractQueryTestCase<IntervalQue
}""", TEXT_FIELD_NAME); }""", TEXT_FIELD_NAME);
builder = (IntervalQueryBuilder) parseQuery(fixed_field_json); builder = (IntervalQueryBuilder) parseQuery(fixed_field_json);
expected = new IntervalQuery(TEXT_FIELD_NAME, Intervals.fixField(MASKED_FIELD, Intervals.wildcard(new BytesRef("te?m")))); expected = new IntervalQuery(
TEXT_FIELD_NAME,
Intervals.fixField(MASKED_FIELD, Intervals.wildcard(new BytesRef("te?m"), IndexSearcher.getMaxClauseCount()))
);
assertEquals(expected, builder.toQuery(createSearchExecutionContext())); assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
String fixed_field_json_no_positions = Strings.format(""" String fixed_field_json_no_positions = Strings.format("""
@ -821,13 +941,22 @@ public class IntervalQueryBuilderTests extends AbstractQueryTestCase<IntervalQue
}""", TEXT_FIELD_NAME); }""", TEXT_FIELD_NAME);
builder = (IntervalQueryBuilder) parseQuery(fixed_field_analyzer_json); builder = (IntervalQueryBuilder) parseQuery(fixed_field_analyzer_json);
expected = new IntervalQuery(TEXT_FIELD_NAME, Intervals.fixField(MASKED_FIELD, Intervals.wildcard(new BytesRef("Te?m")))); expected = new IntervalQuery(
TEXT_FIELD_NAME,
Intervals.fixField(MASKED_FIELD, Intervals.wildcard(new BytesRef("Te?m"), IndexSearcher.getMaxClauseCount()))
);
assertEquals(expected, builder.toQuery(createSearchExecutionContext())); assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
} }
private static IntervalsSource buildFuzzySource(String term, String label, int prefixLength, boolean transpositions, int editDistance) { private static IntervalsSource buildFuzzySource(String term, String label, int prefixLength, boolean transpositions, int editDistance) {
FuzzyQuery fq = new FuzzyQuery(new Term("field", term), editDistance, prefixLength, 128, transpositions); FuzzyQuery fq = new FuzzyQuery(
return Intervals.multiterm(fq.getAutomata(), label); new Term("field", term),
editDistance,
prefixLength,
IndexSearcher.getMaxClauseCount(),
transpositions
);
return Intervals.multiterm(fq.getAutomata(), IndexSearcher.getMaxClauseCount(), label);
} }
public void testFuzzy() throws IOException { public void testFuzzy() throws IOException {
@ -932,7 +1061,77 @@ public class IntervalQueryBuilderTests extends AbstractQueryTestCase<IntervalQue
Intervals.fixField(MASKED_FIELD, buildFuzzySource("term", "term", 2, true, Fuzziness.ONE.asDistance("term"))) Intervals.fixField(MASKED_FIELD, buildFuzzySource("term", "term", 2, true, Fuzziness.ONE.asDistance("term")))
); );
assertEquals(expected, builder.toQuery(createSearchExecutionContext())); assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
} }
public void testRange() throws IOException {
String json = Strings.format("""
{
"intervals": {
"%s": {
"range": {
"gte": "aaa",
"lte": "aab"
}
}
}
}""", TEXT_FIELD_NAME);
IntervalQueryBuilder builder = (IntervalQueryBuilder) parseQuery(json);
Query expected = new IntervalQuery(
TEXT_FIELD_NAME,
Intervals.range(new BytesRef("aaa"), new BytesRef("aab"), true, true, IndexSearcher.getMaxClauseCount())
);
assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
json = Strings.format("""
{
"intervals": {
"%s": {
"range": {
"gt": "aaa",
"lt": "aab"
}
}
}
}""", TEXT_FIELD_NAME);
builder = (IntervalQueryBuilder) parseQuery(json);
expected = new IntervalQuery(
TEXT_FIELD_NAME,
Intervals.range(new BytesRef("aaa"), new BytesRef("aab"), false, false, IndexSearcher.getMaxClauseCount())
);
assertEquals(expected, builder.toQuery(createSearchExecutionContext()));
String incomplete_range = Strings.format("""
{
"intervals": {
"%s": {
"range": {
"gt": "aaa"
}
}
}
}
""", TEXT_FIELD_NAME);
IllegalArgumentException exc = expectThrows(IllegalArgumentException.class, () -> {
IntervalQueryBuilder builder1 = (IntervalQueryBuilder) parseQuery(incomplete_range);
builder1.toQuery(createSearchExecutionContext());
});
assertEquals("Either [lte] or [lt], one of them must be provided", exc.getCause().getMessage());
String incomplete_range2 = Strings.format("""
{
"intervals": {
"%s": {
"range": {
"lt": "aaa"
}
}
}
}
""", TEXT_FIELD_NAME);
exc = expectThrows(IllegalArgumentException.class, () -> {
IntervalQueryBuilder builder1 = (IntervalQueryBuilder) parseQuery(incomplete_range2);
builder1.toQuery(createSearchExecutionContext());
});
assertEquals("Either [gte] or [gt], one of them must be provided", exc.getCause().getMessage());
}
} }

View file

@ -0,0 +1,71 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
package org.elasticsearch.index.query;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.AbstractXContentSerializingTestCase;
import org.elasticsearch.xcontent.XContentParser;
import java.io.IOException;
import static org.elasticsearch.index.query.IntervalsSourceProvider.Range;
public class RangeIntervalsSourceProviderTests extends AbstractXContentSerializingTestCase<Range> {
@Override
protected Range createTestInstance() {
return createRandomRange();
}
static Range createRandomRange() {
return new Range(
"a" + randomAlphaOfLengthBetween(1, 10),
"z" + randomAlphaOfLengthBetween(1, 10),
randomBoolean(),
randomBoolean(),
randomBoolean() ? randomAlphaOfLength(10) : null,
randomBoolean() ? randomAlphaOfLength(10) : null
);
}
@Override
protected Range mutateInstance(Range instance) {
String lowerTerm = instance.getLowerTerm();
String upperTerm = instance.getUpperTerm();
boolean includeLower = instance.getIncludeLower();
boolean includeUpper = instance.getIncludeUpper();
String analyzer = instance.getAnalyzer();
String useField = instance.getUseField();
switch (between(0, 5)) {
case 0 -> lowerTerm = "a" + lowerTerm;
case 1 -> upperTerm = "z" + upperTerm;
case 2 -> includeLower = includeLower == false;
case 3 -> includeUpper = includeUpper == false;
case 4 -> analyzer = randomAlphaOfLength(5);
case 5 -> useField = useField == null ? randomAlphaOfLength(5) : null;
}
return new Range(lowerTerm, upperTerm, includeLower, includeUpper, analyzer, useField);
}
@Override
protected Writeable.Reader<Range> instanceReader() {
return Range::new;
}
@Override
protected Range doParseInstance(XContentParser parser) throws IOException {
if (parser.nextToken() == XContentParser.Token.START_OBJECT) {
parser.nextToken();
}
Range range = (Range) IntervalsSourceProvider.fromXContent(parser);
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
return range;
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
package org.elasticsearch.index.query;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.AbstractXContentSerializingTestCase;
import org.elasticsearch.xcontent.XContentParser;
import java.io.IOException;
import static org.elasticsearch.index.query.IntervalsSourceProvider.Regexp;
public class RegexpIntervalsSourceProviderTests extends AbstractXContentSerializingTestCase<Regexp> {
@Override
protected Regexp createTestInstance() {
return createRandomRegexp();
}
static Regexp createRandomRegexp() {
return new Regexp(
randomAlphaOfLengthBetween(1, 10),
randomBoolean() ? randomAlphaOfLength(10) : null,
randomBoolean() ? randomAlphaOfLength(10) : null
);
}
@Override
protected Regexp mutateInstance(Regexp instance) {
String regexp = instance.getPattern();
String analyzer = instance.getAnalyzer();
String useField = instance.getUseField();
switch (between(0, 2)) {
case 0 -> regexp += "a";
case 1 -> analyzer = randomAlphaOfLength(5);
case 2 -> useField = useField == null ? randomAlphaOfLength(5) : null;
}
return new Regexp(regexp, analyzer, useField);
}
@Override
protected Writeable.Reader<Regexp> instanceReader() {
return Regexp::new;
}
@Override
protected Regexp doParseInstance(XContentParser parser) throws IOException {
if (parser.nextToken() == XContentParser.Token.START_OBJECT) {
parser.nextToken();
}
Regexp regexp = (Regexp) IntervalsSourceProvider.fromXContent(parser);
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
return regexp;
}
}

View file

@ -274,7 +274,7 @@ public class StoreTests extends ESTestCase {
metadata = store.getMetadata(randomBoolean() ? indexCommit : null); metadata = store.getMetadata(randomBoolean() ? indexCommit : null);
assertThat(metadata.fileMetadataMap().isEmpty(), is(false)); assertThat(metadata.fileMetadataMap().isEmpty(), is(false));
for (StoreFileMetadata meta : metadata) { for (StoreFileMetadata meta : metadata) {
try (IndexInput input = store.directory().openInput(meta.name(), IOContext.DEFAULT)) { try (IndexInput input = store.directory().openInput(meta.name(), IOContext.READONCE)) {
String checksum = Store.digestToString(CodecUtil.retrieveChecksum(input)); String checksum = Store.digestToString(CodecUtil.retrieveChecksum(input));
assertThat("File: " + meta.name() + " has a different checksum", meta.checksum(), equalTo(checksum)); assertThat("File: " + meta.name() + " has a different checksum", meta.checksum(), equalTo(checksum));
assertThat(meta.writtenBy(), equalTo(Version.LATEST.toString())); assertThat(meta.writtenBy(), equalTo(Version.LATEST.toString()));

View file

@ -2742,13 +2742,13 @@ public class SearchServiceTests extends ESSingleNodeTestCase {
} }
/** /**
* Verify that a single slice is created for requests that don't support parallel collection, while computation * Verify that a single slice is created for requests that don't support parallel collection, while an executor is still
* is still offloaded to the worker threads. Also ensure multiple slices are created for requests that do support * provided to the searcher to parallelize other operations. Also ensure multiple slices are created for requests that do support
* parallel collection. * parallel collection.
*/ */
public void testSlicingBehaviourForParallelCollection() throws Exception { public void testSlicingBehaviourForParallelCollection() throws Exception {
IndexService indexService = createIndex("index", Settings.EMPTY); IndexService indexService = createIndex("index", Settings.EMPTY);
ThreadPoolExecutor executor = (ThreadPoolExecutor) indexService.getThreadPool().executor(ThreadPool.Names.SEARCH_WORKER); ThreadPoolExecutor executor = (ThreadPoolExecutor) indexService.getThreadPool().executor(ThreadPool.Names.SEARCH);
final int configuredMaxPoolSize = 10; final int configuredMaxPoolSize = 10;
executor.setMaximumPoolSize(configuredMaxPoolSize); // We set this explicitly to be independent of CPU cores. executor.setMaximumPoolSize(configuredMaxPoolSize); // We set this explicitly to be independent of CPU cores.
int numDocs = randomIntBetween(50, 100); int numDocs = randomIntBetween(50, 100);
@ -2799,7 +2799,7 @@ public class SearchServiceTests extends ESSingleNodeTestCase {
assertBusy( assertBusy(
() -> assertEquals( () -> assertEquals(
"DFS supports parallel collection, so the number of slices should be > 1.", "DFS supports parallel collection, so the number of slices should be > 1.",
expectedSlices, expectedSlices - 1, // one slice executes on the calling thread
executor.getCompletedTaskCount() - priorExecutorTaskCount executor.getCompletedTaskCount() - priorExecutorTaskCount
) )
); );
@ -2829,7 +2829,7 @@ public class SearchServiceTests extends ESSingleNodeTestCase {
assertBusy( assertBusy(
() -> assertEquals( () -> assertEquals(
"QUERY supports parallel collection when enabled, so the number of slices should be > 1.", "QUERY supports parallel collection when enabled, so the number of slices should be > 1.",
expectedSlices, expectedSlices - 1, // one slice executes on the calling thread
executor.getCompletedTaskCount() - priorExecutorTaskCount executor.getCompletedTaskCount() - priorExecutorTaskCount
) )
); );
@ -2838,13 +2838,14 @@ public class SearchServiceTests extends ESSingleNodeTestCase {
{ {
try (SearchContext searchContext = service.createContext(readerContext, request, task, ResultsType.FETCH, true)) { try (SearchContext searchContext = service.createContext(readerContext, request, task, ResultsType.FETCH, true)) {
ContextIndexSearcher searcher = searchContext.searcher(); ContextIndexSearcher searcher = searchContext.searcher();
assertNotNull(searcher.getExecutor()); assertNull(searcher.getExecutor());
final long priorExecutorTaskCount = executor.getCompletedTaskCount(); final long priorExecutorTaskCount = executor.getCompletedTaskCount();
searcher.search(termQuery, new TotalHitCountCollectorManager()); searcher.search(termQuery, new TotalHitCountCollectorManager());
assertBusy( assertBusy(
() -> assertEquals( () -> assertEquals(
"The number of slices should be 1 as FETCH does not support parallel collection.", "The number of slices should be 1 as FETCH does not support parallel collection and thus runs on the calling"
1, + " thread.",
0,
executor.getCompletedTaskCount() - priorExecutorTaskCount executor.getCompletedTaskCount() - priorExecutorTaskCount
) )
); );
@ -2853,13 +2854,13 @@ public class SearchServiceTests extends ESSingleNodeTestCase {
{ {
try (SearchContext searchContext = service.createContext(readerContext, request, task, ResultsType.NONE, true)) { try (SearchContext searchContext = service.createContext(readerContext, request, task, ResultsType.NONE, true)) {
ContextIndexSearcher searcher = searchContext.searcher(); ContextIndexSearcher searcher = searchContext.searcher();
assertNotNull(searcher.getExecutor()); assertNull(searcher.getExecutor());
final long priorExecutorTaskCount = executor.getCompletedTaskCount(); final long priorExecutorTaskCount = executor.getCompletedTaskCount();
searcher.search(termQuery, new TotalHitCountCollectorManager()); searcher.search(termQuery, new TotalHitCountCollectorManager());
assertBusy( assertBusy(
() -> assertEquals( () -> assertEquals(
"The number of slices should be 1 as NONE does not support parallel collection.", "The number of slices should be 1 as NONE does not support parallel collection.",
1, 0, // zero since one slice executes on the calling thread
executor.getCompletedTaskCount() - priorExecutorTaskCount executor.getCompletedTaskCount() - priorExecutorTaskCount
) )
); );
@ -2876,13 +2877,13 @@ public class SearchServiceTests extends ESSingleNodeTestCase {
{ {
try (SearchContext searchContext = service.createContext(readerContext, request, task, ResultsType.QUERY, true)) { try (SearchContext searchContext = service.createContext(readerContext, request, task, ResultsType.QUERY, true)) {
ContextIndexSearcher searcher = searchContext.searcher(); ContextIndexSearcher searcher = searchContext.searcher();
assertNotNull(searcher.getExecutor()); assertNull(searcher.getExecutor());
final long priorExecutorTaskCount = executor.getCompletedTaskCount(); final long priorExecutorTaskCount = executor.getCompletedTaskCount();
searcher.search(termQuery, new TotalHitCountCollectorManager()); searcher.search(termQuery, new TotalHitCountCollectorManager());
assertBusy( assertBusy(
() -> assertEquals( () -> assertEquals(
"The number of slices should be 1 when QUERY parallel collection is disabled.", "The number of slices should be 1 when QUERY parallel collection is disabled.",
1, 0, // zero since one slice executes on the calling thread
executor.getCompletedTaskCount() - priorExecutorTaskCount executor.getCompletedTaskCount() - priorExecutorTaskCount
) )
); );
@ -2919,7 +2920,7 @@ public class SearchServiceTests extends ESSingleNodeTestCase {
assertBusy( assertBusy(
() -> assertEquals( () -> assertEquals(
"QUERY supports parallel collection when enabled, so the number of slices should be > 1.", "QUERY supports parallel collection when enabled, so the number of slices should be > 1.",
expectedSlices, expectedSlices - 1, // one slice executes on the calling thread
executor.getCompletedTaskCount() - priorExecutorTaskCount executor.getCompletedTaskCount() - priorExecutorTaskCount
) )
); );

View file

@ -40,7 +40,7 @@ public class DfsPhaseTests extends ESTestCase {
@Before @Before
public final void init() { public final void init() {
threadPool = new TestThreadPool(DfsPhaseTests.class.getName()); threadPool = new TestThreadPool(DfsPhaseTests.class.getName());
threadPoolExecutor = (ThreadPoolExecutor) threadPool.executor(ThreadPool.Names.SEARCH_WORKER); threadPoolExecutor = (ThreadPoolExecutor) threadPool.executor(ThreadPool.Names.SEARCH);
} }
@After @After

View file

@ -224,7 +224,8 @@ public class ContextIndexSearcherTests extends ESTestCase {
int numSegments = directoryReader.getContext().leaves().size(); int numSegments = directoryReader.getContext().leaves().size();
KnnFloatVectorQuery vectorQuery = new KnnFloatVectorQuery("float_vector", new float[] { 0, 0, 0 }, 10, null); KnnFloatVectorQuery vectorQuery = new KnnFloatVectorQuery("float_vector", new float[] { 0, 0, 0 }, 10, null);
vectorQuery.rewrite(searcher); vectorQuery.rewrite(searcher);
assertBusy(() -> assertEquals(numSegments, executor.getCompletedTaskCount())); // 1 task gets executed on the caller thread
assertBusy(() -> assertEquals(numSegments - 1, executor.getCompletedTaskCount()));
} }
} finally { } finally {
terminate(executor); terminate(executor);
@ -253,8 +254,9 @@ public class ContextIndexSearcherTests extends ESTestCase {
Integer totalHits = searcher.search(new MatchAllDocsQuery(), new TotalHitCountCollectorManager()); Integer totalHits = searcher.search(new MatchAllDocsQuery(), new TotalHitCountCollectorManager());
assertEquals(numDocs, totalHits.intValue()); assertEquals(numDocs, totalHits.intValue());
int numExpectedTasks = ContextIndexSearcher.computeSlices(searcher.getIndexReader().leaves(), Integer.MAX_VALUE, 1).length; int numExpectedTasks = ContextIndexSearcher.computeSlices(searcher.getIndexReader().leaves(), Integer.MAX_VALUE, 1).length;
// check that each slice goes to the executor, no matter the queue size or the number of slices // check that each slice except for one that executes on the calling thread goes to the executor, no matter the queue size
assertBusy(() -> assertEquals(numExpectedTasks, executor.getCompletedTaskCount())); // or the number of slices
assertBusy(() -> assertEquals(numExpectedTasks - 1, executor.getCompletedTaskCount()));
} }
} finally { } finally {
terminate(executor); terminate(executor);

View file

@ -1850,8 +1850,6 @@ public class SnapshotResiliencyTests extends ESTestCase {
Settings.builder() Settings.builder()
.put(NODE_NAME_SETTING.getKey(), nodeName) .put(NODE_NAME_SETTING.getKey(), nodeName)
.put(PATH_HOME_SETTING.getKey(), tempDir.resolve(nodeName).toAbsolutePath()) .put(PATH_HOME_SETTING.getKey(), tempDir.resolve(nodeName).toAbsolutePath())
// test uses the same executor service for all thread pools, search worker would need to be a different one
.put(SearchService.SEARCH_WORKER_THREADS_ENABLED.getKey(), false)
.put(Environment.PATH_REPO_SETTING.getKey(), tempDir.resolve("repo").toAbsolutePath()) .put(Environment.PATH_REPO_SETTING.getKey(), tempDir.resolve("repo").toAbsolutePath())
.putList( .putList(
ClusterBootstrapService.INITIAL_MASTER_NODES_SETTING.getKey(), ClusterBootstrapService.INITIAL_MASTER_NODES_SETTING.getKey(),

View file

@ -25,8 +25,6 @@ import org.elasticsearch.test.MockLog;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.elasticsearch.common.util.concurrent.EsExecutors.TaskTrackingConfig.DEFAULT; import static org.elasticsearch.common.util.concurrent.EsExecutors.TaskTrackingConfig.DEFAULT;
@ -371,25 +369,6 @@ public class ThreadPoolTests extends ESTestCase {
} }
} }
public void testSearchWorkedThreadPool() {
final int allocatedProcessors = randomIntBetween(1, EsExecutors.allocatedProcessors(Settings.EMPTY));
final ThreadPool threadPool = new TestThreadPool(
"test",
Settings.builder().put(EsExecutors.NODE_PROCESSORS_SETTING.getKey(), allocatedProcessors).build()
);
try {
ExecutorService executor = threadPool.executor(ThreadPool.Names.SEARCH_WORKER);
assertThat(executor, instanceOf(ThreadPoolExecutor.class));
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
int expectedPoolSize = allocatedProcessors * 3 / 2 + 1;
assertEquals(expectedPoolSize, threadPoolExecutor.getCorePoolSize());
assertEquals(expectedPoolSize, threadPoolExecutor.getMaximumPoolSize());
assertThat(threadPoolExecutor.getQueue(), instanceOf(LinkedTransferQueue.class));
} finally {
assertTrue(terminate(threadPool));
}
}
public void testScheduledOneShotRejection() { public void testScheduledOneShotRejection() {
final var name = "fixed-bounded"; final var name = "fixed-bounded";
final var threadPool = new TestThreadPool( final var threadPool = new TestThreadPool(

View file

@ -212,7 +212,7 @@ public abstract class AggregatorTestCase extends ESTestCase {
@Before @Before
public final void initPlugins() { public final void initPlugins() {
threadPool = new TestThreadPool(AggregatorTestCase.class.getName()); threadPool = new TestThreadPool(AggregatorTestCase.class.getName());
threadPoolExecutor = (ThreadPoolExecutor) threadPool.executor(ThreadPool.Names.SEARCH_WORKER); threadPoolExecutor = (ThreadPoolExecutor) threadPool.executor(ThreadPool.Names.SEARCH);
List<SearchPlugin> plugins = new ArrayList<>(getSearchPlugins()); List<SearchPlugin> plugins = new ArrayList<>(getSearchPlugins());
plugins.add(new AggCardinalityUpperBoundPlugin()); plugins.add(new AggCardinalityUpperBoundPlugin());
SearchModule searchModule = new SearchModule(Settings.EMPTY, plugins); SearchModule searchModule = new SearchModule(Settings.EMPTY, plugins);

View file

@ -9,33 +9,22 @@
package org.elasticsearch.search.internal; package org.elasticsearch.search.internal;
import org.elasticsearch.core.TimeValue; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.search.SearchService; import org.elasticsearch.search.SearchService;
import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.test.ESSingleNodeTestCase;
import java.io.IOException;
public class ConcurrentSearchSingleNodeTests extends ESSingleNodeTestCase { public class ConcurrentSearchSingleNodeTests extends ESSingleNodeTestCase {
private final boolean concurrentSearch = randomBoolean(); private final boolean concurrentSearch = randomBoolean();
public void testConcurrentSearch() throws IOException { public void testConcurrentSearch() {
client().admin().indices().prepareCreate("index").get(); client().admin().indices().prepareCreate("index").get();
IndicesService indicesService = getInstanceFromNode(IndicesService.class); ClusterService clusterService = getInstanceFromNode(ClusterService.class);
IndexService indexService = indicesService.iterator().next(); int minDocsPerSlice = SearchService.MINIMUM_DOCS_PER_SLICE.get(clusterService.getSettings());
IndexShard shard = indexService.getShard(0);
SearchService searchService = getInstanceFromNode(SearchService.class);
ShardSearchRequest shardSearchRequest = new ShardSearchRequest(shard.shardId(), 0L, AliasFilter.EMPTY);
try (SearchContext searchContext = searchService.createSearchContext(shardSearchRequest, TimeValue.MINUS_ONE)) {
ContextIndexSearcher searcher = searchContext.searcher();
if (concurrentSearch) { if (concurrentSearch) {
assertEquals(1, searcher.getMinimumDocsPerSlice()); assertEquals(1, minDocsPerSlice);
} else { } else {
assertEquals(50_000, searcher.getMinimumDocsPerSlice()); assertEquals(50_000, minDocsPerSlice);
}
} }
} }

View file

@ -9,34 +9,23 @@
package org.elasticsearch.search.internal; package org.elasticsearch.search.internal;
import org.elasticsearch.core.TimeValue; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.search.SearchService; import org.elasticsearch.search.SearchService;
import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase;
import java.io.IOException;
@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 1) @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 1)
public class ConcurrentSearchTestPluginTests extends ESIntegTestCase { public class ConcurrentSearchTestPluginTests extends ESIntegTestCase {
private final boolean concurrentSearch = randomBoolean(); private final boolean concurrentSearch = randomBoolean();
public void testConcurrentSearch() throws IOException { public void testConcurrentSearch() {
client().admin().indices().prepareCreate("index").get(); client().admin().indices().prepareCreate("index").get();
IndicesService indicesService = internalCluster().getDataNodeInstance(IndicesService.class); ClusterService clusterService = internalCluster().getDataNodeInstance(ClusterService.class);
IndexService indexService = indicesService.iterator().next(); int minDocsPerSlice = SearchService.MINIMUM_DOCS_PER_SLICE.get(clusterService.getSettings());
IndexShard shard = indexService.getShard(0);
SearchService searchService = internalCluster().getDataNodeInstance(SearchService.class);
ShardSearchRequest shardSearchRequest = new ShardSearchRequest(shard.shardId(), 0L, AliasFilter.EMPTY);
try (SearchContext searchContext = searchService.createSearchContext(shardSearchRequest, TimeValue.MINUS_ONE)) {
ContextIndexSearcher searcher = searchContext.searcher();
if (concurrentSearch) { if (concurrentSearch) {
assertEquals(1, searcher.getMinimumDocsPerSlice()); assertEquals(1, minDocsPerSlice);
} else { } else {
assertEquals(50_000, searcher.getMinimumDocsPerSlice()); assertEquals(50_000, minDocsPerSlice);
}
} }
} }

View file

@ -9,6 +9,7 @@ package org.elasticsearch.xpack.ccr.repository;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.lucene.index.IndexFileNames;
import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IOContext;
import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexInput;
import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.AbstractLifecycleComponent;
@ -244,9 +245,10 @@ public class CcrRestoreSourceService extends AbstractLifecycleComponent implemen
private long readFileBytes(String fileName, ByteArray reference) throws IOException { private long readFileBytes(String fileName, ByteArray reference) throws IOException {
try (Releasable ignored = keyedLock.acquire(fileName)) { try (Releasable ignored = keyedLock.acquire(fileName)) {
var context = fileName.startsWith(IndexFileNames.SEGMENTS) ? IOContext.READONCE : IOContext.READ;
final IndexInput indexInput = cachedInputs.computeIfAbsent(fileName, f -> { final IndexInput indexInput = cachedInputs.computeIfAbsent(fileName, f -> {
try { try {
return commitRef.getIndexCommit().getDirectory().openInput(fileName, IOContext.READONCE); return commitRef.getIndexCommit().getDirectory().openInput(fileName, context);
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }
@ -256,7 +258,7 @@ public class CcrRestoreSourceService extends AbstractLifecycleComponent implemen
long offsetAfterRead = indexInput.getFilePointer(); long offsetAfterRead = indexInput.getFilePointer();
if (offsetAfterRead == indexInput.length()) { if (offsetAfterRead == indexInput.length() || context == IOContext.READONCE) {
cachedInputs.remove(fileName); cachedInputs.remove(fileName);
IOUtils.close(indexInput); IOUtils.close(indexInput);
} }

View file

@ -196,7 +196,7 @@ public class OldSegmentInfos implements Cloneable, Iterable<SegmentCommitInfo> {
long generation = generationFromSegmentsFileName(segmentFileName); long generation = generationFromSegmentsFileName(segmentFileName);
// System.out.println(Thread.currentThread() + ": SegmentInfos.readCommit " + segmentFileName); // System.out.println(Thread.currentThread() + ": SegmentInfos.readCommit " + segmentFileName);
try (ChecksumIndexInput input = directory.openChecksumInput(segmentFileName, IOContext.READ)) { try (ChecksumIndexInput input = directory.openChecksumInput(segmentFileName, IOContext.READONCE)) {
try { try {
return readCommit(directory, input, generation, minSupportedMajorVersion); return readCommit(directory, input, generation, minSupportedMajorVersion);
} catch (EOFException | NoSuchFileException | FileNotFoundException e) { } catch (EOFException | NoSuchFileException | FileNotFoundException e) {

View file

@ -221,7 +221,6 @@ public abstract class MetadataCachingIndexInput extends BlobCacheBufferedIndexIn
ThreadPool.Names.SNAPSHOT, ThreadPool.Names.SNAPSHOT,
ThreadPool.Names.GENERIC, ThreadPool.Names.GENERIC,
ThreadPool.Names.SEARCH, ThreadPool.Names.SEARCH,
ThreadPool.Names.SEARCH_WORKER,
ThreadPool.Names.SEARCH_THROTTLED, ThreadPool.Names.SEARCH_THROTTLED,
// Cache asynchronous fetching runs on a dedicated thread pool. // Cache asynchronous fetching runs on a dedicated thread pool.

View file

@ -692,7 +692,7 @@ public class SearchableSnapshotDirectoryTests extends AbstractSearchableSnapshot
private void testIndexInputs(final CheckedBiConsumer<IndexInput, IndexInput, Exception> consumer) throws Exception { private void testIndexInputs(final CheckedBiConsumer<IndexInput, IndexInput, Exception> consumer) throws Exception {
testDirectories((directory, snapshotDirectory) -> { testDirectories((directory, snapshotDirectory) -> {
for (String fileName : randomSubsetOf(Arrays.asList(snapshotDirectory.listAll()))) { for (String fileName : randomSubsetOf(Arrays.asList(snapshotDirectory.listAll()))) {
final IOContext context = randomIOContext(); final IOContext context = fileName.startsWith(IndexFileNames.SEGMENTS) ? IOContext.READONCE : randomIOContext();
try (IndexInput indexInput = directory.openInput(fileName, context)) { try (IndexInput indexInput = directory.openInput(fileName, context)) {
final List<Closeable> closeables = new ArrayList<>(); final List<Closeable> closeables = new ArrayList<>();
try { try {

View file

@ -29,7 +29,7 @@ subprojects {
restResources { restResources {
restApi { restApi {
include '_common', 'bulk', 'count', 'cluster', 'index', 'indices', 'field_caps', 'msearch', include 'capabilities', '_common', 'bulk', 'count', 'cluster', 'index', 'indices', 'field_caps', 'msearch',
'search', 'async_search', 'graph', '*_point_in_time', 'put_script', 'scripts_painless_execute' 'search', 'async_search', 'graph', '*_point_in_time', 'put_script', 'scripts_painless_execute'
} }
restTests { restTests {