mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-29 15:22:11 +00:00
DATAES-628 - Introduce Document API.
We now expose a Document API to encapsulate data interchanged with Elasticsearch (get responses, search hits, arbitrary maps) for a consistent API.
This commit is contained in:
parent
ce686b1f03
commit
29ecd484c5
@ -0,0 +1,388 @@
|
||||
/*
|
||||
* Copyright 2019 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A representation of a Elasticsearch document as extended {@link Map} with {@link String} keys. All iterators preserve
|
||||
* original insertion order.
|
||||
* <p>
|
||||
* Document does not allow {@code null} keys. It allows {@literal null} values.
|
||||
* <p>
|
||||
* Implementing classes can bei either mutable or immutable. In case a subclass is immutable, its methods may throw
|
||||
* {@link UnsupportedOperationException} when calling modifying methods.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface Document extends Map<String, Object> {
|
||||
|
||||
/**
|
||||
* Create a new mutable {@link Document}.
|
||||
*
|
||||
* @return a new {@link Document}.
|
||||
*/
|
||||
static Document create() {
|
||||
return new MapDocument();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Document} from a {@link Map} containing key-value pairs and sub-documents.
|
||||
*
|
||||
* @param map source map containing key-value pairs and sub-documents.
|
||||
* @return a new {@link Document}.
|
||||
*/
|
||||
static Document from(Map<String, Object> map) {
|
||||
|
||||
Assert.notNull(map, "Map must not be null");
|
||||
|
||||
if (map instanceof LinkedHashMap) {
|
||||
return new MapDocument(map);
|
||||
}
|
||||
|
||||
return new MapDocument(new LinkedHashMap<>(map));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON to {@link Document}.
|
||||
*
|
||||
* @param json must not be {@literal null}.
|
||||
* @return the parsed {@link Document}.
|
||||
*/
|
||||
static Document parse(String json) {
|
||||
try {
|
||||
return new MapDocument(MapDocument.OBJECT_MAPPER.readerFor(Map.class).readValue(json));
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Cannot parse JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link #put(Object, Object)} the {@code key}/{@code value} tuple and return {@code this} {@link Document}.
|
||||
*
|
||||
* @param key key with which the specified value is to be associated.
|
||||
* @param value value to be associated with the specified key.
|
||||
* @return {@code this} {@link Document}.
|
||||
*/
|
||||
default Document append(String key, Object value) {
|
||||
put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@literal true} if this {@link Document} is associated with an identifier.
|
||||
*
|
||||
* @return {@literal true} if this {@link Document} is associated with an identifier, {@literal false} otherwise.
|
||||
*/
|
||||
default boolean hasId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the identifier associated with this {@link Document}.
|
||||
* <p>
|
||||
* The default implementation throws {@link UnsupportedOperationException}. It's recommended to check {@link #hasId()}
|
||||
* prior to calling this method.
|
||||
*
|
||||
* @return the identifier associated with this {@link Document}.
|
||||
* @throws IllegalStateException if the underlying implementation supports Id's but no Id was yet associated with the
|
||||
* document.
|
||||
*/
|
||||
default String getId() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the identifier for this {@link Document}.
|
||||
* <p>
|
||||
* The default implementation throws {@link UnsupportedOperationException}.
|
||||
*/
|
||||
default void setId(String id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@literal true} if this {@link Document} is associated with a version.
|
||||
*
|
||||
* @return {@literal true} if this {@link Document} is associated with a version, {@literal false} otherwise.
|
||||
*/
|
||||
default boolean hasVersion() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the version associated with this {@link Document}.
|
||||
* <p>
|
||||
* The default implementation throws {@link UnsupportedOperationException}. It's recommended to check
|
||||
* {@link #hasVersion()} prior to calling this method.
|
||||
*
|
||||
* @return the version associated with this {@link Document}.
|
||||
* @throws IllegalStateException if the underlying implementation supports Id's but no Id was yet associated with the
|
||||
* document.
|
||||
*/
|
||||
default long getVersion() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the version for this {@link Document}.
|
||||
* <p>
|
||||
* The default implementation throws {@link UnsupportedOperationException}.
|
||||
*/
|
||||
default void setVersion(long version) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped, or {@literal null} if this document contains no
|
||||
* mapping for the key. The value is casted within the method which makes it useful for calling code as it does not
|
||||
* require casting on the calling side. If the value type is not assignable to {@code type}, then this method throws
|
||||
* {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @param type the expected return value type.
|
||||
* @param <T> expected return type.
|
||||
* @return the value to which the specified key is mapped, or {@literal null} if this document contains no mapping for
|
||||
* the key.
|
||||
* @throws ClassCastException if the value of the given key is not of {@code type T}.
|
||||
*/
|
||||
@Nullable
|
||||
default <T> T get(Object key, Class<T> type) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
|
||||
return type.cast(get(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped, or {@literal null} if this document contains no
|
||||
* mapping for the key. If the value type is not a {@link Boolean}, then this method throws
|
||||
* {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped, or {@literal null} if this document contains no mapping for
|
||||
* the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link Boolean}.
|
||||
*/
|
||||
@Nullable
|
||||
default Boolean getBoolean(String key) {
|
||||
return get(key, Boolean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped or {@code defaultValue} if this document contains no
|
||||
* mapping for the key. If the value type is not a {@link Boolean}, then this method throws
|
||||
* {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped or {@code defaultValue} if this document contains no mapping
|
||||
* for the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link Boolean}.
|
||||
*/
|
||||
default boolean getBooleanOrDefault(String key, boolean defaultValue) {
|
||||
return getBooleanOrDefault(key, () -> defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped or the value from {@code defaultValue} if this
|
||||
* document contains no mapping for the key. If the value type is not a {@link Boolean}, then this method throws
|
||||
* {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped or the value from {@code defaultValue} if this document
|
||||
* contains no mapping for the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link Boolean}.
|
||||
* @see BooleanSupplier
|
||||
*/
|
||||
default boolean getBooleanOrDefault(String key, BooleanSupplier defaultValue) {
|
||||
|
||||
Boolean value = getBoolean(key);
|
||||
|
||||
return value == null ? defaultValue.getAsBoolean() : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped, or {@literal null} if this document contains no
|
||||
* mapping for the key. If the value type is not a {@link Integer}, then this method throws
|
||||
* {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped, or {@literal null} if this document contains no mapping for
|
||||
* the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link Integer}.
|
||||
*/
|
||||
@Nullable
|
||||
default Integer getInt(String key) {
|
||||
return get(key, Integer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped or {@code defaultValue} if this document contains no
|
||||
* mapping for the key. If the value type is not a {@link Integer}, then this method throws
|
||||
* {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped or {@code defaultValue} if this document contains no mapping
|
||||
* for the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link Integer}.
|
||||
*/
|
||||
default int getIntOrDefault(String key, int defaultValue) {
|
||||
return getIntOrDefault(key, () -> defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped or the value from {@code defaultValue} if this
|
||||
* document contains no mapping for the key. If the value type is not a {@link Integer}, then this method throws
|
||||
* {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped or the value from {@code defaultValue} if this document
|
||||
* contains no mapping for the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link Integer}.
|
||||
* @see IntSupplier
|
||||
*/
|
||||
default int getIntOrDefault(String key, IntSupplier defaultValue) {
|
||||
|
||||
Integer value = getInt(key);
|
||||
|
||||
return value == null ? defaultValue.getAsInt() : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped, or {@literal null} if this document contains no
|
||||
* mapping for the key. If the value type is not a {@link Long}, then this method throws {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped, or {@literal null} if this document contains no mapping for
|
||||
* the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link Long}.
|
||||
*/
|
||||
@Nullable
|
||||
default Long getLong(String key) {
|
||||
return get(key, Long.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped or {@code defaultValue} if this document contains no
|
||||
* mapping for the key. If the value type is not a {@link Long}, then this method throws {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped or {@code defaultValue} if this document contains no mapping
|
||||
* for the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link Long}.
|
||||
*/
|
||||
default long getLongOrDefault(String key, long defaultValue) {
|
||||
return getLongOrDefault(key, () -> defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped or the value from {@code defaultValue} if this
|
||||
* document contains no mapping for the key. If the value type is not a {@link Long}, then this method throws
|
||||
* {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped or the value from {@code defaultValue} if this document
|
||||
* contains no mapping for the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link Long}.
|
||||
* @see LongSupplier
|
||||
*/
|
||||
default long getLongOrDefault(String key, LongSupplier defaultValue) {
|
||||
|
||||
Long value = getLong(key);
|
||||
|
||||
return value == null ? defaultValue.getAsLong() : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped, or {@literal null} if this document contains no
|
||||
* mapping for the key. If the value type is not a {@link String}, then this method throws {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped, or {@literal null} if this document contains no mapping for
|
||||
* the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link String}.
|
||||
*/
|
||||
@Nullable
|
||||
default String getString(String key) {
|
||||
return get(key, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped or {@code defaultValue} if this document contains no
|
||||
* mapping for the key. If the value type is not a {@link String}, then this method throws {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped or {@code defaultValue} if this document contains no mapping
|
||||
* for the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link String}.
|
||||
*/
|
||||
default String getStringOrDefault(String key, String defaultValue) {
|
||||
return getStringOrDefault(key, () -> defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified {@code key} is mapped or the value from {@code defaultValue} if this
|
||||
* document contains no mapping for the key. If the value type is not a {@link String}, then this method throws
|
||||
* {@link ClassCastException}.
|
||||
*
|
||||
* @param key the key whose associated value is to be returned
|
||||
* @return the value to which the specified key is mapped or the value from {@code defaultValue} if this document
|
||||
* contains no mapping for the key.
|
||||
* @throws ClassCastException if the value of the given key is not a {@link String}.
|
||||
* @see Supplier
|
||||
*/
|
||||
default String getStringOrDefault(String key, Supplier<String> defaultValue) {
|
||||
|
||||
String value = getString(key);
|
||||
|
||||
return value == null ? defaultValue.get() : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the application of a function to {@code this} {@link Document}. The function should expect a
|
||||
* single {@link Document} argument and produce an {@code R} result.
|
||||
* <p>
|
||||
* Any exception thrown by the function will be propagated to the caller.
|
||||
*
|
||||
* @param transformer functional interface to a apply
|
||||
* @param <R> class of the result
|
||||
* @return the result of applying the function to this string
|
||||
* @see java.util.function.Function
|
||||
*/
|
||||
default <R> R transform(Function<? super Document, ? extends R> transformer) {
|
||||
return transformer.apply(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render this {@link Document} to JSON. Auxiliary values such as Id and version are not considered within the JSON
|
||||
* representation.
|
||||
*
|
||||
* @return a JSON representation of this document.
|
||||
*/
|
||||
String toJson();
|
||||
}
|
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright 2019 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;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* {@link Document} implementation backed by a {@link LinkedHashMap}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
*/
|
||||
class MapDocument implements Document {
|
||||
|
||||
static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
private final LinkedHashMap<String, Object> documentAsMap;
|
||||
|
||||
private @Nullable String id;
|
||||
private @Nullable Long version;
|
||||
|
||||
MapDocument() {
|
||||
this(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
MapDocument(Map<String, Object> documentAsMap) {
|
||||
this.documentAsMap = new LinkedHashMap<>(documentAsMap);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#hasId()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasId() {
|
||||
return this.id != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#getId()
|
||||
*/
|
||||
@Override
|
||||
public String getId() {
|
||||
|
||||
if (!hasId()) {
|
||||
throw new IllegalStateException("No Id associated with this Document");
|
||||
}
|
||||
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#setId(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#hasVersion()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasVersion() {
|
||||
return this.version != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#getVersion()
|
||||
*/
|
||||
@Override
|
||||
public long getVersion() {
|
||||
|
||||
if (!hasVersion()) {
|
||||
throw new IllegalStateException("No version associated with this Document");
|
||||
}
|
||||
|
||||
return this.version;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#setVersion(long)
|
||||
*/
|
||||
@Override
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#size()
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return documentAsMap.size();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#isEmpty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return documentAsMap.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#containsKey(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return documentAsMap.containsKey(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#containsValue(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return documentAsMap.containsValue(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#get(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
return documentAsMap.get(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#getOrDefault(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object getOrDefault(Object key, Object defaultValue) {
|
||||
return documentAsMap.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
return documentAsMap.put(key, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#remove(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object remove(Object key) {
|
||||
return documentAsMap.remove(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#putAll(Map)
|
||||
*/
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ?> m) {
|
||||
documentAsMap.putAll(m);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#clear()
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
documentAsMap.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#keySet()
|
||||
*/
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return documentAsMap.keySet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#values()
|
||||
*/
|
||||
@Override
|
||||
public Collection<Object> values() {
|
||||
return documentAsMap.values();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#entrySet()
|
||||
*/
|
||||
@Override
|
||||
public Set<Entry<String, Object>> entrySet() {
|
||||
return documentAsMap.entrySet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return documentAsMap.equals(o);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return documentAsMap.hashCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#forEach(java.util.function.BiConsumer)
|
||||
*/
|
||||
@Override
|
||||
public void forEach(BiConsumer<? super String, ? super Object> action) {
|
||||
documentAsMap.forEach(action);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#toJson()
|
||||
*/
|
||||
@Override
|
||||
public String toJson() {
|
||||
try {
|
||||
return OBJECT_MAPPER.writeValueAsString(this);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new ElasticsearchException("Cannot render document to JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String id = hasId() ? getId() : "?";
|
||||
String version = hasVersion() ? Long.toString(getVersion()) : "?";
|
||||
|
||||
return getClass().getSimpleName() + "@" + id + "#" + version + " " + toJson();
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2019 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;
|
||||
|
||||
/**
|
||||
* Extension to {@link Document} exposing a search {@link #getScore() score}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
* @see Document
|
||||
*/
|
||||
public interface SearchDocument extends Document {
|
||||
|
||||
/**
|
||||
* Return the search {@code score}.
|
||||
*
|
||||
* @return the search {@code score}.
|
||||
*/
|
||||
float getScore();
|
||||
}
|
@ -0,0 +1,674 @@
|
||||
/*
|
||||
* Copyright 2019 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.core;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.document.DocumentField;
|
||||
import org.elasticsearch.index.get.GetResult;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.springframework.data.elasticsearch.Document;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.SearchDocument;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
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.common.document.DocumentField} to
|
||||
* {@link org.springframework.data.elasticsearch.Document}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.0
|
||||
*/
|
||||
class DocumentAdapters {
|
||||
|
||||
/**
|
||||
* Create a {@link Document} from {@link GetResponse}.
|
||||
* <p>
|
||||
* Returns a {@link Document} using the source if available.
|
||||
*
|
||||
* @param source the source {@link GetResponse}.
|
||||
* @return the adapted {@link Document}.
|
||||
*/
|
||||
public static Document from(GetResponse source) {
|
||||
|
||||
Assert.notNull(source, "GetResponse must not be null");
|
||||
|
||||
if (source.isSourceEmpty()) {
|
||||
return fromDocumentFields(source, source.getId(), source.getVersion());
|
||||
}
|
||||
|
||||
Document document = Document.from(source.getSourceAsMap());
|
||||
document.setId(source.getId());
|
||||
document.setVersion(source.getVersion());
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Document} from {@link GetResult}.
|
||||
* <p>
|
||||
* Returns a {@link Document} using the source if available.
|
||||
*
|
||||
* @param source the source {@link GetResult}.
|
||||
* @return the adapted {@link Document}.
|
||||
*/
|
||||
public static Document from(GetResult source) {
|
||||
|
||||
Assert.notNull(source, "GetResult must not be null");
|
||||
|
||||
if (source.isSourceEmpty()) {
|
||||
return fromDocumentFields(source, source.getId(), source.getVersion());
|
||||
}
|
||||
|
||||
Document document = Document.from(source.getSource());
|
||||
document.setId(source.getId());
|
||||
document.setVersion(source.getVersion());
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link SearchDocument} from {@link SearchHit}.
|
||||
* <p>
|
||||
* 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");
|
||||
|
||||
BytesReference sourceRef = source.getSourceRef();
|
||||
|
||||
if (sourceRef == null || sourceRef.length() == 0) {
|
||||
return new SearchDocumentAdapter(source.getScore(),
|
||||
fromDocumentFields(source, source.getId(), source.getVersion()));
|
||||
}
|
||||
|
||||
Document document = Document.from(source.getSourceAsMap());
|
||||
document.setId(source.getId());
|
||||
|
||||
if (source.getVersion() >= 0) {
|
||||
document.setVersion(source.getVersion());
|
||||
}
|
||||
|
||||
return new SearchDocumentAdapter(source.getScore(), document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an unmodifiable {@link Document} from {@link Iterable} of {@link DocumentField}s.
|
||||
*
|
||||
* @param documentFields the {@link DocumentField}s backing the {@link Document}.
|
||||
* @return the adapted {@link Document}.
|
||||
*/
|
||||
public static Document fromDocumentFields(Iterable<DocumentField> documentFields, String id, long version) {
|
||||
|
||||
if (documentFields instanceof Collection) {
|
||||
return new DocumentFieldAdapter((Collection<DocumentField>) documentFields, id, version);
|
||||
}
|
||||
|
||||
List<DocumentField> fields = new ArrayList<>();
|
||||
for (DocumentField documentField : documentFields) {
|
||||
fields.add(documentField);
|
||||
}
|
||||
|
||||
return new DocumentFieldAdapter(fields, id, version);
|
||||
}
|
||||
|
||||
// TODO: Performance regarding keys/values/entry-set
|
||||
static class DocumentFieldAdapter implements Document {
|
||||
|
||||
private final Collection<DocumentField> documentFields;
|
||||
private final String id;
|
||||
private final long version;
|
||||
|
||||
DocumentFieldAdapter(Collection<DocumentField> documentFields, String id, long version) {
|
||||
this.documentFields = documentFields;
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#hasId()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasId() {
|
||||
return id != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#getId()
|
||||
*/
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#hasVersion()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasVersion() {
|
||||
return this.version >= 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#getVersion()
|
||||
*/
|
||||
@Override
|
||||
public long getVersion() {
|
||||
|
||||
if (!hasVersion()) {
|
||||
throw new IllegalStateException("No version associated with this Document");
|
||||
}
|
||||
|
||||
return this.version;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#size()
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return documentFields.size();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#isEmpty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return documentFields.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#containsKey(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
|
||||
for (DocumentField documentField : documentFields) {
|
||||
if (documentField.getName().equals(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#containsValue(java.lang.Object)
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#get(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
|
||||
for (DocumentField documentField : documentFields) {
|
||||
if (documentField.getName().equals(key)) {
|
||||
|
||||
return getValue(documentField);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#remove(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object remove(Object key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#putAll(Map)
|
||||
*/
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ?> m) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#clear()
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#keySet()
|
||||
*/
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return documentFields.stream().map(DocumentField::getName).collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#values()
|
||||
*/
|
||||
@Override
|
||||
public Collection<Object> values() {
|
||||
return documentFields.stream().map(DocumentFieldAdapter::getValue).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#entrySet()
|
||||
*/
|
||||
@Override
|
||||
public Set<Entry<String, Object>> entrySet() {
|
||||
return documentFields.stream().collect(Collectors.toMap(DocumentField::getName, DocumentFieldAdapter::getValue))
|
||||
.entrySet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#forEach(java.util.function.BiConsumer)
|
||||
*/
|
||||
@Override
|
||||
public void forEach(BiConsumer<? super String, ? super Object> action) {
|
||||
|
||||
Objects.requireNonNull(action);
|
||||
|
||||
documentFields.forEach(field -> {
|
||||
action.accept(field.getName(), getValue(field));
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#toJson()
|
||||
*/
|
||||
@Override
|
||||
public String toJson() {
|
||||
|
||||
JsonFactory nodeFactory = new JsonFactory();
|
||||
try {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
JsonGenerator generator = nodeFactory.createGenerator(stream, JsonEncoding.UTF8);
|
||||
generator.writeStartObject();
|
||||
for (DocumentField value : documentFields) {
|
||||
if (value.getValues().size() > 1) {
|
||||
generator.writeArrayFieldStart(value.getName());
|
||||
for (Object val : value.getValues()) {
|
||||
generator.writeObject(val);
|
||||
}
|
||||
generator.writeEndArray();
|
||||
} else {
|
||||
generator.writeObjectField(value.getName(), value.getValue());
|
||||
}
|
||||
}
|
||||
generator.writeEndObject();
|
||||
generator.flush();
|
||||
return new String(stream.toByteArray(), StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Cannot render JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "@" + this.id + "#" + this.version + " " + toJson();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Object getValue(DocumentField documentField) {
|
||||
|
||||
if (documentField.getValues().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (documentField.getValues().size() == 1) {
|
||||
return documentField.getValue();
|
||||
}
|
||||
|
||||
return documentField.getValues();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter for a {@link SearchDocument}.
|
||||
*/
|
||||
static class SearchDocumentAdapter implements SearchDocument {
|
||||
|
||||
private final float score;
|
||||
private final Document delegate;
|
||||
|
||||
SearchDocumentAdapter(float score, Document delegate) {
|
||||
this.score = score;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#append(java.lang.String, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public SearchDocument append(String key, Object value) {
|
||||
delegate.append(key, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.SearchDocument#getScore()
|
||||
*/
|
||||
@Override
|
||||
public float getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#hasId()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasId() {
|
||||
return delegate.hasId();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#getId()
|
||||
*/
|
||||
@Override
|
||||
public String getId() {
|
||||
return delegate.getId();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#setId(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setId(String id) {
|
||||
delegate.setId(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#hasVersion()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasVersion() {
|
||||
return delegate.hasVersion();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#getVersion()
|
||||
*/
|
||||
@Override
|
||||
public long getVersion() {
|
||||
return delegate.getVersion();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#setVersion(long)
|
||||
*/
|
||||
@Override
|
||||
public void setVersion(long version) {
|
||||
delegate.setVersion(version);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#get(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(Object key, Class<T> type) {
|
||||
return delegate.get(key, type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.Document#toJson()
|
||||
*/
|
||||
@Override
|
||||
public String toJson() {
|
||||
return delegate.toJson();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#size()
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#isEmpty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#containsKey(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return delegate.containsKey(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#containsValue(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return delegate.containsValue(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#get(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
return delegate.put(key, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#remove(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object remove(Object key) {
|
||||
return delegate.remove(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#putAll(Map)
|
||||
*/
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ?> m) {
|
||||
delegate.putAll(m);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#clear()
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#keySet()
|
||||
*/
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return delegate.keySet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#values()
|
||||
*/
|
||||
@Override
|
||||
public Collection<Object> values() {
|
||||
return delegate.values();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#entrySet()
|
||||
*/
|
||||
@Override
|
||||
public Set<Entry<String, Object>> entrySet() {
|
||||
return delegate.entrySet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof SearchDocumentAdapter))
|
||||
return false;
|
||||
SearchDocumentAdapter that = (SearchDocumentAdapter) o;
|
||||
return Float.compare(that.score, score) == 0 && delegate.equals(that.delegate);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#forEach(java.util.function.BiConsumer)
|
||||
*/
|
||||
@Override
|
||||
public void forEach(BiConsumer<? super String, ? super Object> action) {
|
||||
delegate.forEach(action);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Map#remove(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean remove(Object key, Object value) {
|
||||
return delegate.remove(key, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String id = hasId() ? getId() : "?";
|
||||
String version = hasVersion() ? Long.toString(getVersion()) : "?";
|
||||
|
||||
return getClass().getSimpleName() + "@" + id + "#" + version + " " + toJson();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2019 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;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link Document}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class DocumentUnitTests {
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldCreateNewDocument() {
|
||||
|
||||
Document document = Document.create().append("key", "value");
|
||||
|
||||
assertThat(document).containsEntry("key", "value");
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldCreateNewDocumentFromMap() {
|
||||
|
||||
Document document = Document.from(Collections.singletonMap("key", "value"));
|
||||
|
||||
assertThat(document).containsEntry("key", "value");
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldRenderDocumentToJson() {
|
||||
|
||||
Document document = Document.from(Collections.singletonMap("key", "value"));
|
||||
|
||||
assertThat(document.toJson()).isEqualTo("{\"key\":\"value\"}");
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldParseDocumentFromJson() {
|
||||
|
||||
Document document = Document.parse("{\"key\":\"value\"}");
|
||||
|
||||
assertThat(document).containsEntry("key", "value");
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldReturnContainsKey() {
|
||||
|
||||
Document document = Document.create().append("string", "value").append("bool", true).append("int", 43)
|
||||
.append("long", 42L);
|
||||
|
||||
assertThat(document.containsKey("string")).isTrue();
|
||||
assertThat(document.containsKey("not-set")).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldReturnContainsValue() {
|
||||
|
||||
Document document = Document.create().append("string", "value").append("bool", Arrays.asList(true, true, false))
|
||||
.append("int", 43).append("long", 42L);
|
||||
|
||||
assertThat(document.containsValue("value")).isTrue();
|
||||
assertThat(document.containsValue(43)).isTrue();
|
||||
assertThat(document.containsValue(44)).isFalse();
|
||||
assertThat(document.containsValue(Arrays.asList(true, true, false))).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldReturnTypedValue() {
|
||||
|
||||
Document document = Document.create().append("string", "value").append("bool", true).append("int", 43)
|
||||
.append("long", 42L);
|
||||
|
||||
assertThat(document.get("string")).isEqualTo("value");
|
||||
assertThat(document.getString("string")).isEqualTo("value");
|
||||
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> document.get("long", String.class));
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldReturnTypedValueString() {
|
||||
|
||||
Document document = Document.create().append("string", "value").append("bool", true).append("int", 43)
|
||||
.append("long", 42L);
|
||||
|
||||
assertThat(document.getString("string")).isEqualTo("value");
|
||||
assertThat(document.getStringOrDefault("not-set", "default")).isEqualTo("default");
|
||||
assertThat(document.getStringOrDefault("not-set", () -> "default")).isEqualTo("default");
|
||||
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> document.getString("long"));
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> document.get("long", String.class));
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldReturnTypedValueBoolean() {
|
||||
|
||||
Document document = Document.create().append("string", "value").append("bool", true).append("int", 43)
|
||||
.append("long", 42L);
|
||||
|
||||
assertThat(document.getBoolean("bool")).isTrue();
|
||||
assertThat(document.getBooleanOrDefault("not-set", true)).isTrue();
|
||||
assertThat(document.getBooleanOrDefault("not-set", () -> true)).isTrue();
|
||||
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> document.getString("bool"));
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> document.get("bool", String.class));
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldReturnTypedValueInt() {
|
||||
|
||||
Document document = Document.create().append("string", "value").append("bool", true).append("int", 43)
|
||||
.append("long", 42L);
|
||||
|
||||
assertThat(document.getInt("int")).isEqualTo(43);
|
||||
assertThat(document.getIntOrDefault("not-set", 44)).isEqualTo(44);
|
||||
assertThat(document.getIntOrDefault("not-set", () -> 44)).isEqualTo(44);
|
||||
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> document.getString("int"));
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> document.get("int", String.class));
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldReturnTypedValueLong() {
|
||||
|
||||
Document document = Document.create().append("string", "value").append("bool", true).append("int", 43)
|
||||
.append("long", 42L);
|
||||
|
||||
assertThat(document.getLong("long")).isEqualTo(42);
|
||||
assertThat(document.getLongOrDefault("not-set", 44)).isEqualTo(44);
|
||||
assertThat(document.getLongOrDefault("not-set", () -> 44)).isEqualTo(44);
|
||||
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> document.getString("long"));
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> document.get("long", String.class));
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldApplyTransformer() {
|
||||
|
||||
Document document = Document.create();
|
||||
document.setId("value");
|
||||
|
||||
assertThat(document.transform(Document::getId)).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldSetId() {
|
||||
|
||||
Document document = Document.create();
|
||||
|
||||
assertThat(document.hasId()).isFalse();
|
||||
assertThatIllegalStateException().isThrownBy(document::getId);
|
||||
|
||||
document.setId("foo");
|
||||
assertThat(document.getId()).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldSetVersion() {
|
||||
|
||||
Document document = Document.create();
|
||||
|
||||
assertThat(document.hasVersion()).isFalse();
|
||||
assertThatIllegalStateException().isThrownBy(document::getVersion);
|
||||
|
||||
document.setVersion(14);
|
||||
assertThat(document.getVersion()).isEqualTo(14);
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldRenderToString() {
|
||||
|
||||
Document document = Document.from(Collections.singletonMap("key", "value"));
|
||||
document.setId("123");
|
||||
document.setVersion(42);
|
||||
|
||||
assertThat(document).hasToString("MapDocument@123#42 {\"key\":\"value\"}");
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2019 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.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
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.junit.Test;
|
||||
|
||||
import org.springframework.data.elasticsearch.Document;
|
||||
import org.springframework.data.elasticsearch.SearchDocument;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DocumentAdapters}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class DocumentAdaptersUnitTests {
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldAdaptGetResponse() {
|
||||
|
||||
Map<String, DocumentField> fields = Collections.singletonMap("field",
|
||||
new DocumentField("field", Arrays.asList("value")));
|
||||
|
||||
GetResult getResult = new GetResult("index", "type", "my-id", 1, 0, 42, true, null, fields);
|
||||
GetResponse response = new GetResponse(getResult);
|
||||
|
||||
Document document = DocumentAdapters.from(response);
|
||||
|
||||
assertThat(document.hasId()).isTrue();
|
||||
assertThat(document.getId()).isEqualTo("my-id");
|
||||
assertThat(document.hasVersion()).isTrue();
|
||||
assertThat(document.getVersion()).isEqualTo(42);
|
||||
assertThat(document.get("field")).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldAdaptGetResponseSource() {
|
||||
|
||||
BytesArray source = new BytesArray("{\"field\":\"value\"}");
|
||||
|
||||
GetResult getResult = new GetResult("index", "type", "my-id", 1, 0, 42, true, source, Collections.emptyMap());
|
||||
GetResponse response = new GetResponse(getResult);
|
||||
|
||||
Document document = DocumentAdapters.from(response);
|
||||
|
||||
assertThat(document.hasId()).isTrue();
|
||||
assertThat(document.getId()).isEqualTo("my-id");
|
||||
assertThat(document.hasVersion()).isTrue();
|
||||
assertThat(document.getVersion()).isEqualTo(42);
|
||||
assertThat(document.get("field")).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldAdaptSearchResponse() {
|
||||
|
||||
Map<String, DocumentField> fields = Collections.singletonMap("field",
|
||||
new DocumentField("field", Arrays.asList("value")));
|
||||
|
||||
SearchHit searchHit = new SearchHit(123, "my-id", new Text("type"), fields);
|
||||
searchHit.score(42);
|
||||
|
||||
SearchDocument document = DocumentAdapters.from(searchHit);
|
||||
|
||||
assertThat(document.hasId()).isTrue();
|
||||
assertThat(document.getId()).isEqualTo("my-id");
|
||||
assertThat(document.hasVersion()).isFalse();
|
||||
assertThat(document.getScore()).isBetween(42f, 42f);
|
||||
assertThat(document.get("field")).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void searchResponseShouldReturnContainsKey() {
|
||||
|
||||
Map<String, DocumentField> fields = new LinkedHashMap<>();
|
||||
|
||||
fields.put("string", new DocumentField("string", Arrays.asList("value")));
|
||||
fields.put("bool", new DocumentField("bool", Arrays.asList(true, true, false)));
|
||||
|
||||
SearchHit searchHit = new SearchHit(123, "my-id", new Text("type"), fields);
|
||||
|
||||
SearchDocument document = DocumentAdapters.from(searchHit);
|
||||
|
||||
assertThat(document.containsKey("string")).isTrue();
|
||||
assertThat(document.containsKey("not-set")).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void searchResponseShouldReturnContainsValue() {
|
||||
|
||||
Map<String, DocumentField> fields = new LinkedHashMap<>();
|
||||
|
||||
fields.put("string", new DocumentField("string", Arrays.asList("value")));
|
||||
fields.put("bool", new DocumentField("bool", Arrays.asList(true, true, false)));
|
||||
fields.put("null", new DocumentField("null", Collections.emptyList()));
|
||||
|
||||
SearchHit searchHit = new SearchHit(123, "my-id", new Text("type"), fields);
|
||||
|
||||
SearchDocument document = DocumentAdapters.from(searchHit);
|
||||
|
||||
assertThat(document.containsValue("value")).isTrue();
|
||||
assertThat(document.containsValue(Arrays.asList(true, true, false))).isTrue();
|
||||
assertThat(document.containsValue(null)).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldRenderToJson() {
|
||||
|
||||
Map<String, DocumentField> fields = new LinkedHashMap<>();
|
||||
|
||||
fields.put("string", new DocumentField("string", Arrays.asList("value")));
|
||||
fields.put("bool", new DocumentField("bool", Arrays.asList(true, true, false)));
|
||||
|
||||
SearchHit searchHit = new SearchHit(123, "my-id", new Text("type"), fields);
|
||||
|
||||
SearchDocument document = DocumentAdapters.from(searchHit);
|
||||
|
||||
assertThat(document.toJson()).isEqualTo("{\"string\":\"value\",\"bool\":[true,true,false]}");
|
||||
}
|
||||
|
||||
@Test // DATAES-628
|
||||
public void shouldAdaptSearchResponseSource() {
|
||||
|
||||
BytesArray source = new BytesArray("{\"field\":\"value\"}");
|
||||
|
||||
SearchHit searchHit = new SearchHit(123, "my-id", new Text("type"), Collections.emptyMap());
|
||||
searchHit.sourceRef(source).score(42);
|
||||
searchHit.version(22);
|
||||
|
||||
SearchDocument document = DocumentAdapters.from(searchHit);
|
||||
|
||||
assertThat(document.hasId()).isTrue();
|
||||
assertThat(document.getId()).isEqualTo("my-id");
|
||||
assertThat(document.hasVersion()).isTrue();
|
||||
assertThat(document.getVersion()).isEqualTo(22);
|
||||
assertThat(document.getScore()).isBetween(42f, 42f);
|
||||
assertThat(document.get("field")).isEqualTo("value");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user