org.elasticsearch.action.search.FetchLookupFieldsPhase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* 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.action.search;
import org.apache.logging.log4j.util.Strings;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.common.util.Maps;
import org.elasticsearch.common.util.concurrent.AtomicArray;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchPhaseResult;
import org.elasticsearch.search.fetch.subphase.LookupField;
import org.elasticsearch.search.internal.InternalSearchResponse;
import org.elasticsearch.transport.RemoteClusterService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Asynchronously resolves {@link LookupField} that are specified {@link DocumentField#getLookupFields()}
*
* @see org.elasticsearch.index.mapper.LookupRuntimeFieldType
*/
final class FetchLookupFieldsPhase extends SearchPhase {
private final SearchPhaseContext context;
private final InternalSearchResponse searchResponse;
private final AtomicArray queryResults;
FetchLookupFieldsPhase(SearchPhaseContext context, InternalSearchResponse searchResponse, AtomicArray queryResults) {
super("fetch_lookup_fields");
this.context = context;
this.searchResponse = searchResponse;
this.queryResults = queryResults;
}
private record Cluster(String clusterAlias, List hitsWithLookupFields, List lookupFields) {
}
private static List groupLookupFieldsByClusterAlias(InternalSearchResponse response) {
final Map> perClusters = new HashMap<>();
for (SearchHit hit : response.hits.getHits()) {
String clusterAlias = hit.getClusterAlias() != null ? hit.getClusterAlias() : RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY;
if (hit.hasLookupFields()) {
perClusters.computeIfAbsent(clusterAlias, k -> new ArrayList<>()).add(hit);
}
}
final List clusters = new ArrayList<>(perClusters.size());
for (Map.Entry> e : perClusters.entrySet()) {
final List lookupFields = e.getValue()
.stream()
.flatMap(h -> h.getDocumentFields().values().stream())
.flatMap(doc -> doc.getLookupFields().stream())
.distinct()
.toList();
clusters.add(new Cluster(e.getKey(), e.getValue(), lookupFields));
}
return clusters;
}
@Override
public void run() {
final List clusters = groupLookupFieldsByClusterAlias(searchResponse);
if (clusters.isEmpty()) {
context.sendSearchResponse(searchResponse, queryResults);
return;
}
final MultiSearchRequest multiSearchRequest = new MultiSearchRequest();
for (Cluster cluster : clusters) {
// Do not prepend the clusterAlias to the targetIndex if the search request is already on the remote cluster.
final String clusterAlias = context.getRequest().getLocalClusterAlias() == null ? cluster.clusterAlias : null;
assert Strings.isEmpty(clusterAlias) || TransportSearchAction.shouldMinimizeRoundtrips(context.getRequest()) == false
: "lookup across clusters only if [ccs_minimize_roundtrips] is disabled";
for (LookupField lookupField : cluster.lookupFields) {
final SearchRequest searchRequest = lookupField.toSearchRequest(clusterAlias);
searchRequest.setCcsMinimizeRoundtrips(false);
multiSearchRequest.add(searchRequest);
}
}
context.getSearchTransport().sendExecuteMultiSearch(multiSearchRequest, context.getTask(), new ActionListener<>() {
@Override
public void onResponse(MultiSearchResponse items) {
Exception failure = null;
int index = 0;
for (Cluster cluster : clusters) {
final Map> lookupResults = Maps.newMapWithExpectedSize(cluster.lookupFields.size());
for (LookupField lookupField : cluster.lookupFields) {
final MultiSearchResponse.Item item = items.getResponses()[index];
if (item.isFailure()) {
failure = ExceptionsHelper.useOrSuppress(failure, item.getFailure());
} else if (failure == null) {
final List