From 58fafe224fefa68df31d889f57cb15137c391bf6 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Mon, 22 Aug 2022 07:32:25 -0700 Subject: [PATCH] Add source fallback support for match_only_text mapped type (#89473) This change adds access to mapped match_only_text fields via the Painless scripting fields API. The values returned from a match_only_text field via the scripting fields API always use source as described by (#81246). These are not available via doc values so there are no bwc issues. --- docs/changelog/89473.yaml | 5 + .../test/painless/50_script_doc_values.yml | 135 +++++++++++++++++- .../extras/MatchOnlyTextFieldMapper.java | 13 ++ .../field/MatchOnlyTextDocValuesField.java | 18 +++ 4 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/89473.yaml create mode 100644 server/src/main/java/org/elasticsearch/script/field/MatchOnlyTextDocValuesField.java diff --git a/docs/changelog/89473.yaml b/docs/changelog/89473.yaml new file mode 100644 index 000000000000..b141fcd28ba3 --- /dev/null +++ b/docs/changelog/89473.yaml @@ -0,0 +1,5 @@ +pr: 89473 +summary: Add source fallback support for `match_only_text` mapped type +area: Mapping +type: enhancement +issues: [] diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml index 053a12ae3ba7..1d99448cf3cf 100644 --- a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml @@ -81,6 +81,8 @@ setup: fielddata: true text_no_field_data: type: text + match_only_text: + type: match_only_text token_count: type: token_count analyzer: standard @@ -125,6 +127,7 @@ setup: scaled_float_no_doc_values: 3.14 text: "Lots of text." text_no_field_data: "Lots of text." + match_only_text: "Lots of text." token_count: count all these words please - do: @@ -169,7 +172,7 @@ setup: scaled_float_no_doc_values: [2.5, -3.5] text: ["Lots of text.", "even more text", "SOOOOO much text"] text_no_field_data: ["Lots of text.", "even more text", "SOOOOO much text"] - + match_only_text: ["Lots of text.", "even more text", "SOOOOO much text"] - do: indices.refresh: {} @@ -3234,6 +3237,136 @@ setup: - match: { hits.hits.1.fields.field.0: "0" } - match: { hits.hits.2.fields.field.0: "Lots of text.SOOOOO much texteven more text3" } +--- +"match_only_text": + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: "1" } } + script_fields: + field: + script: + source: "doc['match_only_text'].get(0)" + - match: { error.failed_shards.0.reason.caused_by.type: "illegal_argument_exception" } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: "1" } } + script_fields: + field: + script: + source: "doc['match_only_text'].value" + - match: { error.failed_shards.0.reason.caused_by.type: "illegal_argument_exception" } + + - do: + search: + rest_total_hits_as_int: true + body: + sort: [ { rank: asc } ] + script_fields: + field: + script: + source: "field('match_only_text').get('')" + - match: { hits.hits.0.fields.field.0: "Lots of text." } + - match: { hits.hits.1.fields.field.0: "" } + - match: { hits.hits.2.fields.field.0: "Lots of text." } + + - do: + search: + rest_total_hits_as_int: true + body: + sort: [ { rank: asc } ] + script_fields: + field: + script: + source: "/* avoid yaml stash */ $('match_only_text', '')" + - match: { hits.hits.0.fields.field.0: "Lots of text." } + - match: { hits.hits.1.fields.field.0: "" } + - match: { hits.hits.2.fields.field.0: "Lots of text." } + + - do: + search: + rest_total_hits_as_int: true + body: + sort: [ { rank: asc } ] + script_fields: + field: + script: + source: "String defaultText = 'default text'; field('match_only_text').get(defaultText)" + - match: { hits.hits.0.fields.field.0: "Lots of text." } + - match: { hits.hits.1.fields.field.0: "default text" } + - match: { hits.hits.2.fields.field.0: "Lots of text." } + + - do: + search: + rest_total_hits_as_int: true + body: + sort: [ { rank: asc } ] + script_fields: + field: + script: + source: "String defaultText = 'default text'; $('match_only_text', defaultText)" + - match: { hits.hits.0.fields.field.0: "Lots of text." } + - match: { hits.hits.1.fields.field.0: "default text" } + - match: { hits.hits.2.fields.field.0: "Lots of text." } + + - do: + search: + rest_total_hits_as_int: true + body: + sort: [ { rank: asc } ] + script_fields: + field: + script: + source: "field('match_only_text').get(1, '')" + - match: { hits.hits.0.fields.field.0: "" } + - match: { hits.hits.1.fields.field.0: "" } + - match: { hits.hits.2.fields.field.0: "SOOOOO much text" } + + - do: + search: + rest_total_hits_as_int: true + body: + sort: [ { rank: asc } ] + script_fields: + field: + script: + source: "String defaultText = 'default text'; field('match_only_text').get(1, defaultText)" + - match: { hits.hits.0.fields.field.0: "default text" } + - match: { hits.hits.1.fields.field.0: "default text" } + - match: { hits.hits.2.fields.field.0: "SOOOOO much text" } + + - do: + search: + rest_total_hits_as_int: true + body: + sort: [ { rank: asc } ] + script_fields: + field: + script: + source: "field('match_only_text').get(1, '')" + - match: { hits.hits.0.fields.field.0: "" } + - match: { hits.hits.1.fields.field.0: "" } + - match: { hits.hits.2.fields.field.0: "SOOOOO much text" } + + - do: + search: + rest_total_hits_as_int: true + body: + sort: [ { rank: asc } ] + script_fields: + field: + script: + source: "String cat = ''; for (String s : field('match_only_text')) { cat += s; } cat + field('match_only_text').size();" + - match: { hits.hits.0.fields.field.0: "Lots of text.1" } + - match: { hits.hits.1.fields.field.0: "0" } + - match: { hits.hits.2.fields.field.0: "Lots of text.SOOOOO much texteven more text3" } + --- "version and sequence number": - do: diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java index a8d73c89700e..e1ea8690dc57 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java @@ -33,6 +33,7 @@ import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.analysis.NamedAnalyzer; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.fielddata.SourceValueFetcherSortedBinaryIndexFieldData; import org.elasticsearch.index.mapper.DocumentParserContext; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MapperBuilderContext; @@ -44,6 +45,8 @@ import org.elasticsearch.index.mapper.TextParams; import org.elasticsearch.index.mapper.TextSearchInfo; import org.elasticsearch.index.mapper.ValueFetcher; import org.elasticsearch.index.query.SearchExecutionContext; +import org.elasticsearch.script.field.TextDocValuesField; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; import org.elasticsearch.search.lookup.SourceLookup; import java.io.IOException; @@ -276,6 +279,16 @@ public class MatchOnlyTextFieldMapper extends FieldMapper { @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { + if (fieldDataContext.fielddataOperation() == FielddataOperation.SCRIPT) { + return new SourceValueFetcherSortedBinaryIndexFieldData.Builder( + name(), + CoreValuesSourceType.KEYWORD, + SourceValueFetcher.toString(fieldDataContext.sourcePathsLookup().apply(name())), + fieldDataContext.lookupSupplier().get().source(), + TextDocValuesField::new + ); + } + throw new IllegalArgumentException(CONTENT_TYPE + " fields do not support sorting and aggregations"); } diff --git a/server/src/main/java/org/elasticsearch/script/field/MatchOnlyTextDocValuesField.java b/server/src/main/java/org/elasticsearch/script/field/MatchOnlyTextDocValuesField.java new file mode 100644 index 000000000000..3e26398f3304 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/script/field/MatchOnlyTextDocValuesField.java @@ -0,0 +1,18 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.script.field; + +import org.elasticsearch.index.fielddata.SortedBinaryDocValues; + +public class MatchOnlyTextDocValuesField extends BaseKeywordDocValuesField { + + public MatchOnlyTextDocValuesField(SortedBinaryDocValues input, String name) { + super(input, name); + } +}