|
@@ -30,12 +30,12 @@ import org.elasticsearch.common.bytes.BytesReference;
|
|
|
import org.elasticsearch.common.collect.Tuple;
|
|
|
import org.elasticsearch.common.inject.Inject;
|
|
|
import org.elasticsearch.common.lucene.search.Queries;
|
|
|
+import org.elasticsearch.common.regex.Regex;
|
|
|
import org.elasticsearch.common.text.StringAndBytesText;
|
|
|
import org.elasticsearch.common.text.Text;
|
|
|
import org.elasticsearch.common.xcontent.XContentHelper;
|
|
|
import org.elasticsearch.common.xcontent.XContentType;
|
|
|
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
|
|
-import org.elasticsearch.index.fieldvisitor.AllFieldsVisitor;
|
|
|
import org.elasticsearch.index.fieldvisitor.CustomFieldsVisitor;
|
|
|
import org.elasticsearch.index.fieldvisitor.FieldsVisitor;
|
|
|
import org.elasticsearch.index.mapper.DocumentMapper;
|
|
@@ -55,13 +55,7 @@ import org.elasticsearch.search.internal.SearchContext;
|
|
|
import org.elasticsearch.search.lookup.SourceLookup;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Collections;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.HashSet;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.Set;
|
|
|
+import java.util.*;
|
|
|
|
|
|
import static java.util.Collections.unmodifiableMap;
|
|
|
import static org.elasticsearch.common.xcontent.XContentFactory.contentBuilder;
|
|
@@ -98,9 +92,7 @@ public class FetchPhase implements SearchPhase {
|
|
|
public void execute(SearchContext context) {
|
|
|
FieldsVisitor fieldsVisitor;
|
|
|
Set<String> fieldNames = null;
|
|
|
- List<String> extractFieldNames = null;
|
|
|
-
|
|
|
- boolean loadAllStored = false;
|
|
|
+ List<String> fieldNamePatterns = null;
|
|
|
if (!context.hasFieldNames()) {
|
|
|
// no fields specified, default to return source if no explicit indication
|
|
|
if (!context.hasScriptFields() && !context.hasFetchSourceContext()) {
|
|
@@ -111,10 +103,6 @@ public class FetchPhase implements SearchPhase {
|
|
|
fieldsVisitor = new FieldsVisitor(context.sourceRequested());
|
|
|
} else {
|
|
|
for (String fieldName : context.fieldNames()) {
|
|
|
- if (fieldName.equals("*")) {
|
|
|
- loadAllStored = true;
|
|
|
- continue;
|
|
|
- }
|
|
|
if (fieldName.equals(SourceFieldMapper.NAME)) {
|
|
|
if (context.hasFetchSourceContext()) {
|
|
|
context.fetchSourceContext().fetchSource(true);
|
|
@@ -123,32 +111,28 @@ public class FetchPhase implements SearchPhase {
|
|
|
}
|
|
|
continue;
|
|
|
}
|
|
|
- MappedFieldType fieldType = context.smartNameFieldType(fieldName);
|
|
|
- if (fieldType == null) {
|
|
|
- // Only fail if we know it is a object field, missing paths / fields shouldn't fail.
|
|
|
- if (context.getObjectMapper(fieldName) != null) {
|
|
|
- throw new IllegalArgumentException("field [" + fieldName + "] isn't a leaf field");
|
|
|
+ if (Regex.isSimpleMatchPattern(fieldName)) {
|
|
|
+ if (fieldNamePatterns == null) {
|
|
|
+ fieldNamePatterns = new ArrayList<>();
|
|
|
+ }
|
|
|
+ fieldNamePatterns.add(fieldName);
|
|
|
+ } else {
|
|
|
+ MappedFieldType fieldType = context.smartNameFieldType(fieldName);
|
|
|
+ if (fieldType == null) {
|
|
|
+ // Only fail if we know it is a object field, missing paths / fields shouldn't fail.
|
|
|
+ if (context.getObjectMapper(fieldName) != null) {
|
|
|
+ throw new IllegalArgumentException("field [" + fieldName + "] isn't a leaf field");
|
|
|
+ }
|
|
|
}
|
|
|
- } else if (fieldType.stored()) {
|
|
|
if (fieldNames == null) {
|
|
|
fieldNames = new HashSet<>();
|
|
|
}
|
|
|
- fieldNames.add(fieldType.names().indexName());
|
|
|
- } else {
|
|
|
- if (extractFieldNames == null) {
|
|
|
- extractFieldNames = new ArrayList<>();
|
|
|
- }
|
|
|
- extractFieldNames.add(fieldName);
|
|
|
+ fieldNames.add(fieldName);
|
|
|
}
|
|
|
}
|
|
|
- if (loadAllStored) {
|
|
|
- fieldsVisitor = new AllFieldsVisitor(); // load everything, including _source
|
|
|
- } else if (fieldNames != null) {
|
|
|
- boolean loadSource = extractFieldNames != null || context.sourceRequested();
|
|
|
- fieldsVisitor = new CustomFieldsVisitor(fieldNames, loadSource);
|
|
|
- } else {
|
|
|
- fieldsVisitor = new FieldsVisitor(extractFieldNames != null || context.sourceRequested());
|
|
|
- }
|
|
|
+ boolean loadSource = context.sourceRequested();
|
|
|
+ fieldsVisitor = new CustomFieldsVisitor(fieldNames == null ? Collections.emptySet() : fieldNames,
|
|
|
+ fieldNamePatterns == null ? Collections.emptyList() : fieldNamePatterns, loadSource);
|
|
|
}
|
|
|
|
|
|
InternalSearchHit[] hits = new InternalSearchHit[context.docIdsToLoadSize()];
|
|
@@ -163,9 +147,9 @@ public class FetchPhase implements SearchPhase {
|
|
|
try {
|
|
|
int rootDocId = findRootDocumentIfNested(context, subReaderContext, subDocId);
|
|
|
if (rootDocId != -1) {
|
|
|
- searchHit = createNestedSearchHit(context, docId, subDocId, rootDocId, extractFieldNames, loadAllStored, fieldNames, subReaderContext);
|
|
|
+ searchHit = createNestedSearchHit(context, docId, subDocId, rootDocId, fieldNames, fieldNamePatterns, subReaderContext);
|
|
|
} else {
|
|
|
- searchHit = createSearchHit(context, fieldsVisitor, docId, subDocId, extractFieldNames, subReaderContext);
|
|
|
+ searchHit = createSearchHit(context, fieldsVisitor, docId, subDocId, subReaderContext);
|
|
|
}
|
|
|
} catch (IOException e) {
|
|
|
throw ExceptionsHelper.convertToElastic(e);
|
|
@@ -199,7 +183,7 @@ public class FetchPhase implements SearchPhase {
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
- private InternalSearchHit createSearchHit(SearchContext context, FieldsVisitor fieldsVisitor, int docId, int subDocId, List<String> extractFieldNames, LeafReaderContext subReaderContext) {
|
|
|
+ private InternalSearchHit createSearchHit(SearchContext context, FieldsVisitor fieldsVisitor, int docId, int subDocId, LeafReaderContext subReaderContext) {
|
|
|
loadStoredFields(context, subReaderContext, fieldsVisitor, subDocId);
|
|
|
fieldsVisitor.postProcess(context.mapperService());
|
|
|
|
|
@@ -219,45 +203,24 @@ public class FetchPhase implements SearchPhase {
|
|
|
typeText = documentMapper.typeText();
|
|
|
}
|
|
|
InternalSearchHit searchHit = new InternalSearchHit(docId, fieldsVisitor.uid().id(), typeText, searchFields);
|
|
|
-
|
|
|
- // go over and extract fields that are not mapped / stored
|
|
|
+ // Set _source if requested.
|
|
|
SourceLookup sourceLookup = context.lookup().source();
|
|
|
sourceLookup.setSegmentAndDocument(subReaderContext, subDocId);
|
|
|
if (fieldsVisitor.source() != null) {
|
|
|
sourceLookup.setSource(fieldsVisitor.source());
|
|
|
}
|
|
|
- if (extractFieldNames != null) {
|
|
|
- for (String extractFieldName : extractFieldNames) {
|
|
|
- List<Object> values = context.lookup().source().extractRawValues(extractFieldName);
|
|
|
- if (!values.isEmpty()) {
|
|
|
- if (searchHit.fieldsOrNull() == null) {
|
|
|
- searchHit.fields(new HashMap<String, SearchHitField>(2));
|
|
|
- }
|
|
|
-
|
|
|
- SearchHitField hitField = searchHit.fields().get(extractFieldName);
|
|
|
- if (hitField == null) {
|
|
|
- hitField = new InternalSearchHitField(extractFieldName, new ArrayList<>(2));
|
|
|
- searchHit.fields().put(extractFieldName, hitField);
|
|
|
- }
|
|
|
- for (Object value : values) {
|
|
|
- hitField.values().add(value);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
return searchHit;
|
|
|
}
|
|
|
|
|
|
- private InternalSearchHit createNestedSearchHit(SearchContext context, int nestedTopDocId, int nestedSubDocId, int rootSubDocId, List<String> extractFieldNames, boolean loadAllStored, Set<String> fieldNames, LeafReaderContext subReaderContext) throws IOException {
|
|
|
+ private InternalSearchHit createNestedSearchHit(SearchContext context, int nestedTopDocId, int nestedSubDocId, int rootSubDocId, Set<String> fieldNames, List<String> fieldNamePatterns, LeafReaderContext subReaderContext) throws IOException {
|
|
|
// Also if highlighting is requested on nested documents we need to fetch the _source from the root document,
|
|
|
// otherwise highlighting will attempt to fetch the _source from the nested doc, which will fail,
|
|
|
// because the entire _source is only stored with the root document.
|
|
|
- final FieldsVisitor rootFieldsVisitor = new FieldsVisitor(context.sourceRequested() || extractFieldNames != null || context.highlight() != null);
|
|
|
+ final FieldsVisitor rootFieldsVisitor = new FieldsVisitor(context.sourceRequested() || context.highlight() != null);
|
|
|
loadStoredFields(context, subReaderContext, rootFieldsVisitor, rootSubDocId);
|
|
|
rootFieldsVisitor.postProcess(context.mapperService());
|
|
|
|
|
|
- Map<String, SearchHitField> searchFields = getSearchFields(context, nestedSubDocId, loadAllStored, fieldNames, subReaderContext);
|
|
|
+ Map<String, SearchHitField> searchFields = getSearchFields(context, nestedSubDocId, fieldNames, fieldNamePatterns, subReaderContext);
|
|
|
DocumentMapper documentMapper = context.mapperService().documentMapper(rootFieldsVisitor.uid().type());
|
|
|
SourceLookup sourceLookup = context.lookup().source();
|
|
|
sourceLookup.setSegmentAndDocument(subReaderContext, nestedSubDocId);
|
|
@@ -299,39 +262,14 @@ public class FetchPhase implements SearchPhase {
|
|
|
}
|
|
|
|
|
|
InternalSearchHit searchHit = new InternalSearchHit(nestedTopDocId, rootFieldsVisitor.uid().id(), documentMapper.typeText(), nestedIdentity, searchFields);
|
|
|
- if (extractFieldNames != null) {
|
|
|
- for (String extractFieldName : extractFieldNames) {
|
|
|
- List<Object> values = context.lookup().source().extractRawValues(extractFieldName);
|
|
|
- if (!values.isEmpty()) {
|
|
|
- if (searchHit.fieldsOrNull() == null) {
|
|
|
- searchHit.fields(new HashMap<String, SearchHitField>(2));
|
|
|
- }
|
|
|
-
|
|
|
- SearchHitField hitField = searchHit.fields().get(extractFieldName);
|
|
|
- if (hitField == null) {
|
|
|
- hitField = new InternalSearchHitField(extractFieldName, new ArrayList<>(2));
|
|
|
- searchHit.fields().put(extractFieldName, hitField);
|
|
|
- }
|
|
|
- for (Object value : values) {
|
|
|
- hitField.values().add(value);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
return searchHit;
|
|
|
}
|
|
|
|
|
|
- private Map<String, SearchHitField> getSearchFields(SearchContext context, int nestedSubDocId, boolean loadAllStored, Set<String> fieldNames, LeafReaderContext subReaderContext) {
|
|
|
+ private Map<String, SearchHitField> getSearchFields(SearchContext context, int nestedSubDocId, Set<String> fieldNames, List<String> fieldNamePatterns, LeafReaderContext subReaderContext) {
|
|
|
Map<String, SearchHitField> searchFields = null;
|
|
|
if (context.hasFieldNames() && !context.fieldNames().isEmpty()) {
|
|
|
- FieldsVisitor nestedFieldsVisitor = null;
|
|
|
- if (loadAllStored) {
|
|
|
- nestedFieldsVisitor = new AllFieldsVisitor();
|
|
|
- } else if (fieldNames != null) {
|
|
|
- nestedFieldsVisitor = new CustomFieldsVisitor(fieldNames, false);
|
|
|
- }
|
|
|
-
|
|
|
+ FieldsVisitor nestedFieldsVisitor = new CustomFieldsVisitor(fieldNames == null ? Collections.emptySet() : fieldNames,
|
|
|
+ fieldNamePatterns == null ? Collections.emptyList() : fieldNamePatterns, false);
|
|
|
if (nestedFieldsVisitor != null) {
|
|
|
loadStoredFields(context, subReaderContext, nestedFieldsVisitor, nestedSubDocId);
|
|
|
nestedFieldsVisitor.postProcess(context.mapperService());
|