21
21
22
22
import org .apache .lucene .index .LeafReaderContext ;
23
23
import org .elasticsearch .common .document .DocumentField ;
24
+ import org .elasticsearch .common .regex .Regex ;
24
25
import org .elasticsearch .index .mapper .MappedFieldType ;
25
26
import org .elasticsearch .index .mapper .ValueFetcher ;
26
27
import org .elasticsearch .index .query .QueryShardContext ;
30
31
import java .io .IOException ;
31
32
import java .util .ArrayList ;
32
33
import java .util .Collection ;
34
+ import java .util .Collections ;
33
35
import java .util .HashMap ;
36
+ import java .util .HashSet ;
34
37
import java .util .List ;
35
38
import java .util .Map ;
36
39
import java .util .Set ;
42
45
public class FieldFetcher {
43
46
public static FieldFetcher create (QueryShardContext context ,
44
47
SearchLookup searchLookup ,
45
- Collection <FieldAndFormat > fieldAndFormats ) {
48
+ Collection <FieldAndFormat > fieldAndFormats ,
49
+ boolean includeUnmapped ) {
46
50
47
51
List <FieldContext > fieldContexts = new ArrayList <>();
52
+ List <String > originalPattern = new ArrayList <>();
53
+ List <String > mappedFields = new ArrayList <>();
48
54
49
55
for (FieldAndFormat fieldAndFormat : fieldAndFormats ) {
50
56
String fieldPattern = fieldAndFormat .field ;
51
57
String format = fieldAndFormat .format ;
52
58
53
59
Collection <String > concreteFields = context .simpleMatchToIndexNames (fieldPattern );
60
+ originalPattern .add (fieldAndFormat .field );
54
61
for (String field : concreteFields ) {
55
62
MappedFieldType ft = context .getFieldType (field );
56
63
if (ft == null || context .isMetadataField (field )) {
57
64
continue ;
58
65
}
59
66
ValueFetcher valueFetcher = ft .valueFetcher (context , searchLookup , format );
67
+ mappedFields .add (field );
60
68
fieldContexts .add (new FieldContext (field , valueFetcher ));
61
69
}
62
70
}
63
71
64
- return new FieldFetcher (fieldContexts );
72
+ return new FieldFetcher (fieldContexts , originalPattern , mappedFields , includeUnmapped );
65
73
}
66
74
67
75
private final List <FieldContext > fieldContexts ;
68
-
69
- private FieldFetcher (List <FieldContext > fieldContexts ) {
76
+ private final List <String > originalPattern ;
77
+ private final List <String > mappedFields ;
78
+ private final boolean includeUnmapped ;
79
+
80
+ private FieldFetcher (
81
+ List <FieldContext > fieldContexts ,
82
+ List <String > originalPattern ,
83
+ List <String > mappedFields ,
84
+ boolean includeUnmapped
85
+ ) {
70
86
this .fieldContexts = fieldContexts ;
87
+ this .originalPattern = originalPattern ;
88
+ this .mappedFields = mappedFields ;
89
+ this .includeUnmapped = includeUnmapped ;
71
90
}
72
91
73
92
public Map <String , DocumentField > fetch (SourceLookup sourceLookup , Set <String > ignoredFields ) throws IOException {
@@ -85,9 +104,60 @@ public Map<String, DocumentField> fetch(SourceLookup sourceLookup, Set<String> i
85
104
documentFields .put (field , new DocumentField (field , parsedValues ));
86
105
}
87
106
}
107
+ if (includeUnmapped ) {
108
+ // also look up unmapped fields from source
109
+ Set <String > allSourcePaths = extractAllLeafPaths (sourceLookup .loadSourceIfNeeded ());
110
+ for (String fetchFieldPattern : originalPattern ) {
111
+ if (Regex .isSimpleMatchPattern (fetchFieldPattern ) == false ) {
112
+ // if pattern has no wildcard, simply look up field if not already present
113
+ if (allSourcePaths .contains (fetchFieldPattern )) {
114
+ addValueFromSource (sourceLookup , fetchFieldPattern , documentFields );
115
+ }
116
+ } else {
117
+ for (String singlePath : allSourcePaths ) {
118
+ if (Regex .simpleMatch (fetchFieldPattern , singlePath )) {
119
+ addValueFromSource (sourceLookup , singlePath , documentFields );
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
88
125
return documentFields ;
89
126
}
90
127
128
+ private void addValueFromSource (SourceLookup sourceLookup , String path , Map <String , DocumentField > documentFields ) {
129
+ // checking mapped fields here again to avoid adding from _source where some value was already added or ignored on purpose before
130
+ if (mappedFields .contains (path ) == false ) {
131
+ Object object = sourceLookup .extractValue (path , null );
132
+ DocumentField f ;
133
+ if (object instanceof List ) {
134
+ f = new DocumentField (path , (List ) object );
135
+ } else {
136
+ f = new DocumentField (path , Collections .singletonList (object ));
137
+ }
138
+ if (f .getValue () != null ) {
139
+ documentFields .put (path , f );
140
+ }
141
+ }
142
+ }
143
+
144
+ static Set <String > extractAllLeafPaths (Map <String , Object > map ) {
145
+ Set <String > allPaths = new HashSet <>();
146
+ collectAllPaths (allPaths , "" , map );
147
+ return allPaths ;
148
+ }
149
+
150
+ private static void collectAllPaths (Set <String > allPaths , String prefix , Map <String , Object > source ) {
151
+ for (String key : source .keySet ()) {
152
+ Object value = source .get (key );
153
+ if (value instanceof Map ) {
154
+ collectAllPaths (allPaths , prefix + key + "." , (Map <String , Object >) value );
155
+ } else {
156
+ allPaths .add (prefix + key );
157
+ }
158
+ }
159
+ }
160
+
91
161
public void setNextReader (LeafReaderContext readerContext ) {
92
162
for (FieldContext field : fieldContexts ) {
93
163
field .valueFetcher .setNextReader (readerContext );
0 commit comments