org.springframework.data.elasticsearch.client.erhlc.DocumentAdapters Maven / Gradle / Ivy
Show all versions of spring-data-elasticsearch Show documentation
/*
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.erhlc;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetItemResponse;
import org.elasticsearch.action.get.MultiGetResponse;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.Explanation;
import org.springframework.data.elasticsearch.core.document.NestedMetaData;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.mapping.MappingException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
/**
* Utility class to adapt {@link org.elasticsearch.action.get.GetResponse},
* {@link org.elasticsearch.index.get.GetResult}, {@link org.elasticsearch.action.get.MultiGetResponse}
* {@link org.elasticsearch.search.SearchHit}, {@link org.elasticsearch.common.document.DocumentField} to
* {@link Document}.
*
* @author Mark Paluch
* @author Peter-Josef Meisch
* @author Roman Puchkovskiy
* @author Matt Gilene
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
public final class DocumentAdapters {
private DocumentAdapters() {}
/**
* Create a {@link Document} from {@link GetResponse}.
*
* Returns a {@link Document} using the getResponse if available.
*
* @param getResponse the getResponse {@link GetResponse}.
* @return the adapted {@link Document}, null if getResponse.isExists() returns false.
*/
@Nullable
public static Document from(GetResponse getResponse) {
Assert.notNull(getResponse, "GetResponse must not be null");
if (!getResponse.isExists()) {
return null;
}
if (getResponse.isSourceEmpty()) {
return fromDocumentFields(getResponse, getResponse.getIndex(), getResponse.getId(), getResponse.getVersion(),
getResponse.getSeqNo(), getResponse.getPrimaryTerm());
}
Document document = Document.from(getResponse.getSourceAsMap());
document.setIndex(getResponse.getIndex());
document.setId(getResponse.getId());
document.setVersion(getResponse.getVersion());
document.setSeqNo(getResponse.getSeqNo());
document.setPrimaryTerm(getResponse.getPrimaryTerm());
return document;
}
/**
* Create a {@link Document} from {@link GetResult}.
*
* Returns a {@link Document} using the source if available.
*
* @param source the source {@link GetResult}.
* @return the adapted {@link Document}, null if source.isExists() returns false.
*/
@Nullable
public static Document from(GetResult source) {
Assert.notNull(source, "GetResult must not be null");
if (!source.isExists()) {
return null;
}
if (source.isSourceEmpty()) {
return fromDocumentFields(source, source.getIndex(), source.getId(), source.getVersion(), source.getSeqNo(),
source.getPrimaryTerm());
}
Document document = Document.from(source.getSource());
document.setIndex(source.getIndex());
document.setId(source.getId());
document.setVersion(source.getVersion());
document.setSeqNo(source.getSeqNo());
document.setPrimaryTerm(source.getPrimaryTerm());
return document;
}
/**
* Creates a List of {@link MultiGetItem}s from {@link MultiGetResponse}.
*
* @param source the source {@link MultiGetResponse}, not {@literal null}.
* @return a list of Documents, contains null values for not found Documents.
*/
public static List> from(MultiGetResponse source) {
Assert.notNull(source, "MultiGetResponse must not be null");
return Arrays.stream(source.getResponses()) //
.map(DocumentAdapters::from) //
.collect(Collectors.toList());
}
/**
* Creates a {@link MultiGetItem} from a {@link MultiGetItemResponse}.
*
* @param itemResponse the response, must not be {@literal null}
* @return the MultiGetItem
*/
public static MultiGetItem from(MultiGetItemResponse itemResponse) {
MultiGetItem.Failure failure = ResponseConverter.getFailure(itemResponse);
return MultiGetItem.of(itemResponse.isFailed() ? null : DocumentAdapters.from(itemResponse.getResponse()), failure);
}
/**
* Create a {@link SearchDocument} from {@link SearchHit}.
*
* Returns a {@link SearchDocument} using the source if available.
*
* @param source the source {@link SearchHit}.
* @return the adapted {@link SearchDocument}.
*/
public static SearchDocument from(SearchHit source) {
Assert.notNull(source, "SearchHit must not be null");
Map> highlightFields = new HashMap<>(source.getHighlightFields().entrySet().stream() //
.collect(Collectors.toMap(Map.Entry::getKey,
entry -> Arrays.stream(entry.getValue().getFragments()).map(Text::string).collect(Collectors.toList()))));
Map innerHits = new LinkedHashMap<>();
Map sourceInnerHits = source.getInnerHits();
if (sourceInnerHits != null) {
sourceInnerHits.forEach((name, searchHits) -> innerHits.put(name, SearchDocumentResponseBuilder.from(searchHits,
null, null, null, searchDocument -> CompletableFuture.completedFuture(null))));
}
NestedMetaData nestedMetaData = from(source.getNestedIdentity());
Explanation explanation = from(source.getExplanation());
List matchedQueries = from(source.getMatchedQueries());
BytesReference sourceRef = source.getSourceRef();
Map sourceFields = source.getFields();
if (sourceRef == null || sourceRef.length() == 0) {
return new SearchDocumentAdapter(
fromDocumentFields(source, source.getIndex(), source.getId(), source.getVersion(), source.getSeqNo(),
source.getPrimaryTerm()),
source.getScore(), source.getSortValues(), sourceFields, highlightFields, innerHits, nestedMetaData,
explanation, matchedQueries);
}
Document document = Document.from(source.getSourceAsMap());
document.setIndex(source.getIndex());
document.setId(source.getId());
if (source.getVersion() >= 0) {
document.setVersion(source.getVersion());
}
document.setSeqNo(source.getSeqNo());
document.setPrimaryTerm(source.getPrimaryTerm());
return new SearchDocumentAdapter(document, source.getScore(), source.getSortValues(), sourceFields, highlightFields,
innerHits, nestedMetaData, explanation, matchedQueries);
}
@Nullable
private static Explanation from(@Nullable org.apache.lucene.search.Explanation explanation) {
if (explanation == null) {
return null;
}
List details = new ArrayList<>();
for (org.apache.lucene.search.Explanation detail : explanation.getDetails()) {
details.add(from(detail));
}
return new Explanation(explanation.isMatch(), explanation.getValue().doubleValue(), explanation.getDescription(),
details);
}
@Nullable
private static NestedMetaData from(@Nullable SearchHit.NestedIdentity nestedIdentity) {
if (nestedIdentity == null) {
return null;
}
NestedMetaData child = from(nestedIdentity.getChild());
return NestedMetaData.of(nestedIdentity.getField().string(), nestedIdentity.getOffset(), child);
}
@Nullable
private static List from(@Nullable String[] matchedQueries) {
return matchedQueries == null ? null : Arrays.asList(matchedQueries);
}
/**
* Create an unmodifiable {@link Document} from {@link Iterable} of {@link DocumentField}s.
*
* @param documentFields the {@link DocumentField}s backing the {@link Document}.
* @param index the index where the Document was found
* @param id the document id
* @param version the document version
* @param seqNo the seqNo if the document
* @param primaryTerm the primaryTerm of the document
* @return the adapted {@link Document}.
*/
public static Document fromDocumentFields(Iterable documentFields, String index, String id,
long version, long seqNo, long primaryTerm) {
if (documentFields instanceof Collection) {
return new DocumentFieldAdapter((Collection) documentFields, index, id, version, seqNo,
primaryTerm);
}
List fields = new ArrayList<>();
for (DocumentField documentField : documentFields) {
fields.add(documentField);
}
return new DocumentFieldAdapter(fields, index, id, version, seqNo, primaryTerm);
}
/**
* Adapter for a collection of {@link DocumentField}s.
*/
static class DocumentFieldAdapter implements Document {
private final Collection documentFields;
private final Map documentFieldMap;
private final String index;
private final String id;
private final long version;
private final long seqNo;
private final long primaryTerm;
DocumentFieldAdapter(Collection documentFields, String index, String id, long version, long seqNo,
long primaryTerm) {
this.documentFields = documentFields;
this.documentFieldMap = new LinkedHashMap<>(documentFields.size());
documentFields.forEach(documentField -> documentFieldMap.put(documentField.getName(), documentField));
this.index = index;
this.id = id;
this.version = version;
this.seqNo = seqNo;
this.primaryTerm = primaryTerm;
}
@Override
public String getIndex() {
return index;
}
@Override
public boolean hasId() {
return StringUtils.hasLength(id);
}
@Override
public String getId() {
return this.id;
}
@Override
public boolean hasVersion() {
return this.version >= 0;
}
@Override
public long getVersion() {
if (!hasVersion()) {
throw new IllegalStateException("No version associated with this Document");
}
return this.version;
}
@Override
public boolean hasSeqNo() {
return true;
}
@Override
public long getSeqNo() {
if (!hasSeqNo()) {
throw new IllegalStateException("No seq_no associated with this Document");
}
return this.seqNo;
}
@Override
public boolean hasPrimaryTerm() {
return true;
}
@Override
public long getPrimaryTerm() {
if (!hasPrimaryTerm()) {
throw new IllegalStateException("No primary_term associated with this Document");
}
return this.primaryTerm;
}
@Override
public int size() {
return documentFields.size();
}
@Override
public boolean isEmpty() {
return documentFields.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return documentFieldMap.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
for (DocumentField documentField : documentFields) {
Object fieldValue = getValue(documentField);
if (fieldValue != null && fieldValue.equals(value) || value == fieldValue) {
return true;
}
}
return false;
}
@Override
@Nullable
public Object get(Object key) {
DocumentField documentField = documentFieldMap.get(key);
return documentField != null ? documentField.getValue() : null;
}
@Override
public Object put(String key, Object value) {
throw new UnsupportedOperationException();
}
@Override
public Object remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map m) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public Set keySet() {
return documentFieldMap.keySet();
}
@Override
public Collection