private InternalSearchHit createSearchHit( SearchContext context, FieldsVisitor fieldsVisitor, int docId, int subDocId, List<String> extractFieldNames, LeafReaderContext subReaderContext) { loadStoredFields(context, subReaderContext, fieldsVisitor, subDocId); fieldsVisitor.postProcess(context.mapperService()); Map<String, SearchHitField> searchFields = null; if (!fieldsVisitor.fields().isEmpty()) { searchFields = new HashMap<>(fieldsVisitor.fields().size()); for (Map.Entry<String, List<Object>> entry : fieldsVisitor.fields().entrySet()) { searchFields.put( entry.getKey(), new InternalSearchHitField(entry.getKey(), entry.getValue())); } } DocumentMapper documentMapper = context.mapperService().documentMapper(fieldsVisitor.uid().type()); Text typeText; if (documentMapper == null) { typeText = new StringAndBytesText(fieldsVisitor.uid().type()); } else { typeText = documentMapper.typeText(); } InternalSearchHit searchHit = new InternalSearchHit(docId, fieldsVisitor.uid().id(), typeText, searchFields); // go over and extract fields that are not mapped / stored 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 { // 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); loadStoredFields(context, subReaderContext, rootFieldsVisitor, rootSubDocId); rootFieldsVisitor.postProcess(context.mapperService()); Map<String, SearchHitField> searchFields = getSearchFields(context, nestedSubDocId, loadAllStored, fieldNames, subReaderContext); DocumentMapper documentMapper = context.mapperService().documentMapper(rootFieldsVisitor.uid().type()); SourceLookup sourceLookup = context.lookup().source(); sourceLookup.setSegmentAndDocument(subReaderContext, nestedSubDocId); ObjectMapper nestedObjectMapper = documentMapper.findNestedObjectMapper(nestedSubDocId, context, subReaderContext); assert nestedObjectMapper != null; InternalSearchHit.InternalNestedIdentity nestedIdentity = getInternalNestedIdentity( context, nestedSubDocId, subReaderContext, documentMapper, nestedObjectMapper); BytesReference source = rootFieldsVisitor.source(); if (source != null) { Tuple<XContentType, Map<String, Object>> tuple = XContentHelper.convertToMap(source, true); Map<String, Object> sourceAsMap = tuple.v2(); List<Map<String, Object>> nestedParsedSource; SearchHit.NestedIdentity nested = nestedIdentity; do { Object extractedValue = XContentMapValues.extractValue(nested.getField().string(), sourceAsMap); if (extractedValue == null) { // The nested objects may not exist in the _source, because it was filtered because of // _source filtering break; } else if (extractedValue instanceof List) { // nested field has an array value in the _source nestedParsedSource = (List<Map<String, Object>>) extractedValue; } else if (extractedValue instanceof Map) { // nested field has an object value in the _source. This just means the nested field has // just one inner object, which is valid, but uncommon. nestedParsedSource = ImmutableList.of((Map<String, Object>) extractedValue); } else { throw new IllegalStateException("extracted source isn't an object or an array"); } sourceAsMap = nestedParsedSource.get(nested.getOffset()); nested = nested.getChild(); } while (nested != null); context.lookup().source().setSource(sourceAsMap); XContentType contentType = tuple.v1(); BytesReference nestedSource = contentBuilder(contentType).map(sourceAsMap).bytes(); context.lookup().source().setSource(nestedSource); context.lookup().source().setSourceContentType(contentType); } 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; }
public static InternalSearchHit readSearchHit( StreamInput in, InternalSearchHits.StreamContext context) throws IOException { InternalSearchHit hit = new InternalSearchHit(); hit.readFrom(in, context); return hit; }
public InternalSearchResponse merge( ScoreDoc[] sortedDocs, AtomicArray<? extends QuerySearchResultProvider> queryResultsArr, AtomicArray<? extends FetchSearchResultProvider> fetchResultsArr) { List<? extends AtomicArray.Entry<? extends QuerySearchResultProvider>> queryResults = queryResultsArr.asList(); List<? extends AtomicArray.Entry<? extends FetchSearchResultProvider>> fetchResults = fetchResultsArr.asList(); if (queryResults.isEmpty()) { return InternalSearchResponse.empty(); } QuerySearchResult firstResult = queryResults.get(0).value.queryResult(); boolean sorted = false; int sortScoreIndex = -1; if (firstResult.topDocs() instanceof TopFieldDocs) { sorted = true; TopFieldDocs fieldDocs = (TopFieldDocs) firstResult.queryResult().topDocs(); for (int i = 0; i < fieldDocs.fields.length; i++) { if (fieldDocs.fields[i].getType() == SortField.Type.SCORE) { sortScoreIndex = i; } } } // merge facets InternalFacets facets = null; if (!queryResults.isEmpty()) { // we rely on the fact that the order of facets is the same on all query results if (firstResult.facets() != null && firstResult.facets().facets() != null && !firstResult.facets().facets().isEmpty()) { List<Facet> aggregatedFacets = Lists.newArrayList(); List<Facet> namedFacets = Lists.newArrayList(); for (Facet facet : firstResult.facets()) { // aggregate each facet name into a single list, and aggregate it namedFacets.clear(); for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : queryResults) { for (Facet facet1 : entry.value.queryResult().facets()) { if (facet.getName().equals(facet1.getName())) { namedFacets.add(facet1); } } } if (!namedFacets.isEmpty()) { Facet aggregatedFacet = ((InternalFacet) namedFacets.get(0)) .reduce(new InternalFacet.ReduceContext(cacheRecycler, namedFacets)); aggregatedFacets.add(aggregatedFacet); } } facets = new InternalFacets(aggregatedFacets); } } // count the total (we use the query result provider here, since we might not get any hits (we // scrolled past them)) long totalHits = 0; float maxScore = Float.NEGATIVE_INFINITY; boolean timedOut = false; Boolean terminatedEarly = null; for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : queryResults) { QuerySearchResult result = entry.value.queryResult(); if (result.searchTimedOut()) { timedOut = true; } if (result.terminatedEarly() != null) { if (terminatedEarly == null) { terminatedEarly = result.terminatedEarly(); } else if (result.terminatedEarly()) { terminatedEarly = true; } } totalHits += result.topDocs().totalHits; if (!Float.isNaN(result.topDocs().getMaxScore())) { maxScore = Math.max(maxScore, result.topDocs().getMaxScore()); } } if (Float.isInfinite(maxScore)) { maxScore = Float.NaN; } // clean the fetch counter for (AtomicArray.Entry<? extends FetchSearchResultProvider> entry : fetchResults) { entry.value.fetchResult().initCounter(); } // merge hits List<InternalSearchHit> hits = new ArrayList<>(); if (!fetchResults.isEmpty()) { for (ScoreDoc shardDoc : sortedDocs) { FetchSearchResultProvider fetchResultProvider = fetchResultsArr.get(shardDoc.shardIndex); if (fetchResultProvider == null) { continue; } FetchSearchResult fetchResult = fetchResultProvider.fetchResult(); int index = fetchResult.counterGetAndIncrement(); if (index < fetchResult.hits().internalHits().length) { InternalSearchHit searchHit = fetchResult.hits().internalHits()[index]; searchHit.score(shardDoc.score); searchHit.shard(fetchResult.shardTarget()); if (sorted) { FieldDoc fieldDoc = (FieldDoc) shardDoc; searchHit.sortValues(fieldDoc.fields); if (sortScoreIndex != -1) { searchHit.score(((Number) fieldDoc.fields[sortScoreIndex]).floatValue()); } } hits.add(searchHit); } } } // merge suggest results Suggest suggest = null; if (!queryResults.isEmpty()) { final Map<String, List<Suggest.Suggestion>> groupedSuggestions = new HashMap<>(); boolean hasSuggestions = false; for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : queryResults) { Suggest shardResult = entry.value.queryResult().queryResult().suggest(); if (shardResult == null) { continue; } hasSuggestions = true; Suggest.group(groupedSuggestions, shardResult); } suggest = hasSuggestions ? new Suggest(Suggest.Fields.SUGGEST, Suggest.reduce(groupedSuggestions)) : null; } // merge addAggregation InternalAggregations aggregations = null; if (!queryResults.isEmpty()) { if (firstResult.aggregations() != null && firstResult.aggregations().asList() != null) { List<InternalAggregations> aggregationsList = new ArrayList<>(queryResults.size()); for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : queryResults) { aggregationsList.add((InternalAggregations) entry.value.queryResult().aggregations()); } aggregations = InternalAggregations.reduce( aggregationsList, new ReduceContext(null, bigArrays, scriptService)); } } InternalSearchHits searchHits = new InternalSearchHits( hits.toArray(new InternalSearchHit[hits.size()]), totalHits, maxScore); return new InternalSearchResponse( searchHits, facets, aggregations, suggest, timedOut, terminatedEarly); }
private boolean isChildHit(InternalSearchHit hit) { DocumentMapper hitDocumentMapper = mapperService.documentMapper(hit.type()); return documentMapper.type().equals(hitDocumentMapper.parentFieldMapper().type()); }
private boolean isParentHit(InternalSearchHit hit) { return hit.type().equals(documentMapper.parentFieldMapper().type()); }