Implement multi_match function for ESQL. Its currently available on snapshot builds pending refinement of the syntax.
This commit is contained in:
parent
ac0972bd67
commit
02f9af732e
|
@ -0,0 +1,5 @@
|
||||||
|
pr: 125062
|
||||||
|
summary: "Add initial version (behind snapshot) of `multi_match` function #121525"
|
||||||
|
area: Search
|
||||||
|
type: feature
|
||||||
|
issues: [121525]
|
6
docs/reference/query-languages/esql/_snippets/functions/description/multi_match.md
generated
Normal file
6
docs/reference/query-languages/esql/_snippets/functions/description/multi_match.md
generated
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
|
||||||
|
Use `MULTI_MATCH` to perform a [multi-match query](/reference/query-languages/query-dsl/query-dsl-match-query.md#query-dsl-multi-match-query) on the specified field. The multi_match query builds on the match query to allow multi-field queries.
|
||||||
|
|
31
docs/reference/query-languages/esql/_snippets/functions/examples/multi_match.md
generated
Normal file
31
docs/reference/query-languages/esql/_snippets/functions/examples/multi_match.md
generated
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
|
||||||
|
```esql
|
||||||
|
FROM books
|
||||||
|
| WHERE MULTI_MATCH("Faulkner", author, description, {"fuzziness": 1})
|
||||||
|
| KEEP book_no, author
|
||||||
|
| SORT book_no
|
||||||
|
| LIMIT 5
|
||||||
|
```
|
||||||
|
|
||||||
|
| book_no:keyword | author:text |
|
||||||
|
| --- | --- |
|
||||||
|
| 2378 | [Carol Faulkner, Holly Byers Ochoa, Lucretia Mott] |
|
||||||
|
| 2713 | William Faulkner |
|
||||||
|
| 2847 | Colleen Faulkner |
|
||||||
|
| 2883 | William Faulkner |
|
||||||
|
| 3293 | Danny Faulkner |
|
||||||
|
|
||||||
|
```esql
|
||||||
|
FROM books
|
||||||
|
| WHERE MULTI_MATCH("Hobbit Back Again", title, description, {"operator": "AND"})
|
||||||
|
| KEEP title;
|
||||||
|
```
|
||||||
|
|
||||||
|
| title:text |
|
||||||
|
| --- |
|
||||||
|
| The Hobbit or There and Back Again |
|
||||||
|
|
||||||
|
|
43
docs/reference/query-languages/esql/_snippets/functions/functionNamedParams/multi_match.md
generated
Normal file
43
docs/reference/query-languages/esql/_snippets/functions/functionNamedParams/multi_match.md
generated
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
|
||||||
|
|
||||||
|
**Supported function named parameters**
|
||||||
|
|
||||||
|
`fuzziness`
|
||||||
|
: (keyword) Maximum edit distance allowed for matching.
|
||||||
|
|
||||||
|
`auto_generate_synonyms_phrase_query`
|
||||||
|
: (boolean) If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.
|
||||||
|
|
||||||
|
`minimum_should_match`
|
||||||
|
: (integer) Minimum number of clauses that must match for a document to be returned.
|
||||||
|
|
||||||
|
`fuzzy_transpositions`
|
||||||
|
: (boolean) If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.
|
||||||
|
|
||||||
|
`tie_breaker`
|
||||||
|
: (float) Controls how score is blended together between field groups. Defaults to 0 (best score from each group).
|
||||||
|
|
||||||
|
`type`
|
||||||
|
: (object) Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.
|
||||||
|
|
||||||
|
`lenient`
|
||||||
|
: (boolean) If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.
|
||||||
|
|
||||||
|
`operator`
|
||||||
|
: (keyword) Boolean logic used to interpret text in the query value. Defaults to OR.
|
||||||
|
|
||||||
|
`max_expansions`
|
||||||
|
: (integer) Maximum number of terms to which the query will expand. Defaults to 50.
|
||||||
|
|
||||||
|
`analyzer`
|
||||||
|
: (keyword) Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.
|
||||||
|
|
||||||
|
`boost`
|
||||||
|
: (float) Floating point number used to decrease or increase the relevance scores of the query.
|
||||||
|
|
||||||
|
`fuzzy_rewrite`
|
||||||
|
: (keyword) Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.
|
||||||
|
|
||||||
|
`prefix_length`
|
||||||
|
: (integer) Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.
|
||||||
|
|
36
docs/reference/query-languages/esql/_snippets/functions/layout/multi_match.md
generated
Normal file
36
docs/reference/query-languages/esql/_snippets/functions/layout/multi_match.md
generated
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
|
||||||
|
|
||||||
|
## `MULTI_MATCH` [esql-multi_match]
|
||||||
|
:::{warning}
|
||||||
|
Do not use on production environments. This functionality is in technical preview and
|
||||||
|
may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview
|
||||||
|
are not subject to the support SLA of official GA features.
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{note}
|
||||||
|
###### Serverless: GA, Elastic Stack: COMING 9.1.0
|
||||||
|
Support for optional named parameters is only available from 9.1.0
|
||||||
|
:::
|
||||||
|
|
||||||
|
**Syntax**
|
||||||
|
|
||||||
|
:::{image} ../../../images/functions/multi_match.svg
|
||||||
|
:alt: Embedded
|
||||||
|
:class: text-center
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
:::{include} ../parameters/multi_match.md
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{include} ../description/multi_match.md
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{include} ../types/multi_match.md
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{include} ../functionNamedParams/multi_match.md
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{include} ../examples/multi_match.md
|
||||||
|
:::
|
13
docs/reference/query-languages/esql/_snippets/functions/parameters/multi_match.md
generated
Normal file
13
docs/reference/query-languages/esql/_snippets/functions/parameters/multi_match.md
generated
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
`query`
|
||||||
|
: Value to find in the provided fields.
|
||||||
|
|
||||||
|
`fields`
|
||||||
|
: Fields to use for matching
|
||||||
|
|
||||||
|
`options`
|
||||||
|
: (Optional) Additional options for MultiMatch, passed as [function named parameters](/reference/query-languages/esql/esql-syntax.md#esql-function-named-params)." See [multi-match query](/reference/query-languages/query-dsl/query-dsl-match-query.md#query-dsl-multi-match-query) for more information.
|
||||||
|
|
36
docs/reference/query-languages/esql/_snippets/functions/types/multi_match.md
generated
Normal file
36
docs/reference/query-languages/esql/_snippets/functions/types/multi_match.md
generated
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
|
||||||
|
|
||||||
|
**Supported types**
|
||||||
|
|
||||||
|
| query | fields | options | result |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| boolean | boolean | named parameters | boolean |
|
||||||
|
| boolean | keyword | named parameters | boolean |
|
||||||
|
| date | date | named parameters | boolean |
|
||||||
|
| date | keyword | named parameters | boolean |
|
||||||
|
| date_nanos | date_nanos | named parameters | boolean |
|
||||||
|
| date_nanos | keyword | named parameters | boolean |
|
||||||
|
| double | double | named parameters | boolean |
|
||||||
|
| double | integer | named parameters | boolean |
|
||||||
|
| double | keyword | named parameters | boolean |
|
||||||
|
| double | long | named parameters | boolean |
|
||||||
|
| integer | double | named parameters | boolean |
|
||||||
|
| integer | integer | named parameters | boolean |
|
||||||
|
| integer | keyword | named parameters | boolean |
|
||||||
|
| integer | long | named parameters | boolean |
|
||||||
|
| ip | ip | named parameters | boolean |
|
||||||
|
| ip | keyword | named parameters | boolean |
|
||||||
|
| keyword | keyword | named parameters | boolean |
|
||||||
|
| long | double | named parameters | boolean |
|
||||||
|
| long | integer | named parameters | boolean |
|
||||||
|
| long | keyword | named parameters | boolean |
|
||||||
|
| long | long | named parameters | boolean |
|
||||||
|
| text | keyword | named parameters | boolean |
|
||||||
|
| unsigned_long | double | named parameters | boolean |
|
||||||
|
| unsigned_long | integer | named parameters | boolean |
|
||||||
|
| unsigned_long | keyword | named parameters | boolean |
|
||||||
|
| unsigned_long | long | named parameters | boolean |
|
||||||
|
| unsigned_long | unsigned_long | named parameters | boolean |
|
||||||
|
| version | keyword | named parameters | boolean |
|
||||||
|
| version | version | named parameters | boolean |
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="716" height="76" viewbox="0 0 716 76"><defs><style type="text/css">.c{fill:none;stroke:#222222;}.k{fill:#000000;font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-size:20px;}.s{fill:#e4f4ff;stroke:#222222;}.syn{fill:#8D8D8D;font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-size:20px;}</style></defs><path class="c" d="M0 46h5m152 0h10m32 0h10m80 0h30m-5 0q-5 0-5-5v-26q0-5 5-5h144q5 0 5 5v26q0 5-5 5m-107 0h10m92 0h30m32 0h30m104 0h20m-139 0q5 0 5 5v10q0 5 5 5h114q5 0 5-5v-10q0-5 5-5m5 0h10m32 0h5"/><rect class="s" x="5" y="20" width="152" height="36"/><text class="k" x="15" y="46">MULTI_MATCH</text><rect class="s" x="167" y="20" width="32" height="36" rx="7"/><text class="syn" x="177" y="46">(</text><rect class="s" x="209" y="20" width="80" height="36" rx="7"/><text class="k" x="219" y="46">query</text><rect class="s" x="319" y="20" width="32" height="36" rx="7"/><text class="syn" x="329" y="46">,</text><rect class="s" x="361" y="20" width="92" height="36" rx="7"/><text class="k" x="371" y="46">fields</text><rect class="s" x="483" y="20" width="32" height="36" rx="7"/><text class="syn" x="493" y="46">,</text><rect class="s" x="545" y="20" width="104" height="36" rx="7"/><text class="k" x="555" y="46">options</text><rect class="s" x="679" y="20" width="32" height="36" rx="7"/><text class="syn" x="689" y="46">)</text></svg>
|
After Width: | Height: | Size: 1.5 KiB |
739
docs/reference/query-languages/esql/kibana/definition/functions/multi_match.json
generated
Normal file
739
docs/reference/query-languages/esql/kibana/definition/functions/multi_match.json
generated
Normal file
|
@ -0,0 +1,739 @@
|
||||||
|
{
|
||||||
|
"comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
|
||||||
|
"type" : "scalar",
|
||||||
|
"name" : "multi_match",
|
||||||
|
"description" : "Use `MULTI_MATCH` to perform a multi-match query on the specified field.\nThe multi_match query builds on the match query to allow multi-field queries.",
|
||||||
|
"signatures" : [
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "boolean",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "boolean",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "boolean",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "date",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "date",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "date",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "date_nanos",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "date_nanos",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "date_nanos",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "double",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "double",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "double",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "integer",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "double",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "double",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "integer",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "double",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "integer",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "integer",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "integer",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "integer",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "ip",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "ip",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "ip",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "double",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "integer",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "text",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "unsigned_long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "double",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "unsigned_long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "integer",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "unsigned_long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "unsigned_long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "unsigned_long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "unsigned_long",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "version",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "keyword",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params" : [
|
||||||
|
{
|
||||||
|
"name" : "query",
|
||||||
|
"type" : "version",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Value to find in the provided fields."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "fields",
|
||||||
|
"type" : "version",
|
||||||
|
"optional" : false,
|
||||||
|
"description" : "Fields to use for matching"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "options",
|
||||||
|
"type" : "function_named_parameters",
|
||||||
|
"mapParams" : "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). Defaults to true.'}, {name='tie_breaker', values=[0], description='Controls how score is blended together between field groups. Defaults to 0 (best score from each group).'}, {name='type', values=['best_fields'], description='Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, `cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. See <<multi-match-types,multi_match types>>.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned. Defaults to true.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value. Defaults to OR.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand. Defaults to 50.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer mapped for the field. If no analyzer is mapped, the index’s default analyzer is used.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information. If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of top_terms_blended_freqs_${max_expansions} by default.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching. Defaults to 0.'}",
|
||||||
|
"optional" : true,
|
||||||
|
"description" : "(Optional) Additional options for MultiMatch, passed as <<esql-function-named-params,function named parameters>>.\" See <<query-dsl-multi-match-query,multi-match query>> for more information."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variadic" : true,
|
||||||
|
"returnType" : "boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"examples" : [
|
||||||
|
"FROM books\n| WHERE MULTI_MATCH(\"Faulkner\", author, description, {\"fuzziness\": 1})\n| KEEP book_no, author\n| SORT book_no\n| LIMIT 5",
|
||||||
|
"FROM books\n| WHERE MULTI_MATCH(\"Hobbit Back Again\", title, description, {\"operator\": \"AND\"})\n| KEEP title;"
|
||||||
|
],
|
||||||
|
"preview" : true,
|
||||||
|
"snapshot_only" : false
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!--
|
||||||
|
This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### MULTI_MATCH
|
||||||
|
Use `MULTI_MATCH` to perform a [multi-match query](https://www.elastic.co/docs/reference/elasticsearch/query-languages/query-dsl/query-dsl-match-query#query-dsl-multi-match-query) on the specified field.
|
||||||
|
The multi_match query builds on the match query to allow multi-field queries.
|
||||||
|
|
||||||
|
```esql
|
||||||
|
FROM books
|
||||||
|
| WHERE MULTI_MATCH("Faulkner", author, description, {"fuzziness": 1})
|
||||||
|
| KEEP book_no, author
|
||||||
|
| SORT book_no
|
||||||
|
| LIMIT 5
|
||||||
|
```
|
|
@ -53,21 +53,23 @@ public final class MultiMatchQueryBuilder extends AbstractQueryBuilder<MultiMatc
|
||||||
public static final ZeroTermsQueryOption DEFAULT_ZERO_TERMS_QUERY = MatchQueryParser.DEFAULT_ZERO_TERMS_QUERY;
|
public static final ZeroTermsQueryOption DEFAULT_ZERO_TERMS_QUERY = MatchQueryParser.DEFAULT_ZERO_TERMS_QUERY;
|
||||||
public static final boolean DEFAULT_FUZZY_TRANSPOSITIONS = FuzzyQuery.defaultTranspositions;
|
public static final boolean DEFAULT_FUZZY_TRANSPOSITIONS = FuzzyQuery.defaultTranspositions;
|
||||||
|
|
||||||
private static final ParseField SLOP_FIELD = new ParseField("slop");
|
public static final ParseField BOOST_FIELD = new ParseField("boost");
|
||||||
private static final ParseField ZERO_TERMS_QUERY_FIELD = new ParseField("zero_terms_query");
|
public static final ParseField SLOP_FIELD = new ParseField("slop");
|
||||||
private static final ParseField LENIENT_FIELD = new ParseField("lenient");
|
public static final ParseField ZERO_TERMS_QUERY_FIELD = new ParseField("zero_terms_query");
|
||||||
private static final ParseField TIE_BREAKER_FIELD = new ParseField("tie_breaker");
|
public static final ParseField LENIENT_FIELD = new ParseField("lenient");
|
||||||
private static final ParseField FUZZY_REWRITE_FIELD = new ParseField("fuzzy_rewrite");
|
public static final ParseField TIE_BREAKER_FIELD = new ParseField("tie_breaker");
|
||||||
private static final ParseField MINIMUM_SHOULD_MATCH_FIELD = new ParseField("minimum_should_match");
|
public static final ParseField FUZZINESS_FIELD = new ParseField("fuzziness");
|
||||||
private static final ParseField OPERATOR_FIELD = new ParseField("operator");
|
public static final ParseField FUZZY_REWRITE_FIELD = new ParseField("fuzzy_rewrite");
|
||||||
private static final ParseField MAX_EXPANSIONS_FIELD = new ParseField("max_expansions");
|
public static final ParseField MINIMUM_SHOULD_MATCH_FIELD = new ParseField("minimum_should_match");
|
||||||
private static final ParseField PREFIX_LENGTH_FIELD = new ParseField("prefix_length");
|
public static final ParseField OPERATOR_FIELD = new ParseField("operator");
|
||||||
private static final ParseField ANALYZER_FIELD = new ParseField("analyzer");
|
public static final ParseField MAX_EXPANSIONS_FIELD = new ParseField("max_expansions");
|
||||||
private static final ParseField TYPE_FIELD = new ParseField("type");
|
public static final ParseField PREFIX_LENGTH_FIELD = new ParseField("prefix_length");
|
||||||
private static final ParseField QUERY_FIELD = new ParseField("query");
|
public static final ParseField ANALYZER_FIELD = new ParseField("analyzer");
|
||||||
private static final ParseField FIELDS_FIELD = new ParseField("fields");
|
public static final ParseField TYPE_FIELD = new ParseField("type");
|
||||||
private static final ParseField GENERATE_SYNONYMS_PHRASE_QUERY = new ParseField("auto_generate_synonyms_phrase_query");
|
public static final ParseField QUERY_FIELD = new ParseField("query");
|
||||||
private static final ParseField FUZZY_TRANSPOSITIONS_FIELD = new ParseField("fuzzy_transpositions");
|
public static final ParseField FIELDS_FIELD = new ParseField("fields");
|
||||||
|
public static final ParseField GENERATE_SYNONYMS_PHRASE_QUERY = new ParseField("auto_generate_synonyms_phrase_query");
|
||||||
|
public static final ParseField FUZZY_TRANSPOSITIONS_FIELD = new ParseField("fuzzy_transpositions");
|
||||||
|
|
||||||
private final Object value;
|
private final Object value;
|
||||||
private final Map<String, Float> fieldsBoosts;
|
private final Map<String, Float> fieldsBoosts;
|
||||||
|
|
|
@ -54,6 +54,7 @@ public abstract class LuceneQueryEvaluator<T extends Vector.Builder> implements
|
||||||
private final List<ShardState> perShardState;
|
private final List<ShardState> perShardState;
|
||||||
|
|
||||||
protected LuceneQueryEvaluator(BlockFactory blockFactory, ShardConfig[] shards) {
|
protected LuceneQueryEvaluator(BlockFactory blockFactory, ShardConfig[] shards) {
|
||||||
|
assert shards != null && shards.length > 0 : "LuceneQueryEvaluator requires shard information";
|
||||||
this.blockFactory = blockFactory;
|
this.blockFactory = blockFactory;
|
||||||
this.shards = shards;
|
this.shards = shards;
|
||||||
this.perShardState = new ArrayList<>(Collections.nCopies(shards.length, null));
|
this.perShardState = new ArrayList<>(Collections.nCopies(shards.length, null));
|
||||||
|
|
|
@ -296,6 +296,7 @@ public final class CsvAssert {
|
||||||
width[c] = header(headers.get(c), types.get(c)).length();
|
width[c] = header(headers.get(c), types.get(c)).length();
|
||||||
}
|
}
|
||||||
for (int r = 0; r < rows; r++) {
|
for (int r = 0; r < rows; r++) {
|
||||||
|
assertThat("Mismatched header size and values", headers.size() == values.get(r).size());
|
||||||
for (int c = 0; c < headers.size(); c++) {
|
for (int c = 0; c < headers.size(); c++) {
|
||||||
printableValues[r][c] = String.valueOf(valueTransformer.apply(types.get(c), values.get(r).get(c)));
|
printableValues[r][c] = String.valueOf(valueTransformer.apply(types.get(c), values.get(r).get(c)));
|
||||||
width[c] = Math.max(width[c], printableValues[r][c].length());
|
width[c] = Math.max(width[c], printableValues[r][c].length());
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
###############################################
|
||||||
|
# Tests for MultiMatch function
|
||||||
|
#
|
||||||
|
|
||||||
|
multiMatchWithField
|
||||||
|
required_capability: multi_match_function
|
||||||
|
|
||||||
|
// tag::multi-match-with-field[]
|
||||||
|
FROM books
|
||||||
|
| WHERE MULTI_MATCH("Faulkner", author, description)
|
||||||
|
| KEEP book_no, author
|
||||||
|
| SORT book_no
|
||||||
|
| LIMIT 5
|
||||||
|
// end::multi-match-with-field[]
|
||||||
|
;
|
||||||
|
|
||||||
|
// tag::multi-match-with-field-result[]
|
||||||
|
book_no:keyword | author:text
|
||||||
|
2378 | [Carol Faulkner, Holly Byers Ochoa, Lucretia Mott]
|
||||||
|
2713 | William Faulkner
|
||||||
|
2847 | Colleen Faulkner
|
||||||
|
2883 | William Faulkner
|
||||||
|
3293 | Danny Faulkner
|
||||||
|
// end::multi-match-with-field-result[]
|
||||||
|
;
|
||||||
|
|
||||||
|
testMultiMatchWithOptionsFuzziness
|
||||||
|
required_capability: multi_match_function
|
||||||
|
|
||||||
|
from books
|
||||||
|
| where multi_match("Pings", title, description, {"fuzziness": 1})
|
||||||
|
| keep book_no;
|
||||||
|
ignoreOrder:true
|
||||||
|
|
||||||
|
book_no:keyword
|
||||||
|
1463
|
||||||
|
2675
|
||||||
|
2714
|
||||||
|
2936
|
||||||
|
4023
|
||||||
|
4917
|
||||||
|
5335
|
||||||
|
7140
|
||||||
|
7350
|
||||||
|
8875
|
||||||
|
;
|
||||||
|
|
||||||
|
testMultiMatchWithOptionsOperator
|
||||||
|
required_capability: multi_match_function
|
||||||
|
|
||||||
|
// tag::multi-match-with-named-function-params[]
|
||||||
|
FROM books
|
||||||
|
| WHERE MULTI_MATCH("Hobbit Back Again", title, description, {"operator": "AND"})
|
||||||
|
| KEEP title;
|
||||||
|
// end::multi-match-with-named-function-params[]
|
||||||
|
|
||||||
|
// tag::multi-match-with-named-function-params-result[]
|
||||||
|
title:text
|
||||||
|
The Hobbit or There and Back Again
|
||||||
|
// end::multi-match-with-named-function-params-result[]
|
||||||
|
;
|
||||||
|
|
||||||
|
testMultiMatchWithOptionsMinimumShouldMatch
|
||||||
|
required_capability: multi_match_function
|
||||||
|
|
||||||
|
from books
|
||||||
|
| where multi_match("here back again", title, description, {"minimum_should_match": 2, "operator": "OR"})
|
||||||
|
| sort book_no
|
||||||
|
| keep title;
|
||||||
|
|
||||||
|
title:text
|
||||||
|
My First 100 Words in Spanish/English (My First 100 Words Pull-Tab Book)
|
||||||
|
The Hobbit or There and Back Again
|
||||||
|
;
|
||||||
|
|
||||||
|
testMultiMatchWithNonPushableDisjunctions
|
||||||
|
required_capability: multi_match_function
|
||||||
|
required_capability: full_text_functions_disjunctions_compute_engine
|
||||||
|
|
||||||
|
from books
|
||||||
|
| where multi_match("lord", title, description) or length(title) > 130
|
||||||
|
| keep book_no
|
||||||
|
;
|
||||||
|
ignoreOrder: true
|
||||||
|
|
||||||
|
book_no:keyword
|
||||||
|
1463
|
||||||
|
2675
|
||||||
|
2714
|
||||||
|
2936
|
||||||
|
4023
|
||||||
|
5335
|
||||||
|
7140
|
||||||
|
7350
|
||||||
|
8678
|
||||||
|
8875
|
||||||
|
;
|
||||||
|
|
||||||
|
testMultiMatchPhraseQuery
|
||||||
|
required_capability: multi_match_function
|
||||||
|
|
||||||
|
from books
|
||||||
|
| where multi_match("Lord of the rings", title, description, { "type": "phrase" } )
|
||||||
|
| keep title
|
||||||
|
;
|
||||||
|
ignoreOrder: true
|
||||||
|
|
||||||
|
title:text
|
||||||
|
A Tolkien Compass: Including J. R. R. Tolkien's Guide to the Names in The Lord of the Rings
|
||||||
|
Return of the Shadow
|
||||||
|
The Lord of the Rings Poster Collection: Six Paintings by Alan Lee (No. 1)
|
||||||
|
Letters of J R R Tolkien
|
||||||
|
The Lord of the Rings - Boxed Set
|
||||||
|
The Two Towers
|
||||||
|
Realms of Tolkien: Images of Middle-earth
|
||||||
|
Return of the King Being the Third Part of The Lord of the Rings
|
||||||
|
;
|
||||||
|
|
||||||
|
testMultiMatchPhrasePrefixQuery
|
||||||
|
required_capability: multi_match_function
|
||||||
|
|
||||||
|
from books
|
||||||
|
| where multi_match("Lord of the ri", title, { "type": "phrase_prefix" } )
|
||||||
|
| keep title
|
||||||
|
;
|
||||||
|
ignoreOrder: true
|
||||||
|
|
||||||
|
title:text
|
||||||
|
The Lord of the Rings - Boxed Set
|
||||||
|
Return of the King Being the Third Part of The Lord of the Rings
|
||||||
|
A Tolkien Compass: Including J. R. R. Tolkien's Guide to the Names in The Lord of the Rings
|
||||||
|
The Lord of the Rings Poster Collection: Six Paintings by Alan Lee (No. 1)
|
||||||
|
;
|
|
@ -114,6 +114,73 @@ book_no:keyword | title:text
|
||||||
6151 | Pop! Went Another Balloon: A Magical Counting Storybook (Magical Counting Storybooks) | [Keith Faulkner, Rory Tyger] | 5.975414276123047
|
6151 | Pop! Went Another Balloon: A Magical Counting Storybook (Magical Counting Storybooks) | [Keith Faulkner, Rory Tyger] | 5.975414276123047
|
||||||
;
|
;
|
||||||
|
|
||||||
|
testMultiMatchWithScore
|
||||||
|
required_capability: multi_match_function
|
||||||
|
required_capability: metadata_score
|
||||||
|
|
||||||
|
from books metadata _score
|
||||||
|
| where multi_match("Mark", author, title, {"fuzziness": 1})
|
||||||
|
| keep book_no, title, author, _score;
|
||||||
|
ignoreOrder:true
|
||||||
|
|
||||||
|
book_no:keyword | title:text | author:text | _score:double
|
||||||
|
2847 | To Love A Dark Stranger (Lovegram Historical Romance) | Colleen Faulkner | 1.9662091732025146
|
||||||
|
;
|
||||||
|
|
||||||
|
testMultiMatchWithScore1
|
||||||
|
required_capability: multi_match_function
|
||||||
|
required_capability: metadata_score
|
||||||
|
|
||||||
|
from books metadata _score
|
||||||
|
| where multi_match("Hobbit", description, title, {"type": "best_fields"})
|
||||||
|
| sort book_no
|
||||||
|
| eval _score = round(_score)
|
||||||
|
| keep book_no, _score;
|
||||||
|
ignoreOrder:true
|
||||||
|
|
||||||
|
book_no:keyword | _score:double
|
||||||
|
1463 | 2.0
|
||||||
|
2301 | 2.0
|
||||||
|
2675 | 2.0
|
||||||
|
2714 | 2.0
|
||||||
|
2936 | 1.0
|
||||||
|
4023 | 2.0
|
||||||
|
4289 | 3.0
|
||||||
|
5335 | 2.0
|
||||||
|
5996 | 2.0
|
||||||
|
6405 | 2.0
|
||||||
|
6760 | 2.0
|
||||||
|
7350 | 2.0
|
||||||
|
7480 | 3.0
|
||||||
|
;
|
||||||
|
|
||||||
|
testMultiMatchWithScore2
|
||||||
|
required_capability: multi_match_function
|
||||||
|
required_capability: metadata_score
|
||||||
|
|
||||||
|
from books metadata _score
|
||||||
|
| where multi_match("Hobbit", description, title, {"type": "most_fields"})
|
||||||
|
| sort book_no
|
||||||
|
| eval _score = round(_score)
|
||||||
|
| keep book_no, _score;
|
||||||
|
ignoreOrder:true
|
||||||
|
|
||||||
|
book_no:keyword | _score:double
|
||||||
|
1463 | 2.0
|
||||||
|
2301 | 2.0
|
||||||
|
2675 | 2.0
|
||||||
|
2714 | 2.0
|
||||||
|
2936 | 1.0
|
||||||
|
4023 | 2.0
|
||||||
|
4289 | 6.0
|
||||||
|
5335 | 2.0
|
||||||
|
5996 | 2.0
|
||||||
|
6405 | 2.0
|
||||||
|
6760 | 2.0
|
||||||
|
7350 | 2.0
|
||||||
|
7480 | 3.0
|
||||||
|
;
|
||||||
|
|
||||||
multipleWhereWithMatchScoringNoSort
|
multipleWhereWithMatchScoringNoSort
|
||||||
required_capability: metadata_score
|
required_capability: metadata_score
|
||||||
required_capability: match_operator_colon
|
required_capability: match_operator_colon
|
||||||
|
|
|
@ -46,6 +46,7 @@ public class ScoringIT extends AbstractEsqlIntegTestCase {
|
||||||
public static List<Object[]> params() {
|
public static List<Object[]> params() {
|
||||||
List<Object[]> params = new ArrayList<>();
|
List<Object[]> params = new ArrayList<>();
|
||||||
params.add(new Object[] { "match(content, \"fox\")" });
|
params.add(new Object[] { "match(content, \"fox\")" });
|
||||||
|
params.add(new Object[] { "multi_match(\"fox\", content, {\"operator\": \"AND\"})" });
|
||||||
params.add(new Object[] { "content:\"fox\"" });
|
params.add(new Object[] { "content:\"fox\"" });
|
||||||
params.add(new Object[] { "qstr(\"content: fox\")" });
|
params.add(new Object[] { "qstr(\"content: fox\")" });
|
||||||
params.add(new Object[] { "kql(\"content*: fox\")" });
|
params.add(new Object[] { "kql(\"content*: fox\")" });
|
||||||
|
|
|
@ -904,6 +904,11 @@ public class EsqlCapabilities {
|
||||||
*/
|
*/
|
||||||
FULL_TEXT_FUNCTIONS_DISJUNCTIONS_SCORE,
|
FULL_TEXT_FUNCTIONS_DISJUNCTIONS_SCORE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for multi-match function.
|
||||||
|
*/
|
||||||
|
MULTI_MATCH_FUNCTION(Build.current().isSnapshot()),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do {@code TO_LOWER} and {@code TO_UPPER} process all field values?
|
* Do {@code TO_LOWER} and {@code TO_UPPER} process all field values?
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.common.util.CollectionUtils;
|
||||||
import org.elasticsearch.common.util.FeatureFlag;
|
import org.elasticsearch.common.util.FeatureFlag;
|
||||||
import org.elasticsearch.xpack.esql.core.QlIllegalArgumentException;
|
import org.elasticsearch.xpack.esql.core.QlIllegalArgumentException;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
||||||
|
import org.elasticsearch.xpack.esql.core.expression.MapExpression;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.function.Function;
|
import org.elasticsearch.xpack.esql.core.expression.function.Function;
|
||||||
import org.elasticsearch.xpack.esql.core.tree.Source;
|
import org.elasticsearch.xpack.esql.core.tree.Source;
|
||||||
import org.elasticsearch.xpack.esql.core.type.DataType;
|
import org.elasticsearch.xpack.esql.core.type.DataType;
|
||||||
|
@ -37,6 +38,7 @@ import org.elasticsearch.xpack.esql.expression.function.aggregate.Values;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.aggregate.WeightedAvg;
|
import org.elasticsearch.xpack.esql.expression.function.aggregate.WeightedAvg;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.fulltext.Kql;
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.Kql;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.MultiMatch;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.fulltext.QueryString;
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.QueryString;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.fulltext.Term;
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.Term;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.grouping.Bucket;
|
import org.elasticsearch.xpack.esql.expression.function.grouping.Bucket;
|
||||||
|
@ -428,6 +430,7 @@ public class EsqlFunctionRegistry {
|
||||||
new FunctionDefinition[] {
|
new FunctionDefinition[] {
|
||||||
def(Kql.class, uni(Kql::new), "kql"),
|
def(Kql.class, uni(Kql::new), "kql"),
|
||||||
def(Match.class, tri(Match::new), "match"),
|
def(Match.class, tri(Match::new), "match"),
|
||||||
|
def(MultiMatch.class, MultiMatch::new, "multi_match"),
|
||||||
def(QueryString.class, bi(QueryString::new), "qstr") } };
|
def(QueryString.class, bi(QueryString::new), "qstr") } };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1025,6 +1028,32 @@ public class EsqlFunctionRegistry {
|
||||||
T build(Source source, Expression exp, List<Expression> variadic);
|
T build(Source source, Expression exp, List<Expression> variadic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected interface BinaryVariadicWithOptionsBuilder<T> {
|
||||||
|
T build(Source source, Expression exp, List<Expression> variadic, Expression options);
|
||||||
|
};
|
||||||
|
|
||||||
|
protected static <T extends Function> FunctionDefinition def(
|
||||||
|
Class<T> function,
|
||||||
|
BinaryVariadicWithOptionsBuilder<T> ctorRef,
|
||||||
|
String... names
|
||||||
|
) {
|
||||||
|
FunctionBuilder builder = (source, children, cfg) -> {
|
||||||
|
boolean hasMinimumOne = OptionalArgument.class.isAssignableFrom(function);
|
||||||
|
if (hasMinimumOne && children.size() < 1) {
|
||||||
|
throw new QlIllegalArgumentException("expects at least one argument");
|
||||||
|
} else if (hasMinimumOne == false && children.size() < 2) {
|
||||||
|
throw new QlIllegalArgumentException("expects at least two arguments");
|
||||||
|
}
|
||||||
|
Expression options = children.getLast();
|
||||||
|
if (options instanceof MapExpression) {
|
||||||
|
return ctorRef.build(source, children.get(0), children.subList(1, children.size() - 1), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctorRef.build(source, children.get(0), children.subList(1, children.size()), null);
|
||||||
|
};
|
||||||
|
return def(function, builder, names);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a {@linkplain FunctionDefinition} for a no-argument function that is configuration aware.
|
* Build a {@linkplain FunctionDefinition} for a no-argument function that is configuration aware.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.xpack.esql.expression.function.fulltext;
|
package org.elasticsearch.xpack.esql.expression.function.fulltext;
|
||||||
|
|
||||||
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.common.lucene.BytesRefs;
|
import org.elasticsearch.common.lucene.BytesRefs;
|
||||||
import org.elasticsearch.compute.lucene.LuceneQueryEvaluator.ShardConfig;
|
import org.elasticsearch.compute.lucene.LuceneQueryEvaluator.ShardConfig;
|
||||||
import org.elasticsearch.compute.lucene.LuceneQueryExpressionEvaluator;
|
import org.elasticsearch.compute.lucene.LuceneQueryExpressionEvaluator;
|
||||||
|
@ -17,14 +18,19 @@ import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.xpack.esql.capabilities.PostAnalysisPlanVerificationAware;
|
import org.elasticsearch.xpack.esql.capabilities.PostAnalysisPlanVerificationAware;
|
||||||
import org.elasticsearch.xpack.esql.capabilities.TranslationAware;
|
import org.elasticsearch.xpack.esql.capabilities.TranslationAware;
|
||||||
import org.elasticsearch.xpack.esql.common.Failures;
|
import org.elasticsearch.xpack.esql.common.Failures;
|
||||||
|
import org.elasticsearch.xpack.esql.core.InvalidArgumentException;
|
||||||
|
import org.elasticsearch.xpack.esql.core.expression.EntryExpression;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.FoldContext;
|
import org.elasticsearch.xpack.esql.core.expression.FoldContext;
|
||||||
|
import org.elasticsearch.xpack.esql.core.expression.Literal;
|
||||||
|
import org.elasticsearch.xpack.esql.core.expression.MapExpression;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Nullability;
|
import org.elasticsearch.xpack.esql.core.expression.Nullability;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.TypeResolutions;
|
import org.elasticsearch.xpack.esql.core.expression.TypeResolutions;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.function.Function;
|
import org.elasticsearch.xpack.esql.core.expression.function.Function;
|
||||||
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
|
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
|
||||||
import org.elasticsearch.xpack.esql.core.tree.Source;
|
import org.elasticsearch.xpack.esql.core.tree.Source;
|
||||||
import org.elasticsearch.xpack.esql.core.type.DataType;
|
import org.elasticsearch.xpack.esql.core.type.DataType;
|
||||||
|
import org.elasticsearch.xpack.esql.core.type.DataTypeConverter;
|
||||||
import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
|
import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
|
||||||
import org.elasticsearch.xpack.esql.expression.predicate.logical.BinaryLogic;
|
import org.elasticsearch.xpack.esql.expression.predicate.logical.BinaryLogic;
|
||||||
import org.elasticsearch.xpack.esql.expression.predicate.logical.Not;
|
import org.elasticsearch.xpack.esql.expression.predicate.logical.Not;
|
||||||
|
@ -42,12 +48,15 @@ import org.elasticsearch.xpack.esql.score.ExpressionScoreMapper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
|
||||||
import static org.elasticsearch.xpack.esql.common.Failure.fail;
|
import static org.elasticsearch.xpack.esql.common.Failure.fail;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isFoldable;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNullAndFoldable;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNullAndFoldable;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString;
|
||||||
|
|
||||||
|
@ -200,6 +209,14 @@ public abstract class FullTextFunction extends Function
|
||||||
m -> "[" + m.functionName() + "] " + m.functionType(),
|
m -> "[" + m.functionName() + "] " + m.functionType(),
|
||||||
failures
|
failures
|
||||||
);
|
);
|
||||||
|
checkCommandsBeforeExpression(
|
||||||
|
plan,
|
||||||
|
condition,
|
||||||
|
MultiMatch.class,
|
||||||
|
lp -> (lp instanceof Limit == false) && (lp instanceof Aggregate == false),
|
||||||
|
m -> "[" + m.functionName() + "] " + m.functionType(),
|
||||||
|
failures
|
||||||
|
);
|
||||||
checkCommandsBeforeExpression(
|
checkCommandsBeforeExpression(
|
||||||
plan,
|
plan,
|
||||||
condition,
|
condition,
|
||||||
|
@ -315,4 +332,39 @@ public abstract class FullTextFunction extends Function
|
||||||
}
|
}
|
||||||
return new LuceneQueryScoreEvaluator.Factory(shardConfigs);
|
return new LuceneQueryScoreEvaluator.Factory(shardConfigs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static void populateOptionsMap(
|
||||||
|
final MapExpression options,
|
||||||
|
final Map<String, Object> optionsMap,
|
||||||
|
final TypeResolutions.ParamOrdinal paramOrdinal,
|
||||||
|
final String sourceText,
|
||||||
|
final Map<String, DataType> allowedOptions
|
||||||
|
) throws InvalidArgumentException {
|
||||||
|
for (EntryExpression entry : options.entryExpressions()) {
|
||||||
|
Expression optionExpr = entry.key();
|
||||||
|
Expression valueExpr = entry.value();
|
||||||
|
TypeResolution resolution = isFoldable(optionExpr, sourceText, paramOrdinal).and(
|
||||||
|
isFoldable(valueExpr, sourceText, paramOrdinal)
|
||||||
|
);
|
||||||
|
if (resolution.unresolved()) {
|
||||||
|
throw new InvalidArgumentException(resolution.message());
|
||||||
|
}
|
||||||
|
Object optionExprLiteral = ((Literal) optionExpr).value();
|
||||||
|
Object valueExprLiteral = ((Literal) valueExpr).value();
|
||||||
|
String optionName = optionExprLiteral instanceof BytesRef br ? br.utf8ToString() : optionExprLiteral.toString();
|
||||||
|
String optionValue = valueExprLiteral instanceof BytesRef br ? br.utf8ToString() : valueExprLiteral.toString();
|
||||||
|
// validate the optionExpr is supported
|
||||||
|
DataType dataType = allowedOptions.get(optionName);
|
||||||
|
if (dataType == null) {
|
||||||
|
throw new InvalidArgumentException(
|
||||||
|
format(null, "Invalid option [{}] in [{}], expected one of {}", optionName, sourceText, allowedOptions.keySet())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
optionsMap.put(optionName, DataTypeConverter.convert(optionValue, dataType));
|
||||||
|
} catch (InvalidArgumentException e) {
|
||||||
|
throw new InvalidArgumentException(format(null, "Invalid option [{}] in [{}], {}", optionName, sourceText, e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ package org.elasticsearch.xpack.esql.expression.function.fulltext;
|
||||||
|
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||||
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
|
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
|
||||||
import org.elasticsearch.xpack.esql.expression.predicate.fulltext.MultiMatchQueryPredicate;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -20,9 +19,9 @@ public class FullTextWritables {
|
||||||
public static List<NamedWriteableRegistry.Entry> getNamedWriteables() {
|
public static List<NamedWriteableRegistry.Entry> getNamedWriteables() {
|
||||||
List<NamedWriteableRegistry.Entry> entries = new ArrayList<>();
|
List<NamedWriteableRegistry.Entry> entries = new ArrayList<>();
|
||||||
|
|
||||||
entries.add(MultiMatchQueryPredicate.ENTRY);
|
|
||||||
entries.add(QueryString.ENTRY);
|
entries.add(QueryString.ENTRY);
|
||||||
entries.add(Match.ENTRY);
|
entries.add(Match.ENTRY);
|
||||||
|
entries.add(MultiMatch.ENTRY);
|
||||||
entries.add(Kql.ENTRY);
|
entries.add(Kql.ENTRY);
|
||||||
|
|
||||||
if (EsqlCapabilities.Cap.TERM_FUNCTION.isEnabled()) {
|
if (EsqlCapabilities.Cap.TERM_FUNCTION.isEnabled()) {
|
||||||
|
|
|
@ -18,17 +18,14 @@ import org.elasticsearch.xpack.esql.capabilities.PostAnalysisPlanVerificationAwa
|
||||||
import org.elasticsearch.xpack.esql.common.Failure;
|
import org.elasticsearch.xpack.esql.common.Failure;
|
||||||
import org.elasticsearch.xpack.esql.common.Failures;
|
import org.elasticsearch.xpack.esql.common.Failures;
|
||||||
import org.elasticsearch.xpack.esql.core.InvalidArgumentException;
|
import org.elasticsearch.xpack.esql.core.InvalidArgumentException;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.EntryExpression;
|
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
|
import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.FoldContext;
|
import org.elasticsearch.xpack.esql.core.expression.FoldContext;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Literal;
|
|
||||||
import org.elasticsearch.xpack.esql.core.expression.MapExpression;
|
import org.elasticsearch.xpack.esql.core.expression.MapExpression;
|
||||||
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
|
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
|
||||||
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
|
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
|
||||||
import org.elasticsearch.xpack.esql.core.tree.Source;
|
import org.elasticsearch.xpack.esql.core.tree.Source;
|
||||||
import org.elasticsearch.xpack.esql.core.type.DataType;
|
import org.elasticsearch.xpack.esql.core.type.DataType;
|
||||||
import org.elasticsearch.xpack.esql.core.type.DataTypeConverter;
|
|
||||||
import org.elasticsearch.xpack.esql.core.type.MultiTypeEsField;
|
import org.elasticsearch.xpack.esql.core.type.MultiTypeEsField;
|
||||||
import org.elasticsearch.xpack.esql.core.util.Check;
|
import org.elasticsearch.xpack.esql.core.util.Check;
|
||||||
import org.elasticsearch.xpack.esql.core.util.NumericUtils;
|
import org.elasticsearch.xpack.esql.core.util.NumericUtils;
|
||||||
|
@ -55,7 +52,6 @@ import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
|
|
||||||
import static org.elasticsearch.index.query.AbstractQueryBuilder.BOOST_FIELD;
|
import static org.elasticsearch.index.query.AbstractQueryBuilder.BOOST_FIELD;
|
||||||
import static org.elasticsearch.index.query.MatchQueryBuilder.ANALYZER_FIELD;
|
import static org.elasticsearch.index.query.MatchQueryBuilder.ANALYZER_FIELD;
|
||||||
import static org.elasticsearch.index.query.MatchQueryBuilder.FUZZY_REWRITE_FIELD;
|
import static org.elasticsearch.index.query.MatchQueryBuilder.FUZZY_REWRITE_FIELD;
|
||||||
|
@ -70,7 +66,6 @@ import static org.elasticsearch.index.query.MatchQueryBuilder.ZERO_TERMS_QUERY_F
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.THIRD;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.THIRD;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isFoldable;
|
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isMapExpression;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isMapExpression;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNull;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNull;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNullAndFoldable;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNullAndFoldable;
|
||||||
|
@ -374,7 +369,6 @@ public class Match extends FullTextFunction implements OptionalArgument, PostAna
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> matchQueryOptions() throws InvalidArgumentException {
|
private Map<String, Object> matchQueryOptions() throws InvalidArgumentException {
|
||||||
|
|
||||||
if (options() == null) {
|
if (options() == null) {
|
||||||
return Map.of(LENIENT_FIELD.getPreferredName(), true);
|
return Map.of(LENIENT_FIELD.getPreferredName(), true);
|
||||||
}
|
}
|
||||||
|
@ -383,33 +377,7 @@ public class Match extends FullTextFunction implements OptionalArgument, PostAna
|
||||||
// Match is lenient by default to avoid failing on incompatible types
|
// Match is lenient by default to avoid failing on incompatible types
|
||||||
matchOptions.put(LENIENT_FIELD.getPreferredName(), true);
|
matchOptions.put(LENIENT_FIELD.getPreferredName(), true);
|
||||||
|
|
||||||
for (EntryExpression entry : ((MapExpression) options()).entryExpressions()) {
|
populateOptionsMap((MapExpression) options(), matchOptions, SECOND, sourceText(), ALLOWED_OPTIONS);
|
||||||
Expression optionExpr = entry.key();
|
|
||||||
Expression valueExpr = entry.value();
|
|
||||||
TypeResolution resolution = isFoldable(optionExpr, sourceText(), SECOND).and(isFoldable(valueExpr, sourceText(), SECOND));
|
|
||||||
if (resolution.unresolved()) {
|
|
||||||
throw new InvalidArgumentException(resolution.message());
|
|
||||||
}
|
|
||||||
Object optionExprLiteral = ((Literal) optionExpr).value();
|
|
||||||
Object valueExprLiteral = ((Literal) valueExpr).value();
|
|
||||||
String optionName = optionExprLiteral instanceof BytesRef br ? br.utf8ToString() : optionExprLiteral.toString();
|
|
||||||
String optionValue = valueExprLiteral instanceof BytesRef br ? br.utf8ToString() : valueExprLiteral.toString();
|
|
||||||
// validate the optionExpr is supported
|
|
||||||
DataType dataType = ALLOWED_OPTIONS.get(optionName);
|
|
||||||
if (dataType == null) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
format(null, "Invalid option [{}] in [{}], expected one of {}", optionName, sourceText(), ALLOWED_OPTIONS.keySet())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
matchOptions.put(optionName, DataTypeConverter.convert(optionValue, dataType));
|
|
||||||
} catch (InvalidArgumentException e) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
format(null, "Invalid option [{}] in [{}], {}", optionName, sourceText(), e.getMessage())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchOptions;
|
return matchOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,16 +460,21 @@ public class Match extends FullTextFunction implements OptionalArgument, PostAna
|
||||||
protected Query translate(TranslatorHandler handler) {
|
protected Query translate(TranslatorHandler handler) {
|
||||||
var fieldAttribute = fieldAsFieldAttribute();
|
var fieldAttribute = fieldAsFieldAttribute();
|
||||||
Check.notNull(fieldAttribute, "Match must have a field attribute as the first argument");
|
Check.notNull(fieldAttribute, "Match must have a field attribute as the first argument");
|
||||||
|
String fieldName = getNameFromFieldAttribute(fieldAttribute);
|
||||||
|
// Make query lenient so mixed field types can be queried when a field type is incompatible with the value provided
|
||||||
|
return new MatchQuery(source(), fieldName, queryAsObject(), matchQueryOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getNameFromFieldAttribute(FieldAttribute fieldAttribute) {
|
||||||
String fieldName = fieldAttribute.name();
|
String fieldName = fieldAttribute.name();
|
||||||
if (fieldAttribute.field() instanceof MultiTypeEsField multiTypeEsField) {
|
if (fieldAttribute.field() instanceof MultiTypeEsField multiTypeEsField) {
|
||||||
// If we have multiple field types, we allow the query to be done, but getting the underlying field name
|
// If we have multiple field types, we allow the query to be done, but getting the underlying field name
|
||||||
fieldName = multiTypeEsField.getName();
|
fieldName = multiTypeEsField.getName();
|
||||||
}
|
}
|
||||||
// Make query lenient so mixed field types can be queried when a field type is incompatible with the value provided
|
return fieldName;
|
||||||
return new MatchQuery(source(), fieldName, queryAsObject(), matchQueryOptions());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private FieldAttribute fieldAsFieldAttribute() {
|
public static FieldAttribute fieldAsFieldAttribute(Expression field) {
|
||||||
Expression fieldExpression = field;
|
Expression fieldExpression = field;
|
||||||
// Field may be converted to other data type (field_name :: data_type), so we need to check the original field
|
// Field may be converted to other data type (field_name :: data_type), so we need to check the original field
|
||||||
if (fieldExpression instanceof AbstractConvertFunction convertFunction) {
|
if (fieldExpression instanceof AbstractConvertFunction convertFunction) {
|
||||||
|
@ -510,6 +483,10 @@ public class Match extends FullTextFunction implements OptionalArgument, PostAna
|
||||||
return fieldExpression instanceof FieldAttribute fieldAttribute ? fieldAttribute : null;
|
return fieldExpression instanceof FieldAttribute fieldAttribute ? fieldAttribute : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FieldAttribute fieldAsFieldAttribute() {
|
||||||
|
return fieldAsFieldAttribute(field);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
// Match does not serialize options, as they get included in the query builder. We need to override equals and hashcode to
|
// Match does not serialize options, as they get included in the query builder. We need to override equals and hashcode to
|
||||||
|
|
|
@ -0,0 +1,469 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.xpack.esql.expression.function.fulltext;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
import org.elasticsearch.index.query.MatchQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
|
import org.elasticsearch.xpack.esql.capabilities.PostAnalysisPlanVerificationAware;
|
||||||
|
import org.elasticsearch.xpack.esql.common.Failure;
|
||||||
|
import org.elasticsearch.xpack.esql.common.Failures;
|
||||||
|
import org.elasticsearch.xpack.esql.core.InvalidArgumentException;
|
||||||
|
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
||||||
|
import org.elasticsearch.xpack.esql.core.expression.MapExpression;
|
||||||
|
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
|
||||||
|
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
|
||||||
|
import org.elasticsearch.xpack.esql.core.tree.Source;
|
||||||
|
import org.elasticsearch.xpack.esql.core.type.DataType;
|
||||||
|
import org.elasticsearch.xpack.esql.core.util.Check;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.Example;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.FunctionInfo;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.MapParam;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.OptionalArgument;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.Param;
|
||||||
|
import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput;
|
||||||
|
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
|
||||||
|
import org.elasticsearch.xpack.esql.planner.TranslatorHandler;
|
||||||
|
import org.elasticsearch.xpack.esql.querydsl.query.MultiMatchQuery;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static java.util.Map.entry;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.ANALYZER_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.BOOST_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.FUZZINESS_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.FUZZY_REWRITE_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.FUZZY_TRANSPOSITIONS_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.GENERATE_SYNONYMS_PHRASE_QUERY;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.LENIENT_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.MAX_EXPANSIONS_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.MINIMUM_SHOULD_MATCH_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.OPERATOR_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.PREFIX_LENGTH_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.SLOP_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.TIE_BREAKER_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.TYPE_FIELD;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.THIRD;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isMapExpression;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNull;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNullAndFoldable;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.BOOLEAN;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.DATETIME;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.DATE_NANOS;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.FLOAT;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.IP;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.VERSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full text function that performs a {@link org.elasticsearch.xpack.esql.querydsl.query.MultiMatchQuery} .
|
||||||
|
*/
|
||||||
|
public class MultiMatch extends FullTextFunction implements OptionalArgument, PostAnalysisPlanVerificationAware {
|
||||||
|
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
|
||||||
|
Expression.class,
|
||||||
|
"MultiMatch",
|
||||||
|
MultiMatch::readFrom
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final Set<DataType> QUERY_DATA_TYPES = Set.of(
|
||||||
|
KEYWORD,
|
||||||
|
BOOLEAN,
|
||||||
|
DATETIME,
|
||||||
|
DATE_NANOS,
|
||||||
|
DOUBLE,
|
||||||
|
INTEGER,
|
||||||
|
IP,
|
||||||
|
LONG,
|
||||||
|
UNSIGNED_LONG,
|
||||||
|
VERSION
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final Set<DataType> FIELD_DATA_TYPES = Set.of(
|
||||||
|
KEYWORD,
|
||||||
|
TEXT,
|
||||||
|
BOOLEAN,
|
||||||
|
DATETIME,
|
||||||
|
DATE_NANOS,
|
||||||
|
DOUBLE,
|
||||||
|
INTEGER,
|
||||||
|
IP,
|
||||||
|
LONG,
|
||||||
|
UNSIGNED_LONG,
|
||||||
|
VERSION
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final Map<String, DataType> OPTIONS = Map.ofEntries(
|
||||||
|
entry(BOOST_FIELD.getPreferredName(), FLOAT),
|
||||||
|
entry(SLOP_FIELD.getPreferredName(), INTEGER),
|
||||||
|
// TODO: add "zero_terms_query"
|
||||||
|
entry(ANALYZER_FIELD.getPreferredName(), KEYWORD),
|
||||||
|
entry(GENERATE_SYNONYMS_PHRASE_QUERY.getPreferredName(), BOOLEAN),
|
||||||
|
entry(FUZZINESS_FIELD.getPreferredName(), KEYWORD),
|
||||||
|
entry(FUZZY_REWRITE_FIELD.getPreferredName(), KEYWORD),
|
||||||
|
entry(FUZZY_TRANSPOSITIONS_FIELD.getPreferredName(), BOOLEAN),
|
||||||
|
entry(LENIENT_FIELD.getPreferredName(), BOOLEAN),
|
||||||
|
entry(MAX_EXPANSIONS_FIELD.getPreferredName(), INTEGER),
|
||||||
|
entry(MINIMUM_SHOULD_MATCH_FIELD.getPreferredName(), KEYWORD),
|
||||||
|
entry(OPERATOR_FIELD.getPreferredName(), KEYWORD),
|
||||||
|
entry(PREFIX_LENGTH_FIELD.getPreferredName(), INTEGER),
|
||||||
|
entry(TIE_BREAKER_FIELD.getPreferredName(), FLOAT),
|
||||||
|
entry(TYPE_FIELD.getPreferredName(), KEYWORD)
|
||||||
|
);
|
||||||
|
|
||||||
|
@FunctionInfo(
|
||||||
|
returnType = "boolean",
|
||||||
|
preview = true,
|
||||||
|
description = """
|
||||||
|
Use `MULTI_MATCH` to perform a <<query-dsl-multi-match-query,multi-match query>> on the specified field.
|
||||||
|
The multi_match query builds on the match query to allow multi-field queries.""",
|
||||||
|
examples = {
|
||||||
|
@Example(file = "multi-match-function", tag = "multi-match-with-field"),
|
||||||
|
@Example(file = "multi-match-function", tag = "multi-match-with-named-function-params") },
|
||||||
|
appliesTo = {
|
||||||
|
@FunctionAppliesTo(
|
||||||
|
lifeCycle = FunctionAppliesToLifecycle.COMING,
|
||||||
|
version = "9.1.0",
|
||||||
|
description = "Support for optional named parameters is only available from 9.1.0"
|
||||||
|
) }
|
||||||
|
)
|
||||||
|
public MultiMatch(
|
||||||
|
Source source,
|
||||||
|
@Param(
|
||||||
|
name = "query",
|
||||||
|
type = { "keyword", "boolean", "date", "date_nanos", "double", "integer", "ip", "long", "text", "unsigned_long", "version" },
|
||||||
|
description = "Value to find in the provided fields."
|
||||||
|
) Expression query,
|
||||||
|
@Param(
|
||||||
|
name = "fields",
|
||||||
|
type = { "keyword", "boolean", "date", "date_nanos", "double", "integer", "ip", "long", "unsigned_long", "version" },
|
||||||
|
description = "Fields to use for matching"
|
||||||
|
) List<Expression> fields,
|
||||||
|
@MapParam(
|
||||||
|
name = "options",
|
||||||
|
params = {
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "boost",
|
||||||
|
type = "float",
|
||||||
|
valueHint = { "2.5" },
|
||||||
|
description = "Floating point number used to decrease or increase the relevance scores of the query."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "analyzer",
|
||||||
|
type = "keyword",
|
||||||
|
valueHint = { "standard" },
|
||||||
|
description = "Analyzer used to convert the text in the query value into token. Defaults to the index-time analyzer"
|
||||||
|
+ " mapped for the field. If no analyzer is mapped, the index’s default analyzer is used."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "auto_generate_synonyms_phrase_query",
|
||||||
|
type = "boolean",
|
||||||
|
valueHint = { "true", "false" },
|
||||||
|
description = "If true, match phrase queries are automatically created for multi-term synonyms. Defaults to true."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "fuzziness",
|
||||||
|
type = "keyword",
|
||||||
|
valueHint = { "AUTO", "1", "2" },
|
||||||
|
description = "Maximum edit distance allowed for matching."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "fuzzy_rewrite",
|
||||||
|
type = "keyword",
|
||||||
|
valueHint = {
|
||||||
|
"constant_score_blended",
|
||||||
|
"constant_score",
|
||||||
|
"constant_score_boolean",
|
||||||
|
"top_terms_blended_freqs_N",
|
||||||
|
"top_terms_boost_N",
|
||||||
|
"top_terms_N" },
|
||||||
|
description = "Method used to rewrite the query. See the rewrite parameter for valid values and more information. "
|
||||||
|
+ "If the fuzziness parameter is not 0, the match query uses a fuzzy_rewrite method of "
|
||||||
|
+ "top_terms_blended_freqs_${max_expansions} by default."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "fuzzy_transpositions",
|
||||||
|
type = "boolean",
|
||||||
|
valueHint = { "true", "false" },
|
||||||
|
description = "If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba). "
|
||||||
|
+ "Defaults to true."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "lenient",
|
||||||
|
type = "boolean",
|
||||||
|
valueHint = { "true", "false" },
|
||||||
|
description = "If false, format-based errors, such as providing a text query value for a numeric field, are returned. "
|
||||||
|
+ "Defaults to true."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "max_expansions",
|
||||||
|
type = "integer",
|
||||||
|
valueHint = { "50" },
|
||||||
|
description = "Maximum number of terms to which the query will expand. Defaults to 50."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "minimum_should_match",
|
||||||
|
type = "integer",
|
||||||
|
valueHint = { "2" },
|
||||||
|
description = "Minimum number of clauses that must match for a document to be returned."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "operator",
|
||||||
|
type = "keyword",
|
||||||
|
valueHint = { "AND", "OR" },
|
||||||
|
description = "Boolean logic used to interpret text in the query value. Defaults to OR."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "prefix_length",
|
||||||
|
type = "integer",
|
||||||
|
valueHint = { "1" },
|
||||||
|
description = "Number of beginning characters left unchanged for fuzzy matching. Defaults to 0."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "tie_breaker",
|
||||||
|
type = "float",
|
||||||
|
valueHint = { "0" },
|
||||||
|
description = "Controls how score is blended together between field groups. Defaults to 0 (best score from each group)."
|
||||||
|
),
|
||||||
|
@MapParam.MapParamEntry(
|
||||||
|
name = "type",
|
||||||
|
type = "object",
|
||||||
|
valueHint = { "'best_fields'" },
|
||||||
|
description = "Controls the way multi_match is executed internally. Can be one of `best_fields`, `most_fields`, "
|
||||||
|
+ "`cross_fields`, `phrase`, `phrase_prefix` or `bool_prefix`. Defaults to 'best_fields'. "
|
||||||
|
+ "See <<multi-match-types,multi_match types>>."
|
||||||
|
), },
|
||||||
|
description = "(Optional) Additional options for MultiMatch, "
|
||||||
|
+ "passed as <<esql-function-named-params,function named parameters>>.\"\n"
|
||||||
|
+ " See <<query-dsl-multi-match-query,multi-match query>> for more information.",
|
||||||
|
optional = true
|
||||||
|
) Expression options
|
||||||
|
) {
|
||||||
|
this(source, query, fields, options, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Due to current limitations, the options field may contain a field, in which case treat it as a field, and use "null" for actual
|
||||||
|
// options. We also remember the originally supplied arguments in order to make tests happy.
|
||||||
|
private final transient List<Expression> fields;
|
||||||
|
private final transient List<Expression> fieldsOriginal;
|
||||||
|
private final transient Expression options;
|
||||||
|
private final transient Expression optionsOriginal;
|
||||||
|
|
||||||
|
private static List<Expression> initChildren(Expression query, List<Expression> fields, Expression options) {
|
||||||
|
Stream<Expression> fieldsAndQuery = Stream.concat(Stream.of(query), fields.stream());
|
||||||
|
return (options == null ? fieldsAndQuery : Stream.concat(fieldsAndQuery, Stream.of(options))).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MultiMatch(Source source, Expression query, List<Expression> fields, Expression options, QueryBuilder queryBuilder) {
|
||||||
|
super(source, query, initChildren(query, fields, options), queryBuilder);
|
||||||
|
this.fieldsOriginal = fields;
|
||||||
|
this.optionsOriginal = options;
|
||||||
|
|
||||||
|
if (options == null || options instanceof MapExpression) {
|
||||||
|
this.fields = fields;
|
||||||
|
this.options = options;
|
||||||
|
} else {
|
||||||
|
this.fields = Stream.concat(fields.stream(), Stream.of(options)).toList();
|
||||||
|
this.options = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MultiMatch readFrom(StreamInput in) throws IOException {
|
||||||
|
Source source = Source.readFrom((PlanStreamInput) in);
|
||||||
|
Expression query = in.readNamedWriteable(Expression.class);
|
||||||
|
List<Expression> fields = in.readNamedWriteableCollectionAsList(Expression.class);
|
||||||
|
QueryBuilder queryBuilder = in.readOptionalNamedWriteable(QueryBuilder.class);
|
||||||
|
return new MultiMatch(source, query, fields, null, queryBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
source().writeTo(out);
|
||||||
|
out.writeNamedWriteable(query());
|
||||||
|
out.writeNamedWriteableCollection(fields);
|
||||||
|
out.writeOptionalNamedWriteable(queryBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWriteableName() {
|
||||||
|
return ENTRY.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression replaceChildren(List<Expression> newChildren) {
|
||||||
|
if (newChildren.getLast() instanceof MapExpression || newChildren.size() == children().size()) {
|
||||||
|
// if the last child is a MapExpression, it is the options map
|
||||||
|
return new MultiMatch(
|
||||||
|
source(),
|
||||||
|
newChildren.getFirst(),
|
||||||
|
newChildren.subList(1, newChildren.size() - 1),
|
||||||
|
newChildren.getLast(),
|
||||||
|
queryBuilder()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MultiMatch(source(), newChildren.getFirst(), newChildren.subList(1, newChildren.size()), null, queryBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NodeInfo<? extends Expression> info() {
|
||||||
|
// Specifically create new instance with original arguments.
|
||||||
|
return NodeInfo.create(this, MultiMatch::new, query(), fieldsOriginal, optionsOriginal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Query translate(TranslatorHandler handler) {
|
||||||
|
Map<String, Float> fieldsWithBoost = new HashMap<>();
|
||||||
|
for (Expression field : fields) {
|
||||||
|
var fieldAttribute = Match.fieldAsFieldAttribute(field);
|
||||||
|
Check.notNull(fieldAttribute, "MultiMatch must have field attributes as arguments #2 to #N-1.");
|
||||||
|
String fieldName = Match.getNameFromFieldAttribute(fieldAttribute);
|
||||||
|
fieldsWithBoost.put(fieldName, 1.0f);
|
||||||
|
}
|
||||||
|
return new MultiMatchQuery(source(), Objects.toString(queryAsObject()), fieldsWithBoost, getOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression replaceQueryBuilder(QueryBuilder queryBuilder) {
|
||||||
|
// Specifically create new instance with original arguments.
|
||||||
|
return new MultiMatch(source(), query(), fieldsOriginal, optionsOriginal, queryBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Expression> fields() {
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression options() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> getOptions() throws InvalidArgumentException {
|
||||||
|
Map<String, Object> options = new HashMap<>();
|
||||||
|
options.put(MatchQueryBuilder.LENIENT_FIELD.getPreferredName(), true);
|
||||||
|
if (options() == null) {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
Match.populateOptionsMap((MapExpression) options(), options, THIRD, sourceText(), OPTIONS);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String functionName() {
|
||||||
|
return ENTRY.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeResolution resolveFields() {
|
||||||
|
return fields.stream()
|
||||||
|
.map(
|
||||||
|
(Expression field) -> isNotNull(field, sourceText(), FIRST).and(
|
||||||
|
isType(
|
||||||
|
field,
|
||||||
|
FIELD_DATA_TYPES::contains,
|
||||||
|
sourceText(),
|
||||||
|
SECOND,
|
||||||
|
"keyword, text, boolean, date, date_nanos, double, integer, ip, long, unsigned_long, version"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.reduce(TypeResolution::and)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeResolution resolveOptions() {
|
||||||
|
if (options() != null) {
|
||||||
|
TypeResolution resolution = isNotNull(options(), sourceText(), THIRD);
|
||||||
|
if (resolution.unresolved()) {
|
||||||
|
return resolution;
|
||||||
|
}
|
||||||
|
// MapExpression does not have a DataType associated with it
|
||||||
|
resolution = isMapExpression(options(), sourceText(), THIRD);
|
||||||
|
if (resolution.unresolved()) {
|
||||||
|
return resolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
getOptions();
|
||||||
|
} catch (InvalidArgumentException e) {
|
||||||
|
return new TypeResolution(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TypeResolution.TYPE_RESOLVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeResolution resolveQuery() {
|
||||||
|
return isType(
|
||||||
|
query(),
|
||||||
|
QUERY_DATA_TYPES::contains,
|
||||||
|
sourceText(),
|
||||||
|
FIRST,
|
||||||
|
"keyword, boolean, date, date_nanos, double, integer, ip, long, unsigned_long, version"
|
||||||
|
).and(isNotNullAndFoldable(query(), sourceText(), FIRST));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TypeResolution resolveParams() {
|
||||||
|
return resolveQuery().and(resolveFields()).and(resolveOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
// MultiMatch does not serialize options, as they get included in the query builder. We need to override equals and hashcode to
|
||||||
|
// ignore options when comparing two MultiMatch functions
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
MultiMatch mm = (MultiMatch) o;
|
||||||
|
return Objects.equals(fields(), mm.fields())
|
||||||
|
&& Objects.equals(query(), mm.query())
|
||||||
|
&& Objects.equals(queryBuilder(), mm.queryBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(fields(), query(), queryBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiConsumer<LogicalPlan, Failures> postAnalysisPlanVerification() {
|
||||||
|
return (plan, failures) -> {
|
||||||
|
super.postAnalysisPlanVerification().accept(plan, failures);
|
||||||
|
plan.forEachExpression(MultiMatch.class, mm -> {
|
||||||
|
for (Expression field : fields) {
|
||||||
|
if (Match.fieldAsFieldAttribute(field) == null) {
|
||||||
|
failures.add(
|
||||||
|
Failure.fail(
|
||||||
|
field,
|
||||||
|
"[{}] {} cannot operate on [{}], which is not a field from an index mapping",
|
||||||
|
functionName(),
|
||||||
|
functionType(),
|
||||||
|
field.sourceText()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,23 +7,19 @@
|
||||||
|
|
||||||
package org.elasticsearch.xpack.esql.expression.function.fulltext;
|
package org.elasticsearch.xpack.esql.expression.function.fulltext;
|
||||||
|
|
||||||
import org.apache.lucene.util.BytesRef;
|
|
||||||
import org.elasticsearch.TransportVersions;
|
import org.elasticsearch.TransportVersions;
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||||
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.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.xpack.esql.core.InvalidArgumentException;
|
import org.elasticsearch.xpack.esql.core.InvalidArgumentException;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.EntryExpression;
|
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Literal;
|
|
||||||
import org.elasticsearch.xpack.esql.core.expression.MapExpression;
|
import org.elasticsearch.xpack.esql.core.expression.MapExpression;
|
||||||
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
|
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
|
||||||
import org.elasticsearch.xpack.esql.core.querydsl.query.QueryStringQuery;
|
import org.elasticsearch.xpack.esql.core.querydsl.query.QueryStringQuery;
|
||||||
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
|
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
|
||||||
import org.elasticsearch.xpack.esql.core.tree.Source;
|
import org.elasticsearch.xpack.esql.core.tree.Source;
|
||||||
import org.elasticsearch.xpack.esql.core.type.DataType;
|
import org.elasticsearch.xpack.esql.core.type.DataType;
|
||||||
import org.elasticsearch.xpack.esql.core.type.DataTypeConverter;
|
|
||||||
import org.elasticsearch.xpack.esql.expression.function.Example;
|
import org.elasticsearch.xpack.esql.expression.function.Example;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo;
|
import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle;
|
import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle;
|
||||||
|
@ -42,7 +38,6 @@ import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
|
|
||||||
import static org.elasticsearch.index.query.QueryStringQueryBuilder.ALLOW_LEADING_WILDCARD_FIELD;
|
import static org.elasticsearch.index.query.QueryStringQueryBuilder.ALLOW_LEADING_WILDCARD_FIELD;
|
||||||
import static org.elasticsearch.index.query.QueryStringQueryBuilder.ANALYZER_FIELD;
|
import static org.elasticsearch.index.query.QueryStringQueryBuilder.ANALYZER_FIELD;
|
||||||
import static org.elasticsearch.index.query.QueryStringQueryBuilder.ANALYZE_WILDCARD_FIELD;
|
import static org.elasticsearch.index.query.QueryStringQueryBuilder.ANALYZE_WILDCARD_FIELD;
|
||||||
|
@ -65,7 +60,6 @@ import static org.elasticsearch.index.query.QueryStringQueryBuilder.REWRITE_FIEL
|
||||||
import static org.elasticsearch.index.query.QueryStringQueryBuilder.TIME_ZONE_FIELD;
|
import static org.elasticsearch.index.query.QueryStringQueryBuilder.TIME_ZONE_FIELD;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isFoldable;
|
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isMapExpression;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isMapExpression;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNull;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNull;
|
||||||
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNullAndFoldable;
|
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNullAndFoldable;
|
||||||
|
@ -330,33 +324,7 @@ public class QueryString extends FullTextFunction implements OptionalArgument {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> matchOptions = new HashMap<>();
|
Map<String, Object> matchOptions = new HashMap<>();
|
||||||
for (EntryExpression entry : ((MapExpression) options()).entryExpressions()) {
|
populateOptionsMap((MapExpression) options(), matchOptions, SECOND, sourceText(), ALLOWED_OPTIONS);
|
||||||
Expression optionExpr = entry.key();
|
|
||||||
Expression valueExpr = entry.value();
|
|
||||||
TypeResolution resolution = isFoldable(optionExpr, sourceText(), SECOND).and(isFoldable(valueExpr, sourceText(), SECOND));
|
|
||||||
if (resolution.unresolved()) {
|
|
||||||
throw new InvalidArgumentException(resolution.message());
|
|
||||||
}
|
|
||||||
Object optionExprLiteral = ((Literal) optionExpr).value();
|
|
||||||
Object valueExprLiteral = ((Literal) valueExpr).value();
|
|
||||||
String optionName = optionExprLiteral instanceof BytesRef br ? br.utf8ToString() : optionExprLiteral.toString();
|
|
||||||
String optionValue = valueExprLiteral instanceof BytesRef br ? br.utf8ToString() : valueExprLiteral.toString();
|
|
||||||
// validate the optionExpr is supported
|
|
||||||
DataType dataType = ALLOWED_OPTIONS.get(optionName);
|
|
||||||
if (dataType == null) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
format(null, "Invalid option [{}] in [{}], expected one of {}", optionName, sourceText(), ALLOWED_OPTIONS.keySet())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
matchOptions.put(optionName, DataTypeConverter.convert(optionValue, dataType));
|
|
||||||
} catch (InvalidArgumentException e) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
format(null, "Invalid option [{}] in [{}], {}", optionName, sourceText(), e.getMessage())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchOptions;
|
return matchOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
/*
|
|
||||||
* 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; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
package org.elasticsearch.xpack.esql.expression.predicate.fulltext;
|
|
||||||
|
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
|
||||||
import org.elasticsearch.xpack.esql.capabilities.TranslationAware;
|
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
|
||||||
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
|
|
||||||
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
|
|
||||||
import org.elasticsearch.xpack.esql.core.tree.Source;
|
|
||||||
import org.elasticsearch.xpack.esql.optimizer.rules.physical.local.LucenePushdownPredicates;
|
|
||||||
import org.elasticsearch.xpack.esql.planner.TranslatorHandler;
|
|
||||||
import org.elasticsearch.xpack.esql.querydsl.query.MultiMatchQuery;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
|
|
||||||
public class MultiMatchQueryPredicate extends FullTextPredicate implements TranslationAware {
|
|
||||||
|
|
||||||
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
|
|
||||||
Expression.class,
|
|
||||||
"MultiMatchQueryPredicate",
|
|
||||||
MultiMatchQueryPredicate::new
|
|
||||||
);
|
|
||||||
|
|
||||||
private final String fieldString;
|
|
||||||
private final Map<String, Float> fields;
|
|
||||||
|
|
||||||
public MultiMatchQueryPredicate(Source source, String fieldString, String query, String options) {
|
|
||||||
super(source, query, options, emptyList());
|
|
||||||
this.fieldString = fieldString;
|
|
||||||
// inferred
|
|
||||||
this.fields = FullTextUtils.parseFields(fieldString, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiMatchQueryPredicate(StreamInput in) throws IOException {
|
|
||||||
super(in);
|
|
||||||
assert super.children().isEmpty();
|
|
||||||
fieldString = in.readString();
|
|
||||||
// inferred
|
|
||||||
this.fields = FullTextUtils.parseFields(fieldString, source());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected NodeInfo<MultiMatchQueryPredicate> info() {
|
|
||||||
return NodeInfo.create(this, MultiMatchQueryPredicate::new, fieldString, query(), options());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Expression replaceChildren(List<Expression> newChildren) {
|
|
||||||
throw new UnsupportedOperationException("this type of node doesn't have any children to replace");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String fieldString() {
|
|
||||||
return fieldString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Float> fields() {
|
|
||||||
return fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
|
||||||
super.writeTo(out);
|
|
||||||
out.writeString(fieldString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(fieldString, super.hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (super.equals(obj)) {
|
|
||||||
MultiMatchQueryPredicate other = (MultiMatchQueryPredicate) obj;
|
|
||||||
return Objects.equals(fieldString, other.fieldString);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getWriteableName() {
|
|
||||||
return ENTRY.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean translatable(LucenePushdownPredicates pushdownPredicates) {
|
|
||||||
return true; // needs update if we'll ever validate the fields
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Query asQuery(TranslatorHandler handler) {
|
|
||||||
return new MultiMatchQuery(source(), query(), fields(), this);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,64 +7,74 @@
|
||||||
package org.elasticsearch.xpack.esql.querydsl.query;
|
package org.elasticsearch.xpack.esql.querydsl.query;
|
||||||
|
|
||||||
import org.elasticsearch.common.unit.Fuzziness;
|
import org.elasticsearch.common.unit.Fuzziness;
|
||||||
import org.elasticsearch.core.Booleans;
|
|
||||||
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
|
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
|
||||||
import org.elasticsearch.index.query.Operator;
|
import org.elasticsearch.index.query.Operator;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
|
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
|
||||||
import org.elasticsearch.xpack.esql.core.tree.Source;
|
import org.elasticsearch.xpack.esql.core.tree.Source;
|
||||||
import org.elasticsearch.xpack.esql.expression.predicate.fulltext.MultiMatchQueryPredicate;
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.ANALYZER_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.BOOST_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.FUZZINESS_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.FUZZY_REWRITE_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.FUZZY_TRANSPOSITIONS_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.GENERATE_SYNONYMS_PHRASE_QUERY;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.LENIENT_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.MAX_EXPANSIONS_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.MINIMUM_SHOULD_MATCH_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.OPERATOR_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.PREFIX_LENGTH_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.SLOP_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.TIE_BREAKER_FIELD;
|
||||||
|
import static org.elasticsearch.index.query.MultiMatchQueryBuilder.TYPE_FIELD;
|
||||||
|
|
||||||
public class MultiMatchQuery extends Query {
|
public class MultiMatchQuery extends Query {
|
||||||
|
|
||||||
private static final Map<String, BiConsumer<MultiMatchQueryBuilder, String>> BUILDER_APPLIERS;
|
private static final Map<String, BiConsumer<MultiMatchQueryBuilder, Object>> BUILDER_APPLIERS;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// TODO: it'd be great if these could be constants instead of Strings, needs a core change to make the fields public first
|
|
||||||
BUILDER_APPLIERS = Map.ofEntries(
|
BUILDER_APPLIERS = Map.ofEntries(
|
||||||
entry("slop", (qb, s) -> qb.slop(Integer.valueOf(s))),
|
entry(BOOST_FIELD.getPreferredName(), (qb, obj) -> qb.boost((Float) obj)),
|
||||||
|
entry(SLOP_FIELD.getPreferredName(), (qb, obj) -> qb.slop((Integer) obj)),
|
||||||
// TODO: add zero terms query support, I'm not sure the best way to parse it yet...
|
// TODO: add zero terms query support, I'm not sure the best way to parse it yet...
|
||||||
// appliers.put("zero_terms_query", (qb, s) -> qb.zeroTermsQuery(s));
|
// appliers.put("zero_terms_query", (qb, s) -> qb.zeroTermsQuery(s));
|
||||||
entry("analyzer", MultiMatchQueryBuilder::analyzer),
|
entry(ANALYZER_FIELD.getPreferredName(), (qb, obj) -> qb.analyzer((String) obj)),
|
||||||
entry("auto_generate_synonyms_phrase_query", (qb, s) -> qb.autoGenerateSynonymsPhraseQuery(Booleans.parseBoolean(s))),
|
entry(GENERATE_SYNONYMS_PHRASE_QUERY.getPreferredName(), (qb, obj) -> qb.autoGenerateSynonymsPhraseQuery((Boolean) obj)),
|
||||||
entry("fuzziness", (qb, s) -> qb.fuzziness(Fuzziness.fromString(s))),
|
entry(FUZZINESS_FIELD.getPreferredName(), (qb, obj) -> qb.fuzziness(Fuzziness.fromString((String) obj))),
|
||||||
entry("fuzzy_rewrite", MultiMatchQueryBuilder::fuzzyRewrite),
|
entry(FUZZY_REWRITE_FIELD.getPreferredName(), (qb, obj) -> qb.fuzzyRewrite((String) obj)),
|
||||||
entry("fuzzy_transpositions", (qb, s) -> qb.fuzzyTranspositions(Booleans.parseBoolean(s))),
|
entry(FUZZY_TRANSPOSITIONS_FIELD.getPreferredName(), (qb, obj) -> qb.fuzzyTranspositions((Boolean) obj)),
|
||||||
entry("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s))),
|
entry(LENIENT_FIELD.getPreferredName(), (qb, obj) -> qb.lenient((Boolean) obj)),
|
||||||
entry("max_expansions", (qb, s) -> qb.maxExpansions(Integer.valueOf(s))),
|
entry(MAX_EXPANSIONS_FIELD.getPreferredName(), (qb, obj) -> qb.maxExpansions((Integer) obj)),
|
||||||
entry("minimum_should_match", MultiMatchQueryBuilder::minimumShouldMatch),
|
entry(MINIMUM_SHOULD_MATCH_FIELD.getPreferredName(), (qb, obj) -> qb.minimumShouldMatch((String) obj)),
|
||||||
entry("operator", (qb, s) -> qb.operator(Operator.fromString(s))),
|
entry(OPERATOR_FIELD.getPreferredName(), (qb, obj) -> qb.operator(Operator.fromString((String) obj))),
|
||||||
entry("prefix_length", (qb, s) -> qb.prefixLength(Integer.valueOf(s))),
|
entry(PREFIX_LENGTH_FIELD.getPreferredName(), (qb, obj) -> qb.prefixLength((Integer) obj)),
|
||||||
entry("tie_breaker", (qb, s) -> qb.tieBreaker(Float.valueOf(s))),
|
entry(TIE_BREAKER_FIELD.getPreferredName(), (qb, obj) -> qb.tieBreaker((Float) obj)),
|
||||||
entry("type", MultiMatchQueryBuilder::type)
|
entry(TYPE_FIELD.getPreferredName(), (qb, obj) -> qb.type((String) obj))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String query;
|
private final String query;
|
||||||
private final Map<String, Float> fields;
|
private final Map<String, Float> fields;
|
||||||
private final Map<String, String> options;
|
private final Map<String, Object> options;
|
||||||
private final MultiMatchQueryPredicate predicate;
|
|
||||||
|
|
||||||
public MultiMatchQuery(Source source, String query, Map<String, Float> fields, MultiMatchQueryPredicate predicate) {
|
public MultiMatchQuery(Source source, String query, Map<String, Float> fields, Map<String, Object> options) {
|
||||||
super(source);
|
super(source);
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
this.predicate = predicate;
|
this.options = options;
|
||||||
this.options = predicate.optionMap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected QueryBuilder asBuilder() {
|
protected QueryBuilder asBuilder() {
|
||||||
final MultiMatchQueryBuilder queryBuilder = QueryBuilders.multiMatchQuery(query);
|
final MultiMatchQueryBuilder queryBuilder = QueryBuilders.multiMatchQuery(query);
|
||||||
queryBuilder.fields(fields);
|
queryBuilder.fields(fields);
|
||||||
queryBuilder.analyzer(predicate.analyzer());
|
|
||||||
options.forEach((k, v) -> {
|
options.forEach((k, v) -> {
|
||||||
if (BUILDER_APPLIERS.containsKey(k)) {
|
if (BUILDER_APPLIERS.containsKey(k)) {
|
||||||
BUILDER_APPLIERS.get(k).accept(queryBuilder, v);
|
BUILDER_APPLIERS.get(k).accept(queryBuilder, v);
|
||||||
|
@ -77,7 +87,7 @@ public class MultiMatchQuery extends Query {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(query, fields, predicate);
|
return Objects.hash(query, fields, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,11 +101,17 @@ public class MultiMatchQuery extends Query {
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiMatchQuery other = (MultiMatchQuery) obj;
|
MultiMatchQuery other = (MultiMatchQuery) obj;
|
||||||
return Objects.equals(query, other.query) && Objects.equals(fields, other.fields) && Objects.equals(predicate, other.predicate);
|
return Objects.equals(query, other.query) && Objects.equals(fields, other.fields) && Objects.equals(options, other.options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String innerToString() {
|
protected String innerToString() {
|
||||||
return fields + ":" + query;
|
// Use a TreeMap so we get the fields in a predictable order.
|
||||||
|
return new TreeMap<>(fields) + ":" + query;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean scorable() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,6 +307,10 @@ public class CsvTests extends ESTestCase {
|
||||||
"CSV tests cannot currently handle scoring that depends on Lucene",
|
"CSV tests cannot currently handle scoring that depends on Lucene",
|
||||||
testCase.requiredCapabilities.contains(EsqlCapabilities.Cap.METADATA_SCORE.capabilityName())
|
testCase.requiredCapabilities.contains(EsqlCapabilities.Cap.METADATA_SCORE.capabilityName())
|
||||||
);
|
);
|
||||||
|
assumeFalse(
|
||||||
|
"CSV tests cannot currently handle multi_match function that depends on Lucene",
|
||||||
|
testCase.requiredCapabilities.contains(EsqlCapabilities.Cap.MULTI_MATCH_FUNCTION.capabilityName())
|
||||||
|
);
|
||||||
|
|
||||||
if (Build.current().isSnapshot()) {
|
if (Build.current().isSnapshot()) {
|
||||||
assertThat(
|
assertThat(
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.elasticsearch.xpack.esql.expression.function.aggregate.Max;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.aggregate.Min;
|
import org.elasticsearch.xpack.esql.expression.function.aggregate.Min;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.fulltext.MatchOperator;
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.MatchOperator;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.MultiMatch;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.fulltext.QueryString;
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.QueryString;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Concat;
|
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Concat;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Substring;
|
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Substring;
|
||||||
|
@ -2851,6 +2852,22 @@ public class AnalyzerTests extends ESTestCase {
|
||||||
assertEquals(DataType.DOUBLE, ee.dataType());
|
assertEquals(DataType.DOUBLE, ee.dataType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testFunctionNamedParamsAsFunctionArgument2() {
|
||||||
|
LogicalPlan plan = analyze("""
|
||||||
|
from test
|
||||||
|
| WHERE MULTI_MATCH("Anna Smith", first_name, last_name, {"minimum_should_match": 3.0})
|
||||||
|
""");
|
||||||
|
Limit limit = as(plan, Limit.class);
|
||||||
|
Filter filter = as(limit.child(), Filter.class);
|
||||||
|
MultiMatch mm = as(filter.condition(), MultiMatch.class);
|
||||||
|
MapExpression me = as(mm.options(), MapExpression.class);
|
||||||
|
assertEquals(1, me.entryExpressions().size());
|
||||||
|
EntryExpression ee = as(me.entryExpressions().get(0), EntryExpression.class);
|
||||||
|
assertEquals(new Literal(EMPTY, "minimum_should_match", DataType.KEYWORD), ee.key());
|
||||||
|
assertEquals(new Literal(EMPTY, 3.0, DataType.DOUBLE), ee.value());
|
||||||
|
assertEquals(DataType.DOUBLE, ee.dataType());
|
||||||
|
}
|
||||||
|
|
||||||
public void testResolveInsist_fieldExists_insistedOutputContainsNoUnmappedFields() {
|
public void testResolveInsist_fieldExists_insistedOutputContainsNoUnmappedFields() {
|
||||||
assumeTrue("Requires UNMAPPED FIELDS", EsqlCapabilities.Cap.UNMAPPED_FIELDS.isEnabled());
|
assumeTrue("Requires UNMAPPED FIELDS", EsqlCapabilities.Cap.UNMAPPED_FIELDS.isEnabled());
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.elasticsearch.xpack.esql.core.type.EsField;
|
||||||
import org.elasticsearch.xpack.esql.core.type.InvalidMappedField;
|
import org.elasticsearch.xpack.esql.core.type.InvalidMappedField;
|
||||||
import org.elasticsearch.xpack.esql.core.type.UnsupportedEsField;
|
import org.elasticsearch.xpack.esql.core.type.UnsupportedEsField;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.MultiMatch;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.fulltext.QueryString;
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.QueryString;
|
||||||
import org.elasticsearch.xpack.esql.index.EsIndex;
|
import org.elasticsearch.xpack.esql.index.EsIndex;
|
||||||
import org.elasticsearch.xpack.esql.index.IndexResolution;
|
import org.elasticsearch.xpack.esql.index.IndexResolution;
|
||||||
|
@ -53,6 +54,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
|
||||||
import static org.elasticsearch.xpack.esql.core.type.DataType.IP;
|
import static org.elasticsearch.xpack.esql.core.type.DataType.IP;
|
||||||
import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
|
import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
|
||||||
import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
|
import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
|
||||||
|
import static org.elasticsearch.xpack.esql.core.type.DataType.OBJECT;
|
||||||
import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
|
import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
|
||||||
import static org.elasticsearch.xpack.esql.core.type.DataType.VERSION;
|
import static org.elasticsearch.xpack.esql.core.type.DataType.VERSION;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
@ -1465,6 +1467,7 @@ public class VerifierTests extends ESTestCase {
|
||||||
|
|
||||||
public void testFullTextFunctionsDisjunctions() {
|
public void testFullTextFunctionsDisjunctions() {
|
||||||
checkWithFullTextFunctionsDisjunctions("match(last_name, \"Smith\")");
|
checkWithFullTextFunctionsDisjunctions("match(last_name, \"Smith\")");
|
||||||
|
checkWithFullTextFunctionsDisjunctions("multi_match(\"Smith\", first_name, last_name)");
|
||||||
checkWithFullTextFunctionsDisjunctions("last_name : \"Smith\"");
|
checkWithFullTextFunctionsDisjunctions("last_name : \"Smith\"");
|
||||||
checkWithFullTextFunctionsDisjunctions("qstr(\"last_name: Smith\")");
|
checkWithFullTextFunctionsDisjunctions("qstr(\"last_name: Smith\")");
|
||||||
checkWithFullTextFunctionsDisjunctions("kql(\"last_name: Smith\")");
|
checkWithFullTextFunctionsDisjunctions("kql(\"last_name: Smith\")");
|
||||||
|
@ -2325,6 +2328,171 @@ public class VerifierTests extends ESTestCase {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchOptions() {
|
||||||
|
// Check positive cases
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name)");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, {\"analyzer\": \"standard\"})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"analyzer\": \"standard\"})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"slop\": 10})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"auto_generate_synonyms_phrase_query\": true})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"fuzziness\": 2})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"fuzzy_transpositions\": false})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"lenient\": false})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"max_expansions\": 10})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"minimum_should_match\": \"2\"})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"operator\": \"AND\"})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"prefix_length\": 2})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"tie_breaker\": 1.0})");
|
||||||
|
query("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"type\": \"best_fields\"})");
|
||||||
|
|
||||||
|
// Check all data types for available options
|
||||||
|
DataType[] optionTypes = new DataType[] { INTEGER, LONG, FLOAT, DOUBLE, KEYWORD, BOOLEAN };
|
||||||
|
for (Map.Entry<String, DataType> allowedOptions : MultiMatch.OPTIONS.entrySet()) {
|
||||||
|
String optionName = allowedOptions.getKey();
|
||||||
|
DataType optionType = allowedOptions.getValue();
|
||||||
|
// Check every possible type for the option - we'll try to convert it to the expected type
|
||||||
|
for (DataType currentType : optionTypes) {
|
||||||
|
String optionValue = switch (currentType) {
|
||||||
|
case BOOLEAN -> String.valueOf(randomBoolean());
|
||||||
|
case INTEGER -> String.valueOf(randomIntBetween(0, 100000));
|
||||||
|
case LONG -> String.valueOf(randomLong());
|
||||||
|
case FLOAT -> String.valueOf(randomFloat());
|
||||||
|
case DOUBLE -> String.valueOf(randomDouble());
|
||||||
|
case KEYWORD -> randomAlphaOfLength(10);
|
||||||
|
default -> throw new IllegalArgumentException("Unsupported option type: " + currentType);
|
||||||
|
};
|
||||||
|
String queryOptionValue = optionValue;
|
||||||
|
if (currentType == KEYWORD) {
|
||||||
|
queryOptionValue = "\"" + optionValue + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
String query = "FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\""
|
||||||
|
+ optionName
|
||||||
|
+ "\": "
|
||||||
|
+ queryOptionValue
|
||||||
|
+ "})";
|
||||||
|
try {
|
||||||
|
// Check conversion is possible
|
||||||
|
DataTypeConverter.convert(optionValue, optionType);
|
||||||
|
// If no exception was thrown, conversion is possible and should be done
|
||||||
|
query(query);
|
||||||
|
} catch (InvalidArgumentException e) {
|
||||||
|
// Conversion is not possible, query should fail
|
||||||
|
assertEquals(
|
||||||
|
"1:19: Invalid option ["
|
||||||
|
+ optionName
|
||||||
|
+ "] in [MULTI_MATCH(\"Jean\", first_name, last_name, {\""
|
||||||
|
+ optionName
|
||||||
|
+ "\": "
|
||||||
|
+ queryOptionValue
|
||||||
|
+ "})], cannot "
|
||||||
|
+ (optionType == OBJECT ? "convert from" : "cast")
|
||||||
|
+ " ["
|
||||||
|
+ optionValue
|
||||||
|
+ "]"
|
||||||
|
+ (optionType == OBJECT ? (", type [keyword]") : "")
|
||||||
|
+ " to ["
|
||||||
|
+ optionType.typeName()
|
||||||
|
+ "]",
|
||||||
|
error(query)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
error("FROM test | WHERE MULTI_MATCH(\"Jean\", first_name, last_name, {\"unknown_option\": true})"),
|
||||||
|
containsString(
|
||||||
|
"1:19: Invalid option [unknown_option] in [MULTI_MATCH(\"Jean\", first_name, last_name, "
|
||||||
|
+ "{\"unknown_option\": true})], expected one of "
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchFunctionIsNotNullable() {
|
||||||
|
assertEquals(
|
||||||
|
"1:62: [MultiMatch] function cannot operate on [text::keyword], which is not a field from an index mapping",
|
||||||
|
error("row n = null | eval text = n + 5 | where multi_match(\"Anna\", text::keyword)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchWithNonIndexedColumnCurrentlyUnsupported() {
|
||||||
|
assertEquals(
|
||||||
|
"1:78: [MultiMatch] function cannot operate on [initial], which is not a field from an index mapping",
|
||||||
|
error("from test | eval initial = substring(first_name, 1) | where multi_match(\"A\", initial)")
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
"1:80: [MultiMatch] function cannot operate on [text], which is not a field from an index mapping",
|
||||||
|
error("from test | eval text=concat(first_name, last_name) | where multi_match(\"cat\", text)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchFunctionNotAllowedAfterCommands() throws Exception {
|
||||||
|
assertEquals(
|
||||||
|
"1:24: [MultiMatch] function cannot be used after LIMIT",
|
||||||
|
error("from test | limit 10 | where multi_match(\"Anna\", first_name)")
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
"1:47: [MultiMatch] function cannot be used after STATS",
|
||||||
|
error("from test | STATS c = AVG(salary) BY gender | where multi_match(\"F\", gender)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchFunctionWithDisjunctions() {
|
||||||
|
checkWithDisjunctions("MultiMatch", "multi_match(\"Anna\", first_name, last_name)", "function");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchFunctionWithNonBooleanFunctions() {
|
||||||
|
checkFullTextFunctionsWithNonBooleanFunctions("MultiMatch", "multi_match(\"Anna\", first_name, last_name)", "function");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchFunctionArgNotConstant() throws Exception {
|
||||||
|
assertEquals(
|
||||||
|
"1:19: second argument of [match(first_name, first_name)] must be a constant, received [first_name]",
|
||||||
|
error("from test | where match(first_name, first_name)")
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
"1:59: second argument of [match(first_name, query)] must be a constant, received [query]",
|
||||||
|
error("from test | eval query = concat(\"first\", \" name\") | where match(first_name, query)")
|
||||||
|
);
|
||||||
|
// Other value types are tested in QueryStringFunctionTests
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should pass eventually once we lift some restrictions on the multi-match function.
|
||||||
|
public void testMultiMatchFunctionCurrentlyUnsupportedBehaviour() throws Exception {
|
||||||
|
assertEquals(
|
||||||
|
"1:82: Unknown column [first_name]\nline 1:94: Unknown column [last_name]",
|
||||||
|
error("from test | stats max_salary = max(salary) by emp_no | where multi_match(\"Anna\", first_name, last_name)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchFunctionNullArgs() throws Exception {
|
||||||
|
assertEquals(
|
||||||
|
"1:19: first argument of [multi_match(\"query\", null)] cannot be null, received [null]",
|
||||||
|
error("from test | where multi_match(\"query\", null)")
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
"1:19: first argument of [multi_match(null, first_name)] cannot be null, received [null]",
|
||||||
|
error("from test | where multi_match(null, first_name)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchTargetsExistingField() throws Exception {
|
||||||
|
assertEquals(
|
||||||
|
"1:53: Unknown column [first_name]\nline 1:65: Unknown column [last_name]",
|
||||||
|
error("from test | keep emp_no | where multi_match(\"Anna\", first_name, last_name)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchInsideEval() throws Exception {
|
||||||
|
assumeTrue("MultiMatch operator is available just for snapshots", Build.current().isSnapshot());
|
||||||
|
assertEquals(
|
||||||
|
"1:36: [MultiMatch] function is only supported in WHERE commands\n"
|
||||||
|
+ "line 1:55: [MultiMatch] function cannot operate on [title], which is not a field from an index mapping",
|
||||||
|
error("row title = \"brown fox\" | eval x = multi_match(\"fox\", title)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public void testInsistNotOnTopOfFrom() {
|
public void testInsistNotOnTopOfFrom() {
|
||||||
assumeTrue("requires snapshot builds", Build.current().isSnapshot());
|
assumeTrue("requires snapshot builds", Build.current().isSnapshot());
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.xpack.esql.expression.function.fulltext;
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.annotations.Name;
|
||||||
|
|
||||||
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
|
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
||||||
|
import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
|
||||||
|
import org.elasticsearch.xpack.esql.core.tree.Source;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.FunctionName;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import static org.elasticsearch.xpack.esql.planner.TranslatorHandler.TRANSLATOR_HANDLER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is essentially testing MultiMatch as if it was a regular match: it makes a singular list of the supplied "field" parameter.
|
||||||
|
*/
|
||||||
|
@FunctionName("multi_match")
|
||||||
|
public class MultiMatchTests extends MatchTests {
|
||||||
|
|
||||||
|
public MultiMatchTests(@Name("TestCase") Supplier<TestCaseSupplier.TestCase> testCaseSupplier) {
|
||||||
|
super(testCaseSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Expression build(Source source, List<Expression> args) {
|
||||||
|
// Note we are reversing the order of arguments here.
|
||||||
|
MultiMatch mm = new MultiMatch(source, args.get(1), List.of(args.get(0)), args.get(2));
|
||||||
|
// We need to add the QueryBuilder to the multi_match expression, as it is used to implement equals() and hashCode() and
|
||||||
|
// thus test the serialization methods. But we can only do this if the parameters make sense .
|
||||||
|
if (mm.query().foldable() && mm.fields().stream().allMatch(field -> field instanceof FieldAttribute)) {
|
||||||
|
QueryBuilder queryBuilder = TRANSLATOR_HANDLER.asQuery(mm).toQueryBuilder();
|
||||||
|
mm.replaceQueryBuilder(queryBuilder);
|
||||||
|
}
|
||||||
|
return mm;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* 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; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.xpack.esql.expression.predicate.fulltext;
|
|
||||||
|
|
||||||
import org.elasticsearch.xpack.esql.expression.AbstractExpressionSerializationTests;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public abstract class AbstractFulltextSerializationTests<T extends FullTextPredicate> extends AbstractExpressionSerializationTests<T> {
|
|
||||||
|
|
||||||
static final String OPTION_DELIMITER = ";";
|
|
||||||
|
|
||||||
String randomOptionOrNull() {
|
|
||||||
if (randomBoolean()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
HashMap<String, String> options = new HashMap<>();
|
|
||||||
int maxOptions = randomInt(8);
|
|
||||||
for (int i = 0; i < maxOptions; i++) {
|
|
||||||
var opt = randomIndividualOption();
|
|
||||||
options.computeIfAbsent(opt.getKey(), k -> opt.getValue()); // no duplicate options
|
|
||||||
}
|
|
||||||
return options.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(OPTION_DELIMITER));
|
|
||||||
}
|
|
||||||
|
|
||||||
Map.Entry<String, String> randomIndividualOption() {
|
|
||||||
return Map.entry(randomAlphaOfLength(randomIntBetween(1, 4)), randomAlphaOfLength(randomIntBetween(1, 4)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* 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; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.xpack.esql.expression.predicate.fulltext;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class MultiMatchQuerySerializationTests extends AbstractFulltextSerializationTests<MultiMatchQueryPredicate> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final MultiMatchQueryPredicate createTestInstance() {
|
|
||||||
return new MultiMatchQueryPredicate(
|
|
||||||
randomSource(),
|
|
||||||
randomFieldString(),
|
|
||||||
randomAlphaOfLength(randomIntBetween(1, 16)),
|
|
||||||
randomOptionOrNull()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MultiMatchQueryPredicate mutateInstance(MultiMatchQueryPredicate instance) throws IOException {
|
|
||||||
var fieldString = instance.fieldString();
|
|
||||||
var query = instance.query();
|
|
||||||
var options = instance.options();
|
|
||||||
switch (between(0, 2)) {
|
|
||||||
case 0 -> fieldString = randomValueOtherThan(fieldString, this::randomFieldString);
|
|
||||||
case 1 -> query = randomValueOtherThan(query, () -> randomAlphaOfLength(randomIntBetween(1, 16)));
|
|
||||||
case 2 -> options = randomValueOtherThan(options, this::randomOptionOrNull);
|
|
||||||
}
|
|
||||||
return new MultiMatchQueryPredicate(instance.source(), fieldString, query, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
String randomFieldString() {
|
|
||||||
if (randomBoolean()) {
|
|
||||||
return ""; // empty, no fields
|
|
||||||
}
|
|
||||||
HashMap<String, Float> fields = new HashMap<>();
|
|
||||||
int maxOptions = randomInt(4);
|
|
||||||
for (int i = 0; i < maxOptions; i++) {
|
|
||||||
var opt = randomIndividualField();
|
|
||||||
fields.computeIfAbsent(opt.getKey(), k -> opt.getValue()); // no duplicate fields
|
|
||||||
}
|
|
||||||
return fields.entrySet().stream().map(e -> e.getKey() + "^" + e.getValue()).collect(Collectors.joining(","));
|
|
||||||
}
|
|
||||||
|
|
||||||
Map.Entry<String, Float> randomIndividualField() {
|
|
||||||
return Map.entry(randomAlphaOfLength(randomIntBetween(1, 4)), randomFloat());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,6 +20,7 @@ import org.elasticsearch.index.mapper.MapperServiceTestCase;
|
||||||
import org.elasticsearch.index.mapper.ParsedDocument;
|
import org.elasticsearch.index.mapper.ParsedDocument;
|
||||||
import org.elasticsearch.index.query.BoolQueryBuilder;
|
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||||
import org.elasticsearch.index.query.MatchQueryBuilder;
|
import org.elasticsearch.index.query.MatchQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
|
||||||
import org.elasticsearch.index.query.Operator;
|
import org.elasticsearch.index.query.Operator;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
|
@ -1657,6 +1658,37 @@ public class LocalPhysicalPlanOptimizerTests extends MapperServiceTestCase {
|
||||||
assertThat(expectedQStrQuery.toString(), is(planStr.get()));
|
assertThat(expectedQStrQuery.toString(), is(planStr.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testMultiMatchOptionsPushDown() {
|
||||||
|
String query = """
|
||||||
|
from test
|
||||||
|
| where MULTI_MATCH("Anna", first_name, last_name, {"fuzzy_rewrite": "constant_score", "slop": 10, "analyzer": "auto",
|
||||||
|
"auto_generate_synonyms_phrase_query": "false", "fuzziness": "auto", "fuzzy_transpositions": false, "lenient": "false",
|
||||||
|
"max_expansions": 10, "minimum_should_match": 3, "operator": "AND", "prefix_length": 20, "tie_breaker": 1.0,
|
||||||
|
"type": "best_fields", "boost": 2.0})
|
||||||
|
""";
|
||||||
|
var plan = plannerOptimizer.plan(query);
|
||||||
|
|
||||||
|
AtomicReference<String> planStr = new AtomicReference<>();
|
||||||
|
plan.forEachDown(EsQueryExec.class, result -> planStr.set(result.query().toString()));
|
||||||
|
|
||||||
|
var expectedQuery = new MultiMatchQueryBuilder("Anna").fields(Map.of("first_name", 1.0f, "last_name", 1.0f))
|
||||||
|
.slop(10)
|
||||||
|
.boost(2.0f)
|
||||||
|
.analyzer("auto")
|
||||||
|
.autoGenerateSynonymsPhraseQuery(false)
|
||||||
|
.operator(Operator.fromString("AND"))
|
||||||
|
.fuzziness(Fuzziness.fromString("auto"))
|
||||||
|
.fuzzyRewrite("constant_score")
|
||||||
|
.fuzzyTranspositions(false)
|
||||||
|
.lenient(false)
|
||||||
|
.type("best_fields")
|
||||||
|
.maxExpansions(10)
|
||||||
|
.minimumShouldMatch("3")
|
||||||
|
.prefixLength(20)
|
||||||
|
.tieBreaker(1.0f);
|
||||||
|
assertThat(expectedQuery.toString(), is(planStr.get()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expecting
|
* Expecting
|
||||||
* LimitExec[1000[INTEGER]]
|
* LimitExec[1000[INTEGER]]
|
||||||
|
|
|
@ -61,6 +61,7 @@ import org.elasticsearch.xpack.esql.expression.function.aggregate.Sum;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.aggregate.ToPartial;
|
import org.elasticsearch.xpack.esql.expression.function.aggregate.ToPartial;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.aggregate.Values;
|
import org.elasticsearch.xpack.esql.expression.function.aggregate.Values;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
|
||||||
|
import org.elasticsearch.xpack.esql.expression.function.fulltext.MultiMatch;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.grouping.Bucket;
|
import org.elasticsearch.xpack.esql.expression.function.grouping.Bucket;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.grouping.Categorize;
|
import org.elasticsearch.xpack.esql.expression.function.grouping.Categorize;
|
||||||
import org.elasticsearch.xpack.esql.expression.function.scalar.convert.ToDouble;
|
import org.elasticsearch.xpack.esql.expression.function.scalar.convert.ToDouble;
|
||||||
|
@ -7378,6 +7379,24 @@ public class LogicalPlanOptimizerTests extends ESTestCase {
|
||||||
assertEquals(DataType.DOUBLE, ee.dataType());
|
assertEquals(DataType.DOUBLE, ee.dataType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testFunctionNamedParamsAsFunctionArgument1() {
|
||||||
|
var query = """
|
||||||
|
from test
|
||||||
|
| WHERE MULTI_MATCH("Anna Smith", first_name, last_name, {"minimum_should_match": 2.0})
|
||||||
|
""";
|
||||||
|
var plan = optimizedPlan(query);
|
||||||
|
Limit limit = as(plan, Limit.class);
|
||||||
|
Filter filter = as(limit.child(), Filter.class);
|
||||||
|
MultiMatch match = as(filter.condition(), MultiMatch.class);
|
||||||
|
MapExpression me = as(match.options(), MapExpression.class);
|
||||||
|
assertEquals(1, me.entryExpressions().size());
|
||||||
|
EntryExpression ee = as(me.entryExpressions().get(0), EntryExpression.class);
|
||||||
|
BytesRef key = as(ee.key().fold(FoldContext.small()), BytesRef.class);
|
||||||
|
assertEquals("minimum_should_match", key.utf8ToString());
|
||||||
|
assertEquals(new Literal(EMPTY, 2.0, DataType.DOUBLE), ee.value());
|
||||||
|
assertEquals(DataType.DOUBLE, ee.dataType());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TopN[[Order[emp_no{f}#11,ASC,LAST]],1000[INTEGER]]
|
* TopN[[Order[emp_no{f}#11,ASC,LAST]],1000[INTEGER]]
|
||||||
* \_Join[LEFT,[language_code{r}#5],[language_code{r}#5],[language_code{f}#22]]
|
* \_Join[LEFT,[language_code{r}#5],[language_code{r}#5],[language_code{f}#22]]
|
||||||
|
|
|
@ -11,48 +11,36 @@ import org.elasticsearch.index.query.MultiMatchQueryBuilder;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.esql.core.tree.Source;
|
import org.elasticsearch.xpack.esql.core.tree.Source;
|
||||||
import org.elasticsearch.xpack.esql.core.util.StringUtils;
|
import org.elasticsearch.xpack.esql.core.util.StringUtils;
|
||||||
import org.elasticsearch.xpack.esql.expression.predicate.fulltext.MultiMatchQueryPredicate;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
public class MultiMatchQueryTests extends ESTestCase {
|
public class MultiMatchQueryTests extends ESTestCase {
|
||||||
|
|
||||||
public void testQueryBuilding() {
|
public void testQueryBuilding() {
|
||||||
MultiMatchQueryBuilder qb = getBuilder("lenient=true");
|
MultiMatchQueryBuilder qb = getBuilder(Map.of("lenient", true));
|
||||||
assertThat(qb.lenient(), equalTo(true));
|
assertThat(qb.lenient(), equalTo(true));
|
||||||
|
|
||||||
qb = getBuilder("type=best_fields");
|
qb = getBuilder(Map.of("type", "best_fields"));
|
||||||
assertThat(qb.getType(), equalTo(MultiMatchQueryBuilder.Type.BEST_FIELDS));
|
assertThat(qb.getType(), equalTo(MultiMatchQueryBuilder.Type.BEST_FIELDS));
|
||||||
|
|
||||||
Exception e = expectThrows(IllegalArgumentException.class, () -> getBuilder("pizza=yummy"));
|
Exception e = expectThrows(IllegalArgumentException.class, () -> getBuilder(Map.of("pizza", "yummy")));
|
||||||
assertThat(e.getMessage(), equalTo("illegal multi_match option [pizza]"));
|
assertThat(e.getMessage(), equalTo("illegal multi_match option [pizza]"));
|
||||||
|
|
||||||
e = expectThrows(ElasticsearchParseException.class, () -> getBuilder("type=aoeu"));
|
e = expectThrows(ElasticsearchParseException.class, () -> getBuilder(Map.of("type", "aoeu")));
|
||||||
assertThat(e.getMessage(), equalTo("failed to parse [multi_match] query type [aoeu]. unknown type."));
|
assertThat(e.getMessage(), equalTo("failed to parse [multi_match] query type [aoeu]. unknown type."));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MultiMatchQueryBuilder getBuilder(String options) {
|
private static MultiMatchQueryBuilder getBuilder(Map<String, Object> options) {
|
||||||
final Source source = new Source(1, 1, StringUtils.EMPTY);
|
final Source source = new Source(1, 1, StringUtils.EMPTY);
|
||||||
final MultiMatchQueryPredicate mmqp = new MultiMatchQueryPredicate(source, "foo,bar", "eggplant", options);
|
final MultiMatchQuery mmq = new MultiMatchQuery(source, "eggplant", Map.of("bar", 1.0f, "foo", 1.0f), options);
|
||||||
final Map<String, Float> fields = new HashMap<>();
|
|
||||||
fields.put("foo", 1.0f);
|
|
||||||
fields.put("bar", 1.0f);
|
|
||||||
final MultiMatchQuery mmq = new MultiMatchQuery(source, "eggplant", fields, mmqp);
|
|
||||||
return (MultiMatchQueryBuilder) mmq.asBuilder();
|
return (MultiMatchQueryBuilder) mmq.asBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testToString() {
|
public void testToString() {
|
||||||
final Source source = new Source(1, 1, StringUtils.EMPTY);
|
final Source source = new Source(1, 1, StringUtils.EMPTY);
|
||||||
final MultiMatchQueryPredicate mmqp = new MultiMatchQueryPredicate(source, "foo,bar", "eggplant", "");
|
final MultiMatchQuery mmq = new MultiMatchQuery(source, "eggplant", Map.of("bar", 1.0f, "foo", 1.0f), null);
|
||||||
// Use a TreeMap so we get the fields in a predictable order.
|
|
||||||
final Map<String, Float> fields = new TreeMap<>();
|
|
||||||
fields.put("foo", 1.0f);
|
|
||||||
fields.put("bar", 1.0f);
|
|
||||||
final MultiMatchQuery mmq = new MultiMatchQuery(source, "eggplant", fields, mmqp);
|
|
||||||
assertEquals("MultiMatchQuery@1:2[{bar=1.0, foo=1.0}:eggplant]", mmq.toString());
|
assertEquals("MultiMatchQuery@1:2[{bar=1.0, foo=1.0}:eggplant]", mmq.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@ setup:
|
||||||
- match: {esql.functions.coalesce: $functions_coalesce}
|
- match: {esql.functions.coalesce: $functions_coalesce}
|
||||||
- gt: {esql.functions.categorize: $functions_categorize}
|
- gt: {esql.functions.categorize: $functions_categorize}
|
||||||
# Testing for the entire function set isn't feasbile, so we just check that we return the correct count as an approximation.
|
# Testing for the entire function set isn't feasbile, so we just check that we return the correct count as an approximation.
|
||||||
- length: {esql.functions: 136} # check the "sister" test below for a likely update to the same esql.functions length check
|
- length: {esql.functions: 137} # check the "sister" test below for a likely update to the same esql.functions length check
|
||||||
|
|
||||||
---
|
---
|
||||||
"Basic ESQL usage output (telemetry) non-snapshot version":
|
"Basic ESQL usage output (telemetry) non-snapshot version":
|
||||||
|
|
Loading…
Reference in New Issue