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.
This commit is contained in:
Jack Conradson 2022-08-22 07:32:25 -07:00 committed by GitHub
parent b15f6dd6d0
commit 58fafe224f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 170 additions and 1 deletions

View file

@ -0,0 +1,5 @@
pr: 89473
summary: Add source fallback support for `match_only_text` mapped type
area: Mapping
type: enhancement
issues: []

View file

@ -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:

View file

@ -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");
}

View file

@ -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);
}
}