Skip to content

Commit 1aa43ec

Browse files
authored
Add text field support in the Painless scripting fields API (elastic#89396)
This change adds access to mapped text fields via the Painless scripting fields API. The values returned from a text field via the scripting fields API always use source as described by (elastic#81246). Access via the old-style through doc will still depend on field data, so there is no change and avoids bwc issues.
1 parent cbea639 commit 1aa43ec

File tree

5 files changed

+335
-24
lines changed

5 files changed

+335
-24
lines changed

docs/changelog/89396.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 89396
2+
summary: Add text field support in the Painless scripting fields API
3+
area: Mapping
4+
type: enhancement
5+
issues: []

modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ setup:
7070
type: scaled_float
7171
scaling_factor: 100
7272
doc_values: false
73+
text:
74+
type: text
75+
fielddata: true
76+
text_no_field_data:
77+
type: text
7378
token_count:
7479
type: token_count
7580
analyzer: standard
@@ -110,6 +115,8 @@ setup:
110115
half_float_no_doc_values: 3.140625
111116
scaled_float: 3.14
112117
scaled_float_no_doc_values: 3.14
118+
text: "Lots of text."
119+
text_no_field_data: "Lots of text."
113120
token_count: count all these words please
114121

115122
- do:
@@ -150,6 +157,8 @@ setup:
150157
half_float_no_doc_values: [2.234, 1.123]
151158
scaled_float: [-3.5, 2.5]
152159
scaled_float_no_doc_values: [2.5, -3.5]
160+
text: ["Lots of text.", "even more text", "SOOOOO much text"]
161+
text_no_field_data: ["Lots of text.", "even more text", "SOOOOO much text"]
153162

154163

155164
- do:
@@ -2719,6 +2728,264 @@ setup:
27192728
source: "int value = field('dne').get(1, 1); value"
27202729
- match: { hits.hits.0.fields.field.0: 1 }
27212730

2731+
---
2732+
"text":
2733+
- do:
2734+
search:
2735+
rest_total_hits_as_int: true
2736+
body:
2737+
query: { term: { _id: "1" } }
2738+
script_fields:
2739+
field:
2740+
script:
2741+
source: "doc['text'].get(0)"
2742+
- match: { hits.hits.0.fields.field.0: lots }
2743+
2744+
- do:
2745+
search:
2746+
rest_total_hits_as_int: true
2747+
body:
2748+
query: { term: { _id: "1" } }
2749+
script_fields:
2750+
field:
2751+
script:
2752+
source: "doc['text'].value"
2753+
- match: { hits.hits.0.fields.field.0: lots }
2754+
2755+
- do:
2756+
search:
2757+
rest_total_hits_as_int: true
2758+
body:
2759+
sort: [ { rank: asc } ]
2760+
script_fields:
2761+
field:
2762+
script:
2763+
source: "field('text').get('')"
2764+
- match: { hits.hits.0.fields.field.0: "Lots of text." }
2765+
- match: { hits.hits.1.fields.field.0: "" }
2766+
- match: { hits.hits.2.fields.field.0: "Lots of text." }
2767+
2768+
- do:
2769+
search:
2770+
rest_total_hits_as_int: true
2771+
body:
2772+
sort: [ { rank: asc } ]
2773+
script_fields:
2774+
field:
2775+
script:
2776+
source: "/* avoid yaml stash */ $('text', '')"
2777+
- match: { hits.hits.0.fields.field.0: "Lots of text." }
2778+
- match: { hits.hits.1.fields.field.0: "" }
2779+
- match: { hits.hits.2.fields.field.0: "Lots of text." }
2780+
2781+
- do:
2782+
search:
2783+
rest_total_hits_as_int: true
2784+
body:
2785+
sort: [ { rank: asc } ]
2786+
script_fields:
2787+
field:
2788+
script:
2789+
source: "String defaultText = 'default text'; field('text').get(defaultText)"
2790+
- match: { hits.hits.0.fields.field.0: "Lots of text." }
2791+
- match: { hits.hits.1.fields.field.0: "default text" }
2792+
- match: { hits.hits.2.fields.field.0: "Lots of text." }
2793+
2794+
- do:
2795+
search:
2796+
rest_total_hits_as_int: true
2797+
body:
2798+
sort: [ { rank: asc } ]
2799+
script_fields:
2800+
field:
2801+
script:
2802+
source: "String defaultText = 'default text'; $('text', defaultText)"
2803+
- match: { hits.hits.0.fields.field.0: "Lots of text." }
2804+
- match: { hits.hits.1.fields.field.0: "default text" }
2805+
- match: { hits.hits.2.fields.field.0: "Lots of text." }
2806+
2807+
- do:
2808+
search:
2809+
rest_total_hits_as_int: true
2810+
body:
2811+
sort: [ { rank: asc } ]
2812+
script_fields:
2813+
field:
2814+
script:
2815+
source: "field('text').get(1, '')"
2816+
- match: { hits.hits.0.fields.field.0: "" }
2817+
- match: { hits.hits.1.fields.field.0: "" }
2818+
- match: { hits.hits.2.fields.field.0: "SOOOOO much text" }
2819+
2820+
- do:
2821+
search:
2822+
rest_total_hits_as_int: true
2823+
body:
2824+
sort: [ { rank: asc } ]
2825+
script_fields:
2826+
field:
2827+
script:
2828+
source: "String defaultText = 'default text'; field('text').get(1, defaultText)"
2829+
- match: { hits.hits.0.fields.field.0: "default text" }
2830+
- match: { hits.hits.1.fields.field.0: "default text" }
2831+
- match: { hits.hits.2.fields.field.0: "SOOOOO much text" }
2832+
2833+
- do:
2834+
search:
2835+
rest_total_hits_as_int: true
2836+
body:
2837+
sort: [ { rank: asc } ]
2838+
script_fields:
2839+
field:
2840+
script:
2841+
source: "field('text').get(1, '')"
2842+
- match: { hits.hits.0.fields.field.0: "" }
2843+
- match: { hits.hits.1.fields.field.0: "" }
2844+
- match: { hits.hits.2.fields.field.0: "SOOOOO much text" }
2845+
2846+
- do:
2847+
search:
2848+
rest_total_hits_as_int: true
2849+
body:
2850+
sort: [ { rank: asc } ]
2851+
script_fields:
2852+
field:
2853+
script:
2854+
source: "String cat = ''; for (String s : field('text')) { cat += s; } cat + field('text').size();"
2855+
- match: { hits.hits.0.fields.field.0: "Lots of text.1" }
2856+
- match: { hits.hits.1.fields.field.0: "0" }
2857+
- match: { hits.hits.2.fields.field.0: "Lots of text.SOOOOO much texteven more text3" }
2858+
2859+
---
2860+
"text_no_field_data":
2861+
- do:
2862+
catch: bad_request
2863+
search:
2864+
rest_total_hits_as_int: true
2865+
body:
2866+
query: { term: { _id: "1" } }
2867+
script_fields:
2868+
field:
2869+
script:
2870+
source: "doc['text_no_field_data'].get(0)"
2871+
- match: { error.failed_shards.0.reason.caused_by.type: "illegal_argument_exception" }
2872+
2873+
- do:
2874+
catch: bad_request
2875+
search:
2876+
rest_total_hits_as_int: true
2877+
body:
2878+
query: { term: { _id: "1" } }
2879+
script_fields:
2880+
field:
2881+
script:
2882+
source: "doc['text_no_field_data'].value"
2883+
- match: { error.failed_shards.0.reason.caused_by.type: "illegal_argument_exception" }
2884+
2885+
- do:
2886+
search:
2887+
rest_total_hits_as_int: true
2888+
body:
2889+
sort: [ { rank: asc } ]
2890+
script_fields:
2891+
field:
2892+
script:
2893+
source: "field('text_no_field_data').get('')"
2894+
- match: { hits.hits.0.fields.field.0: "Lots of text." }
2895+
- match: { hits.hits.1.fields.field.0: "" }
2896+
- match: { hits.hits.2.fields.field.0: "Lots of text." }
2897+
2898+
- do:
2899+
search:
2900+
rest_total_hits_as_int: true
2901+
body:
2902+
sort: [ { rank: asc } ]
2903+
script_fields:
2904+
field:
2905+
script:
2906+
source: "/* avoid yaml stash */ $('text_no_field_data', '')"
2907+
- match: { hits.hits.0.fields.field.0: "Lots of text." }
2908+
- match: { hits.hits.1.fields.field.0: "" }
2909+
- match: { hits.hits.2.fields.field.0: "Lots of text." }
2910+
2911+
- do:
2912+
search:
2913+
rest_total_hits_as_int: true
2914+
body:
2915+
sort: [ { rank: asc } ]
2916+
script_fields:
2917+
field:
2918+
script:
2919+
source: "String defaultText = 'default text'; field('text_no_field_data').get(defaultText)"
2920+
- match: { hits.hits.0.fields.field.0: "Lots of text." }
2921+
- match: { hits.hits.1.fields.field.0: "default text" }
2922+
- match: { hits.hits.2.fields.field.0: "Lots of text." }
2923+
2924+
- do:
2925+
search:
2926+
rest_total_hits_as_int: true
2927+
body:
2928+
sort: [ { rank: asc } ]
2929+
script_fields:
2930+
field:
2931+
script:
2932+
source: "String defaultText = 'default text'; $('text_no_field_data', defaultText)"
2933+
- match: { hits.hits.0.fields.field.0: "Lots of text." }
2934+
- match: { hits.hits.1.fields.field.0: "default text" }
2935+
- match: { hits.hits.2.fields.field.0: "Lots of text." }
2936+
2937+
- do:
2938+
search:
2939+
rest_total_hits_as_int: true
2940+
body:
2941+
sort: [ { rank: asc } ]
2942+
script_fields:
2943+
field:
2944+
script:
2945+
source: "field('text_no_field_data').get(1, '')"
2946+
- match: { hits.hits.0.fields.field.0: "" }
2947+
- match: { hits.hits.1.fields.field.0: "" }
2948+
- match: { hits.hits.2.fields.field.0: "SOOOOO much text" }
2949+
2950+
- do:
2951+
search:
2952+
rest_total_hits_as_int: true
2953+
body:
2954+
sort: [ { rank: asc } ]
2955+
script_fields:
2956+
field:
2957+
script:
2958+
source: "String defaultText = 'default text'; field('text_no_field_data').get(1, defaultText)"
2959+
- match: { hits.hits.0.fields.field.0: "default text" }
2960+
- match: { hits.hits.1.fields.field.0: "default text" }
2961+
- match: { hits.hits.2.fields.field.0: "SOOOOO much text" }
2962+
2963+
- do:
2964+
search:
2965+
rest_total_hits_as_int: true
2966+
body:
2967+
sort: [ { rank: asc } ]
2968+
script_fields:
2969+
field:
2970+
script:
2971+
source: "field('text_no_field_data').get(1, '')"
2972+
- match: { hits.hits.0.fields.field.0: "" }
2973+
- match: { hits.hits.1.fields.field.0: "" }
2974+
- match: { hits.hits.2.fields.field.0: "SOOOOO much text" }
2975+
2976+
- do:
2977+
search:
2978+
rest_total_hits_as_int: true
2979+
body:
2980+
sort: [ { rank: asc } ]
2981+
script_fields:
2982+
field:
2983+
script:
2984+
source: "String cat = ''; for (String s : field('text_no_field_data')) { cat += s; } cat + field('text_no_field_data').size();"
2985+
- match: { hits.hits.0.fields.field.0: "Lots of text.1" }
2986+
- match: { hits.hits.1.fields.field.0: "0" }
2987+
- match: { hits.hits.2.fields.field.0: "Lots of text.SOOOOO much texteven more text3" }
2988+
27222989
---
27232990
"version and sequence number":
27242991
- do:

server/src/main/java/org/elasticsearch/index/fielddata/SourceValueFetcherSortedBinaryIndexFieldData.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ public static class SourceValueFetcherSortedBinaryDocValues extends SortedBinary
9191
private final ValueFetcher valueFetcher;
9292
private final SourceLookup sourceLookup;
9393

94-
private SortedSet<Object> values;
95-
private Iterator<Object> iterator;
94+
private final SortedSet<BytesRef> values;
95+
private Iterator<BytesRef> iterator;
9696

9797
public SourceValueFetcherSortedBinaryDocValues(
9898
LeafReaderContext leafReaderContext,
@@ -102,12 +102,19 @@ public SourceValueFetcherSortedBinaryDocValues(
102102
this.leafReaderContext = leafReaderContext;
103103
this.valueFetcher = valueFetcher;
104104
this.sourceLookup = sourceLookup;
105+
106+
values = new TreeSet<>();
105107
}
106108

107109
@Override
108110
public boolean advanceExact(int doc) throws IOException {
109111
sourceLookup.setSegmentAndDocument(leafReaderContext, doc);
110-
values = new TreeSet<>(valueFetcher.fetchValues(sourceLookup, Collections.emptyList()));
112+
values.clear();
113+
114+
for (Object object : valueFetcher.fetchValues(sourceLookup, Collections.emptyList())) {
115+
values.add(new BytesRef(object.toString()));
116+
}
117+
111118
iterator = values.iterator();
112119

113120
return true;
@@ -121,7 +128,7 @@ public int docValueCount() {
121128
@Override
122129
public BytesRef nextValue() throws IOException {
123130
assert iterator.hasNext();
124-
return new BytesRef(iterator.next().toString());
131+
return iterator.next();
125132
}
126133
}
127134
}

0 commit comments

Comments
 (0)