DATAES-840 - Introduce IndexCoordinateResolver.

Original PR: #467
This commit is contained in:
Peter-Josef Meisch 2020-06-12 08:23:41 +02:00 committed by GitHub
parent 3c44a1c969
commit aeaa27cb99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 971 additions and 624 deletions

View File

@ -3,8 +3,16 @@
This section describes breaking changes from version 4.0.x to 4.1.x and how removed features can be replaced by new introduced features.
== Deprecations
.Definition of the id property
It is possible to define a property of en entity as the id property by naming it either `id` or `document`. This behaviour is now deprecated and will produce a warning. PLease us the `@Id` annotation to mark a property as being the id property.
[[elasticsearch-migration-guide-4.0-4.1.removal]]
== Removals
.Type mappings
The _type mappings_ parameters of the `@Document` annotation and the `IndexCoordinates` object were removed. They had been deprecated in Spring Data Elasticsearch 4.0 and their values weren't used anymore.
=== Breaking changes

View File

@ -38,6 +38,7 @@ import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersiste
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.AliasQuery;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@ -55,20 +56,24 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
protected final RequestFactory requestFactory;
@Nullable protected final Class<?> boundClass;
private final IndexCoordinates boundIndex;
@Nullable private final IndexCoordinates boundIndex;
public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter, Class<?> boundClass) {
Assert.notNull(boundClass, "boundClass may not be null");
this.elasticsearchConverter = elasticsearchConverter;
requestFactory = new RequestFactory(elasticsearchConverter);
this.boundClass = boundClass;
this.boundIndex = getIndexCoordinatesFor(boundClass);
this.boundIndex = null;
}
public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter, IndexCoordinates boundIndex) {
Assert.notNull(boundIndex, "boundIndex may not be null");
this.elasticsearchConverter = elasticsearchConverter;
requestFactory = new RequestFactory(elasticsearchConverter);
this.boundClass = null;
this.boundIndex = boundIndex;
}
@ -85,48 +90,59 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
@Override
public boolean create() {
IndexCoordinates index;
Document settings = null;
if (boundClass != null) {
Class<?> clazz = boundClass;
String indexName = getIndexCoordinates().getIndexName();
ElasticsearchPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(clazz);
index = persistentEntity.getIndexCoordinates();
if (clazz.isAnnotationPresent(Setting.class)) {
String settingPath = clazz.getAnnotation(Setting.class).settingPath();
if (hasText(settingPath)) {
String settings = ResourceUtil.readFileFromClasspath(settingPath);
String fileSettings = ResourceUtil.readFileFromClasspath(settingPath);
if (hasText(settings)) {
return doCreate(indexName, Document.parse(settings));
if (hasText(fileSettings)) {
settings = Document.parse(fileSettings);
}
} else {
LOGGER.info("settingPath in @Setting has to be defined. Using default instead.");
}
}
return doCreate(indexName, getDefaultSettings(getRequiredPersistentEntity(clazz)));
if (settings == null) {
settings = getDefaultSettings(persistentEntity);
}
} else {
index = boundIndex;
}
return doCreate(getIndexCoordinates().getIndexName(), null);
// noinspection ConstantConditions
return doCreate(index, settings);
}
@Override
public boolean create(Document settings) {
return doCreate(getIndexCoordinates().getIndexName(), settings);
return doCreate(getIndexCoordinates(), settings);
}
protected abstract boolean doCreate(String indexName, @Nullable Document settings);
protected abstract boolean doCreate(IndexCoordinates index, @Nullable Document settings);
@Override
public boolean delete() {
return doDelete(getIndexCoordinates().getIndexName());
return doDelete(getIndexCoordinates());
}
protected abstract boolean doDelete(String indexName);
protected abstract boolean doDelete(IndexCoordinates index);
@Override
public boolean exists() {
return doExists(getIndexCoordinates().getIndexName());
return doExists(getIndexCoordinates());
}
protected abstract boolean doExists(String indexName);
protected abstract boolean doExists(IndexCoordinates index);
@Override
public boolean putMapping(Document mapping) {
@ -149,10 +165,10 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
@Override
public Map<String, Object> getSettings(boolean includeDefaults) {
return doGetSettings(getIndexCoordinates().getIndexName(), includeDefaults);
return doGetSettings(getIndexCoordinates(), includeDefaults);
}
protected abstract Map<String, Object> doGetSettings(String indexName, boolean includeDefaults);
protected abstract Map<String, Object> doGetSettings(IndexCoordinates index, boolean includeDefaults);
@Override
public void refresh() {
@ -170,10 +186,10 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
@Override
public List<AliasMetaData> queryForAlias() {
return doQueryForAlias(getIndexCoordinates().getIndexName());
return doQueryForAlias(getIndexCoordinates());
}
protected abstract List<AliasMetaData> doQueryForAlias(String indexName);
protected abstract List<AliasMetaData> doQueryForAlias(IndexCoordinates index);
@Override
public boolean removeAlias(AliasQuery query) {
@ -238,14 +254,15 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz);
}
/**
* get the current {@link IndexCoordinates}. These may change over time when the entity class has a SpEL constructed
* index name. When this IndexOperations is not bound to a class, the bound IndexCoordinates are returned.
*
* @return IndexCoordinates
*/
protected IndexCoordinates getIndexCoordinates() {
return (boundClass != null) ? getIndexCoordinatesFor(boundClass) : boundIndex;
if (boundClass != null) {
return getIndexCoordinatesFor(boundClass);
}
Assert.notNull(boundIndex, "boundIndex may not be null");
return boundIndex;
}
public IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {

View File

@ -32,6 +32,7 @@ import org.elasticsearch.action.search.MultiSearchResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@ -48,6 +49,7 @@ import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersiste
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
@ -55,6 +57,7 @@ import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.support.VersionInfo;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.util.CloseableIterator;
@ -199,6 +202,11 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return get(query.getId(), clazz, index);
}
@Override
public <T> List<T> multiGet(Query query, Class<T> clazz) {
return multiGet(query, clazz, getIndexCoordinatesFor(clazz));
}
@Override
@Nullable
public <T> T queryForObject(GetQuery query, Class<T> clazz) {
@ -226,6 +234,11 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return this.delete(id, getIndexCoordinatesFor(entityType));
}
@Override
public void delete(Query query, Class<?> clazz) {
delete(query, getIndexCoordinatesFor(clazz));
}
@Override
public String delete(Object entity) {
return delete(entity, getIndexCoordinatesFor(entity.getClass()));
@ -235,6 +248,22 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
public String delete(Object entity, IndexCoordinates index) {
return this.delete(getEntityId(entity), index);
}
@Override
public List<String> bulkIndex(List<IndexQuery> queries, Class<?> clazz) {
return bulkIndex(queries, getIndexCoordinatesFor(clazz));
}
@Override
public List<String> bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions, Class<?> clazz) {
return bulkIndex(queries, bulkOptions, getIndexCoordinatesFor(clazz));
}
@Override
public void bulkUpdate(List<UpdateQuery> queries, Class<?> clazz) {
bulkUpdate(queries, getIndexCoordinatesFor(clazz));
}
// endregion
// region SearchOperations
@ -282,6 +311,11 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return search(new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).build(), clazz, index);
}
@Override
public <T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz) {
return multiSearch(queries, clazz, getIndexCoordinatesFor(clazz));
}
@Override
public <T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz, IndexCoordinates index) {
MultiSearchRequest request = new MultiSearchRequest();
@ -300,9 +334,46 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return res;
}
@Override
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes) {
Assert.notNull(queries, "queries must not be null");
Assert.notNull(classes, "classes must not be null");
Assert.isTrue(queries.size() == classes.size(), "queries and classes must have the same size");
MultiSearchRequest request = new MultiSearchRequest();
Iterator<Class<?>> it = classes.iterator();
for (Query query : queries) {
Class<?> clazz = it.next();
request.add(requestFactory.searchRequest(query, clazz, getIndexCoordinatesFor(clazz)));
}
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
List<SearchHits<?>> res = new ArrayList<>(queries.size());
int c = 0;
Iterator<Class<?>> it1 = classes.iterator();
for (Query query : queries) {
Class entityClass = it1.next();
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(entityClass,
getIndexCoordinatesFor(entityClass));
SearchResponse response = items[c++].getResponse();
res.add(callback.doWith(SearchDocumentResponse.from(response)));
}
return res;
}
@Override
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes,
IndexCoordinates index) {
Assert.notNull(queries, "queries must not be null");
Assert.notNull(classes, "classes must not be null");
Assert.notNull(index, "index must not be null");
Assert.isTrue(queries.size() == classes.size(), "queries and classes must have the same size");
MultiSearchRequest request = new MultiSearchRequest();
Iterator<Class<?>> it = classes.iterator();
for (Query query : queries) {
@ -356,6 +427,12 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
abstract protected void searchScrollClear(List<String> scrollIds);
abstract protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request);
@Override
public SearchResponse suggest(SuggestBuilder suggestion, Class<?> clazz) {
return suggest(suggestion, getIndexCoordinatesFor(clazz));
}
// endregion
// region Helper methods

View File

@ -15,40 +15,31 @@
*/
package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.client.Requests.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.GetAliasesResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.GetMappingsResponse;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.core.client.support.AliasData;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.AliasQuery;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* {@link IndexOperations} implementation using the RestClient.
*
@ -58,7 +49,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
*/
class DefaultIndexOperations extends AbstractDefaultIndexOperations implements IndexOperations {
private ElasticsearchRestTemplate restTemplate;
private final ElasticsearchRestTemplate restTemplate;
public DefaultIndexOperations(ElasticsearchRestTemplate restTemplate, Class<?> boundClass) {
super(restTemplate.getElasticsearchConverter(), boundClass);
@ -71,27 +62,29 @@ class DefaultIndexOperations extends AbstractDefaultIndexOperations implements I
}
@Override
protected boolean doCreate(String indexName, @Nullable Document settings) {
CreateIndexRequest request = requestFactory.createIndexRequest(indexName, settings);
protected boolean doCreate(IndexCoordinates index, @Nullable Document settings) {
CreateIndexRequest request = requestFactory.createIndexRequest(index, settings);
return restTemplate.execute(client -> client.indices().create(request, RequestOptions.DEFAULT).isAcknowledged());
}
@Override
protected boolean doDelete(String indexName) {
protected boolean doDelete(IndexCoordinates index) {
Assert.notNull(indexName, "No index defined for delete operation");
Assert.notNull(index, "index must not be null");
if (doExists(indexName)) {
DeleteIndexRequest request = new DeleteIndexRequest(indexName);
return restTemplate.execute(client -> client.indices().delete(request, RequestOptions.DEFAULT).isAcknowledged());
if (doExists(index)) {
DeleteIndexRequest deleteIndexRequest = requestFactory.deleteIndexRequest(index);
return restTemplate
.execute(client -> client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT).isAcknowledged());
}
return false;
}
@Override
protected boolean doExists(String indexName) {
GetIndexRequest request = new GetIndexRequest(indexName);
return restTemplate.execute(client -> client.indices().exists(request, RequestOptions.DEFAULT));
protected boolean doExists(IndexCoordinates index) {
GetIndexRequest getIndexRequest = requestFactory.getIndexRequest(index);
return restTemplate.execute(client -> client.indices().exists(getIndexRequest, RequestOptions.DEFAULT));
}
@Override
@ -109,16 +102,20 @@ class DefaultIndexOperations extends AbstractDefaultIndexOperations implements I
Assert.notNull(index, "No index defined for getMapping()");
GetMappingsRequest mappingsRequest = requestFactory.getMappingsRequest(index);
return restTemplate.execute(client -> {
RestClient restClient = client.getLowLevelClient();
Request request = new Request("GET", '/' + index.getIndexName() + "/_mapping");
Response response = restClient.performRequest(request);
return convertMappingResponse(EntityUtils.toString(response.getEntity()));
GetMappingsResponse mapping = client.indices().getMapping(mappingsRequest, RequestOptions.DEFAULT);
// we only return data for the first index name that was requested (always have done so)
String index1 = mappingsRequest.indices()[0];
Map<String, Object> result = new LinkedHashMap<>();
return mapping.mappings().get(index1).getSourceAsMap();
});
}
@Override
protected boolean doAddAlias(AliasQuery query, IndexCoordinates index) {
IndicesAliasesRequest request = requestFactory.indicesAddAliasesRequest(query, index);
return restTemplate
.execute(client -> client.indices().updateAliases(request, RequestOptions.DEFAULT).isAcknowledged());
@ -136,34 +133,28 @@ class DefaultIndexOperations extends AbstractDefaultIndexOperations implements I
}
@Override
protected List<AliasMetaData> doQueryForAlias(String indexName) {
List<AliasMetaData> aliases = null;
protected List<AliasMetaData> doQueryForAlias(IndexCoordinates index) {
GetAliasesRequest getAliasesRequest = requestFactory.getAliasesRequest(index);
return restTemplate.execute(client -> {
RestClient restClient = client.getLowLevelClient();
Response response;
String aliasResponse;
response = restClient.performRequest(new Request("GET", '/' + indexName + "/_alias/*"));
aliasResponse = EntityUtils.toString(response.getEntity());
return convertAliasResponse(aliasResponse);
GetAliasesResponse alias = client.indices().getAlias(getAliasesRequest, RequestOptions.DEFAULT);
// we only return data for the first index name that was requested (always have done so)
String index1 = getAliasesRequest.indices()[0];
return new ArrayList<>(alias.getAliases().get(index1));
});
}
@Override
protected Map<String, Object> doGetSettings(String indexName, boolean includeDefaults) {
protected Map<String, Object> doGetSettings(IndexCoordinates index, boolean includeDefaults) {
Assert.notNull(indexName, "No index defined for getSettings");
Assert.notNull(index, "index must not be null");
GetSettingsRequest request = new GetSettingsRequest() //
.indices(indexName) //
.includeDefaults(includeDefaults);
//
GetSettingsRequest getSettingsRequest = requestFactory.getSettingsRequest(index, includeDefaults);
GetSettingsResponse response = restTemplate.execute(client -> client.indices() //
.getSettings(request, RequestOptions.DEFAULT));
.getSettings(getSettingsRequest, RequestOptions.DEFAULT));
return convertSettingsResponseToMap(response, indexName);
return convertSettingsResponseToMap(response, getSettingsRequest.indices()[0]);
}
@Override
@ -171,61 +162,9 @@ class DefaultIndexOperations extends AbstractDefaultIndexOperations implements I
Assert.notNull(index, "No index defined for refresh()");
restTemplate
.execute(client -> client.indices().refresh(refreshRequest(index.getIndexNames()), RequestOptions.DEFAULT));
RefreshRequest refreshRequest = requestFactory.refreshRequest(index);
restTemplate.execute(client -> client.indices().refresh(refreshRequest, RequestOptions.DEFAULT));
}
// region Helper methods
private Map<String, Object> convertMappingResponse(String mappingResponse) {
ObjectMapper mapper = new ObjectMapper();
try {
Map<String, Object> result = null;
JsonNode node = mapper.readTree(mappingResponse);
node = node.findValue("mappings");
result = mapper.readValue(mapper.writeValueAsString(node), HashMap.class);
return result;
} catch (IOException e) {
throw new UncategorizedElasticsearchException("Could not map alias response : " + mappingResponse, e);
}
}
/**
* It takes two steps to create a List<AliasMetadata> from the elasticsearch http response because the aliases field
* is actually a Map by alias name, but the alias name is on the AliasMetadata.
*
* @param aliasResponse
* @return
*/
private List<AliasMetaData> convertAliasResponse(String aliasResponse) {
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode node = mapper.readTree(aliasResponse);
node = node.findValue("aliases");
if (node == null) {
return Collections.emptyList();
}
Map<String, AliasData> aliasData = mapper.readValue(mapper.writeValueAsString(node),
new TypeReference<Map<String, AliasData>>() {});
Iterable<Map.Entry<String, AliasData>> aliasIter = aliasData.entrySet();
List<AliasMetaData> aliasMetaDataList = new ArrayList<>();
for (Map.Entry<String, AliasData> aliasentry : aliasIter) {
AliasData data = aliasentry.getValue();
aliasMetaDataList.add(AliasMetaData.newAliasMetaDataBuilder(aliasentry.getKey()).filter(data.getFilter())
.routing(data.getRouting()).searchRouting(data.getSearch_routing()).indexRouting(data.getIndex_routing())
.build());
}
return aliasMetaDataList;
} catch (IOException e) {
throw new UncategorizedElasticsearchException("Could not map alias response : " + aliasResponse, e);
}
}
// endregion
}

View File

@ -21,16 +21,18 @@ import java.util.List;
import java.util.Map;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
@ -62,26 +64,29 @@ class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations imp
}
@Override
protected boolean doCreate(String indexName, @Nullable Document settings) {
CreateIndexRequestBuilder createIndexRequestBuilder = requestFactory.createIndexRequestBuilder(client, indexName,
protected boolean doCreate(IndexCoordinates index, @Nullable Document settings) {
CreateIndexRequestBuilder createIndexRequestBuilder = requestFactory.createIndexRequestBuilder(client, index,
settings);
return createIndexRequestBuilder.execute().actionGet().isAcknowledged();
}
@Override
protected boolean doDelete(String indexName) {
protected boolean doDelete(IndexCoordinates index) {
Assert.notNull(indexName, "No index defined for delete operation");
Assert.notNull(index, "No index defined for delete operation");
if (doExists(indexName)) {
return client.admin().indices().delete(new DeleteIndexRequest(indexName)).actionGet().isAcknowledged();
if (doExists(index)) {
DeleteIndexRequest deleteIndexRequest = requestFactory.deleteIndexRequest(index);
return client.admin().indices().delete(deleteIndexRequest).actionGet().isAcknowledged();
}
return false;
}
@Override
protected boolean doExists(String indexName) {
return client.admin().indices().exists(indicesExistsRequest(indexName)).actionGet().isExists();
protected boolean doExists(IndexCoordinates index) {
IndicesExistsRequest indicesExistsRequest = requestFactory.indicesExistsRequest(index);
return client.admin().indices().exists(indicesExistsRequest).actionGet().isExists();
}
@Override
@ -98,14 +103,12 @@ class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations imp
Assert.notNull(index, "No index defined for getMapping()");
try {
return client.admin().indices().getMappings( //
new GetMappingsRequest().indices(index.getIndexNames())).actionGet() //
.getMappings().get(index.getIndexName()).get(IndexCoordinates.TYPE) //
.getSourceAsMap();
} catch (Exception e) {
throw new ElasticsearchException("Error while getting mapping for indexName : " + index.getIndexName(), e);
}
GetMappingsRequest mappingsRequest = requestFactory.getMappingsRequest(client, index);
return client.admin().indices().getMappings( //
mappingsRequest).actionGet() //
.getMappings().get(mappingsRequest.indices()[0]).get(IndexCoordinates.TYPE) //
.getSourceAsMap();
}
@Override
@ -120,39 +123,39 @@ class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations imp
Assert.notNull(index, "No index defined for Alias");
Assert.notNull(query.getAliasName(), "No alias defined");
return client.admin().indices().prepareAliases().removeAlias(index.getIndexName(), query.getAliasName()).execute()
.actionGet().isAcknowledged();
IndicesAliasesRequestBuilder indicesAliasesRequestBuilder = requestFactory
.indicesRemoveAliasesRequestBuilder(client, query, index);
return indicesAliasesRequestBuilder.execute().actionGet().isAcknowledged();
}
@Override
protected List<AliasMetaData> doQueryForAlias(String indexName) {
return client.admin().indices().getAliases(new GetAliasesRequest().indices(indexName)).actionGet().getAliases()
.get(indexName);
protected List<AliasMetaData> doQueryForAlias(IndexCoordinates index) {
GetAliasesRequest getAliasesRequest = requestFactory.getAliasesRequest(index);
return client.admin().indices().getAliases(getAliasesRequest).actionGet().getAliases().get(index.getIndexName());
}
@Override
protected Map<String, Object> doGetSettings(String indexName, boolean includeDefaults) {
protected Map<String, Object> doGetSettings(IndexCoordinates index, boolean includeDefaults) {
Assert.notNull(indexName, "No index defined for getSettings");
GetSettingsRequest request = new GetSettingsRequest() //
.indices(indexName) //
.includeDefaults(includeDefaults);
Assert.notNull(index, "index must not be null");
GetSettingsRequest getSettingsRequest = requestFactory.getSettingsRequest(index, includeDefaults);
GetSettingsResponse response = client.admin() //
.indices() //
.getSettings(request) //
.getSettings(getSettingsRequest) //
.actionGet();
return convertSettingsResponseToMap(response, indexName);
return convertSettingsResponseToMap(response, getSettingsRequest.indices()[0]);
}
@Override
protected void doRefresh(IndexCoordinates index) {
Assert.notNull(index, "No index defined for refresh()");
Assert.notNull(index, "index must not be null");
client.admin().indices().refresh(refreshRequest(index.getIndexNames())).actionGet();
RefreshRequest request = requestFactory.refreshRequest(index);
client.admin().indices().refresh(request).actionGet();
}
}

View File

@ -114,6 +114,16 @@ public interface DocumentOperations {
@Nullable
<T> T get(String id, Class<T> clazz, IndexCoordinates index);
/**
* Execute a multiGet against elasticsearch for the given ids.
*
* @param query the query defining the ids of the objects to get
* @param clazz the type of the object to be returned
* @return list of objects, contains null values for ids that are not found
* @since 4.1
*/
<T> List<T> multiGet(Query query, Class<T> clazz);
/**
* Execute a multiGet against elasticsearch for the given ids.
*
@ -142,6 +152,18 @@ public interface DocumentOperations {
*/
boolean exists(String id, IndexCoordinates index);
/**
* Bulk index all objects. Will do save or update.
*
* @param queries the queries to execute in bulk
* @param clazz the entity class
* @return the ids of the indexed objects
* @since 4.1
*/
default List<String> bulkIndex(List<IndexQuery> queries, Class<?> clazz) {
return bulkIndex(queries, BulkOptions.defaultOptions(), clazz);
}
/**
* Bulk index all objects. Will do save or update.
*
@ -152,6 +174,17 @@ public interface DocumentOperations {
return bulkIndex(queries, BulkOptions.defaultOptions(), index);
}
/**
* Bulk index all objects. Will do save or update.
*
* @param queries the queries to execute in bulk
* @param bulkOptions options to be added to the bulk request
* @param clazz the entity class
* @return the ids of the indexed objects
* @since 4.1
*/
List<String> bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions, Class<?> clazz);
/**
* Bulk index all objects. Will do save or update.
*
@ -170,6 +203,14 @@ public interface DocumentOperations {
bulkUpdate(queries, BulkOptions.defaultOptions(), index);
}
/**
* Bulk update all objects. Will do update.
* @param clazz the entity class
* @param queries the queries to execute in bulk
* @since 4.1
*/
void bulkUpdate(List<UpdateQuery> queries, Class<?> clazz);
/**
* Bulk update all objects. Will do update.
*
@ -213,6 +254,16 @@ public interface DocumentOperations {
*/
String delete(Object entity, IndexCoordinates index);
/**
* Delete all records matching the query.
*
* @param query query defining the objects
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @since 4.1
*/
void delete(Query query, Class<?> clazz);
/**
* Delete all records matching the query.
*

View File

@ -60,17 +60,17 @@ public interface ElasticsearchOperations extends DocumentOperations, SearchOpera
IndexCoordinates getIndexCoordinatesFor(Class<?> clazz);
// region IndexOperations
/**
* Create an index for given indexName .
*
* @param indexName the name of the index
* @return {@literal true} if the index was created
* @deprecated since 4.0, use {@link IndexOperations#create()}
*/
@Deprecated
default boolean createIndex(String indexName) {
return indexOps(IndexCoordinates.of(indexName)).create();
}
/**
* Create an index for given indexName .
*
* @param indexName the name of the index
* @return {@literal true} if the index was created
* @deprecated since 4.0, use {@link IndexOperations#create()}
*/
@Deprecated
default boolean createIndex(String indexName) {
return indexOps(IndexCoordinates.of(indexName)).create();
}
/**
* Create an index for given indexName and Settings.

View File

@ -37,7 +37,6 @@ import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.slf4j.Logger;
@ -315,10 +314,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
@Override
public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) {
SearchRequest searchRequest = new SearchRequest(index.getIndexNames());
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.suggest(suggestion);
searchRequest.source(sourceBuilder);
SearchRequest searchRequest = requestFactory.searchRequest(suggestion, index);
return execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
}

View File

@ -23,6 +23,7 @@ import org.elasticsearch.action.admin.cluster.node.info.NodesInfoAction;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequestBuilder;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.delete.DeleteRequestBuilder;
import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetRequestBuilder;
@ -227,8 +228,9 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(id, "id must not be null");
Assert.notNull(index, "index must not be null");
return client.prepareDelete(index.getIndexName(), IndexCoordinates.TYPE, elasticsearchConverter.convertId(id))
.execute().actionGet().getId();
DeleteRequestBuilder deleteRequestBuilder = requestFactory.deleteRequestBuilder(client,
elasticsearchConverter.convertId(id), index);
return deleteRequestBuilder.execute().actionGet().getId();
}
@Override
@ -293,8 +295,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(query.getPageable(), "pageable of query must not be null.");
ActionFuture<SearchResponse> action = requestFactory //
.searchRequestBuilder(client, query, clazz, index) //
ActionFuture<SearchResponse> action = requestFactory.searchRequestBuilder(client, query, clazz, index) //
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)) //
.execute();
@ -332,7 +333,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
@Override
public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) {
return client.prepareSearch(index.getIndexNames()).suggest(suggestion).get();
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, suggestion, index);
return searchRequestBuilder.get();
}
@Override

View File

@ -28,7 +28,6 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Common operations performed on an entity in the context of it's mapping metadata.
@ -43,6 +42,8 @@ class EntityOperations {
private static final String ID_FIELD = "id";
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context;
public EntityOperations(
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context) {
@ -51,8 +52,6 @@ class EntityOperations {
this.context = context;
}
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context;
/**
* Creates a new {@link Entity} for the given bean.
*
@ -100,11 +99,26 @@ class EntityOperations {
* @param index index name override can be {@literal null}.
* @param type index type override can be {@literal null}.
* @return the {@link IndexCoordinates} containing index name and index type.
* @deprecated since 4.1, use {@link EntityOperations#determineIndex(Entity, String)}
*/
@Deprecated
IndexCoordinates determineIndex(Entity<?> entity, @Nullable String index, @Nullable String type) {
return determineIndex(entity.getPersistentEntity(), index, type);
}
/**
* Determine index name and type name from {@link Entity} with {@code index} and {@code type} overrides. Allows using
* preferred values for index and type if provided, otherwise fall back to index and type defined on entity level.
*
* @param entity the entity to determine the index name. Can be {@literal null} if {@code index} and {@literal type}
* are provided.
* @param index index name override can be {@literal null}.
* @return the {@link IndexCoordinates} containing index name and index type.
*/
IndexCoordinates determineIndex(Entity<?> entity, @Nullable String index) {
return determineIndex(entity.getPersistentEntity(), index);
}
/**
* Determine index name and type name from {@link ElasticsearchPersistentEntity} with {@code index} and {@code type}
* overrides. Allows using preferred values for index and type if provided, otherwise fall back to index and type
@ -115,20 +129,27 @@ class EntityOperations {
* @param index index name override can be {@literal null}.
* @param type index type override can be {@literal null}.
* @return the {@link IndexCoordinates} containing index name and index type.
* @deprecated since 4.1, use {@link EntityOperations#determineIndex(ElasticsearchPersistentEntity, String)}
*/
@Deprecated
IndexCoordinates determineIndex(ElasticsearchPersistentEntity<?> persistentEntity, @Nullable String index,
@Nullable String type) {
return persistentEntity.getIndexCoordinates();
return determineIndex(persistentEntity, index);
}
private static String indexName(@Nullable ElasticsearchPersistentEntity<?> entity, @Nullable String index) {
if (StringUtils.isEmpty(index)) {
Assert.notNull(entity, "Cannot determine index name");
return entity.getIndexCoordinates().getIndexName();
}
return index;
/**
* Determine index name and type name from {@link ElasticsearchPersistentEntity} with {@code index} and {@code type}
* overrides. Allows using preferred values for index and type if provided, otherwise fall back to index and type
* defined on entity level.
*
* @param persistentEntity the entity to determine the index name. Can be {@literal null} if {@code index} and
* {@literal type} are provided.
* @param index index name override can be {@literal null}.
* @return the {@link IndexCoordinates} containing index name and index type.
* @since 4.1
*/
IndexCoordinates determineIndex(ElasticsearchPersistentEntity<?> persistentEntity, @Nullable String index) {
return index != null ? IndexCoordinates.of(index) : persistentEntity.getIndexCoordinates();
}
/**

View File

@ -38,7 +38,7 @@ import org.springframework.util.Assert;
*/
public interface ReactiveDocumentOperations {
/**
* Index the given entity, once available, extracting index and type from entity metadata.
* Index the given entity, once available, extracting index from entity metadata.
*
* @param entityPublisher must not be {@literal null}.
* @param <T>
@ -50,15 +50,6 @@ public interface ReactiveDocumentOperations {
return entityPublisher.flatMap(this::save);
}
/**
* Index the given entity extracting index and type from entity metadata.
*
* @param entity must not be {@literal null}.
* @param <T>
* @return a {@link Mono} emitting the saved entity.
*/
<T> Mono<T> save(T entity);
/**
* Index the entity, once available, under the given {@literal type} in the given {@literal index}. If the
* {@literal index} is {@literal null} or empty the index name provided via entity metadata is used. Same for the
@ -75,6 +66,15 @@ public interface ReactiveDocumentOperations {
return entityPublisher.flatMap(it -> save(it, index));
}
/**
* Index the given entity extracting index from entity metadata.
*
* @param entity must not be {@literal null}.
* @param <T>
* @return a {@link Mono} emitting the saved entity.
*/
<T> Mono<T> save(T entity);
/**
* Index the entity under the given {@literal type} in the given {@literal index}. If the {@literal index} is
* {@literal null} or empty the index name provided via entity metadata is used. Same for the {@literal type}.
@ -87,7 +87,21 @@ public interface ReactiveDocumentOperations {
<T> Mono<T> save(T entity, IndexCoordinates index);
/**
* Index entities under the given {@literal type} in the given {@literal index}. If the {@literal index} is
* Index entities the index extracted from entity metadata.
*
* @param entities must not be {@literal null}.
* @param clazz the entity class, used to determine the index
* @return a {@link Flux} emitting saved entities.
* @since 4.1
*/
default <T> Flux<T> saveAll(Iterable<T> entities, Class<T> clazz) {
List<T> entityList = new ArrayList<>();
entities.forEach(entityList::add);
return saveAll(Mono.just(entityList), clazz);
}
/**
* Index entities in the given {@literal index}. If the {@literal index} is
* {@literal null} or empty the index name provided via entity metadata is used.
*
* @param entities must not be {@literal null}.
@ -103,7 +117,17 @@ public interface ReactiveDocumentOperations {
}
/**
* Index entities under the given {@literal type} in the given {@literal index}. If the {@literal index} is
* Index entities in the index extracted from entity metadata.
*
* @param entities must not be {@literal null}.
* @param clazz the entity class, used to determine the index
* @return a {@link Flux} emitting saved entities.
* @since 4.1
*/
<T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entities, Class<T> clazz);
/**
* Index entities in the given {@literal index}. If the {@literal index} is
* {@literal null} or empty the index name provided via entity metadata is used.
*
* @param entities must not be {@literal null}.
@ -114,6 +138,16 @@ public interface ReactiveDocumentOperations {
*/
<T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entities, IndexCoordinates index);
/**
* Execute a multiGet against elasticsearch for the given ids.
*
* @param query the query defining the ids of the objects to get
* @param clazz the type of the object to be returned, used to determine the index
* @return flux with list of nullable objects
* @since 4.1
*/
<T> Flux<T> multiGet(Query query, Class<T> clazz);
/**
* Execute a multiGet against elasticsearch for the given ids.
*
@ -223,7 +257,7 @@ public interface ReactiveDocumentOperations {
Mono<Boolean> exists(String id, Class<?> entityType, IndexCoordinates index);
/**
* Delete the given entity extracting index and type from entity metadata.
* Delete the given entity extracting index from entity metadata.
*
* @param entity must not be {@literal null}.
* @return a {@link Mono} emitting the {@literal id} of the removed document.
@ -231,7 +265,7 @@ public interface ReactiveDocumentOperations {
Mono<String> delete(Object entity);
/**
* Delete the given entity extracting index and type from entity metadata.
* Delete the given entity extracting index from entity metadata.
*
* @param entity must not be {@literal null}.
* @param index the target index, must not be {@literal null}
@ -249,7 +283,7 @@ public interface ReactiveDocumentOperations {
Mono<String> delete(String id, IndexCoordinates index);
/**
* Delete the entity with given {@literal id} extracting index and type from entity metadata.
* Delete the entity with given {@literal id} extracting index from entity metadata.
*
* @param id must not be {@literal null}.
* @param entityType must not be {@literal null}.
@ -259,7 +293,7 @@ public interface ReactiveDocumentOperations {
Mono<String> delete(String id, Class<?> entityType);
/**
* Delete the entity with given {@literal id} extracting index and type from entity metadata.
* Delete the entity with given {@literal id} extracting index from entity metadata.
*
* @param id must not be {@literal null}.
* @param entityType must not be {@literal null}.
@ -273,7 +307,7 @@ public interface ReactiveDocumentOperations {
}
/**
* Delete the documents matching the given {@link Query} extracting index and type from entity metadata.
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
@ -282,7 +316,7 @@ public interface ReactiveDocumentOperations {
Mono<Long> delete(Query query, Class<?> entityType);
/**
* Delete the documents matching the given {@link Query} extracting index and type from entity metadata.
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.

View File

@ -189,6 +189,11 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
return save(entity, getIndexCoordinatesFor(entity.getClass()));
}
@Override
public <T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entities, Class<T> clazz) {
return saveAll(entities, getIndexCoordinatesFor(clazz));
}
@Override
public <T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entitiesPublisher, IndexCoordinates index) {
@ -215,6 +220,11 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
});
}
@Override
public <T> Flux<T> multiGet(Query query, Class<T> clazz) {
return multiGet(query, clazz, getIndexCoordinatesFor(clazz));
}
@Override
public <T> Flux<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {

View File

@ -25,11 +25,18 @@ import java.util.List;
import java.util.Map;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteRequestBuilder;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.get.MultiGetRequest;
@ -44,6 +51,8 @@ import org.elasticsearch.action.update.UpdateRequestBuilder;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.unit.DistanceUnit;
@ -67,6 +76,7 @@ import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortMode;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
@ -98,11 +108,15 @@ class RequestFactory {
this.elasticsearchConverter = elasticsearchConverter;
}
// region alias
public IndicesAliasesRequest.AliasActions aliasAction(AliasQuery query, IndexCoordinates index) {
Assert.notNull(index, "No index defined for Alias");
Assert.notNull(query.getAliasName(), "No alias defined");
String[] indexNames = index.getIndexNames();
IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add()
.alias(query.getAliasName()).index(index.getIndexName());
.alias(query.getAliasName()).indices(indexNames);
if (query.getFilterBuilder() != null) {
aliasAction.filter(query.getFilterBuilder());
@ -125,6 +139,40 @@ class RequestFactory {
return aliasAction;
}
public GetAliasesRequest getAliasesRequest(IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
return new GetAliasesRequest().indices(indexNames);
}
public IndicesAliasesRequest indicesAddAliasesRequest(AliasQuery query, IndexCoordinates index) {
IndicesAliasesRequest.AliasActions aliasAction = aliasAction(query, index);
IndicesAliasesRequest request = new IndicesAliasesRequest();
request.addAliasAction(aliasAction);
return request;
}
public IndicesAliasesRequest indicesRemoveAliasesRequest(AliasQuery query, IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.remove() //
.indices(indexNames) //
.alias(query.getAliasName());
return Requests.indexAliasesRequest() //
.addAliasAction(aliasAction);
}
IndicesAliasesRequestBuilder indicesRemoveAliasesRequestBuilder(Client client, AliasQuery query,
IndexCoordinates index) {
String indexName = index.getIndexName();
return client.admin().indices().prepareAliases().removeAlias(indexName, query.getAliasName());
}
// endregion
// region bulk
public BulkRequest bulkRequest(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
BulkRequest bulkRequest = new BulkRequest();
@ -195,9 +243,12 @@ class RequestFactory {
return bulkRequestBuilder;
}
@SuppressWarnings("unchecked")
public CreateIndexRequest createIndexRequest(String indexName, @Nullable Document settings) {
CreateIndexRequest request = new CreateIndexRequest(indexName);
// endregion
// region index management
public CreateIndexRequest createIndexRequest(IndexCoordinates index, @Nullable Document settings) {
CreateIndexRequest request = new CreateIndexRequest(index.getIndexName());
if (settings != null) {
request.settings(settings);
@ -205,9 +256,10 @@ class RequestFactory {
return request;
}
@SuppressWarnings("unchecked")
public CreateIndexRequestBuilder createIndexRequestBuilder(Client client, String indexName,
public CreateIndexRequestBuilder createIndexRequestBuilder(Client client, IndexCoordinates index,
@Nullable Document settings) {
String indexName = index.getIndexName();
CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(indexName);
if (settings != null) {
@ -216,9 +268,71 @@ class RequestFactory {
return createIndexRequestBuilder;
}
public GetIndexRequest getIndexRequest(IndexCoordinates index) {
return new GetIndexRequest(index.getIndexNames());
}
public IndicesExistsRequest indicesExistsRequest(IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
return new IndicesExistsRequest(indexNames);
}
public DeleteIndexRequest deleteIndexRequest(IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
return new DeleteIndexRequest(indexNames);
}
public RefreshRequest refreshRequest(IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
return new RefreshRequest(indexNames);
}
public GetSettingsRequest getSettingsRequest(IndexCoordinates index, boolean includeDefaults) {
String[] indexNames = index.getIndexNames();
return new GetSettingsRequest().indices(indexNames).includeDefaults(includeDefaults);
}
public PutMappingRequest putMappingRequest(IndexCoordinates index, Document mapping) {
PutMappingRequest request = new PutMappingRequest(index.getIndexNames());
request.source(mapping);
return request;
}
public PutMappingRequestBuilder putMappingRequestBuilder(Client client, IndexCoordinates index, Document mapping) {
String[] indexNames = index.getIndexNames();
PutMappingRequestBuilder requestBuilder = client.admin().indices().preparePutMapping(indexNames)
.setType(IndexCoordinates.TYPE);
requestBuilder.setSource(mapping);
return requestBuilder;
}
public GetMappingsRequest getMappingsRequest(IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
return new GetMappingsRequest().indices(indexNames);
}
public org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest getMappingsRequest(Client client,
IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
return new org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest().indices(indexNames);
}
// endregion
// region delete
@Deprecated
public DeleteByQueryRequest deleteByQueryRequest(DeleteQuery deleteQuery, IndexCoordinates index) {
DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(index.getIndexNames()) //
String[] indexNames = index.getIndexNames();
DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(indexNames) //
.setQuery(deleteQuery.getQuery()) //
.setAbortOnVersionConflict(false) //
.setRefresh(true);
@ -251,14 +365,23 @@ class RequestFactory {
}
public DeleteRequest deleteRequest(String id, IndexCoordinates index) {
return new DeleteRequest(index.getIndexName(), id);
String indexName = index.getIndexName();
return new DeleteRequest(indexName, id);
}
public DeleteRequestBuilder deleteRequestBuilder(Client client, String id, IndexCoordinates index) {
String indexName = index.getIndexName();
return client.prepareDelete(indexName, IndexCoordinates.TYPE, id);
}
@Deprecated
public DeleteByQueryRequestBuilder deleteByQueryRequestBuilder(Client client, DeleteQuery deleteQuery,
IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
DeleteByQueryRequestBuilder requestBuilder = new DeleteByQueryRequestBuilder(client, DeleteByQueryAction.INSTANCE) //
.source(index.getIndexNames()) //
.source(indexNames) //
.filter(deleteQuery.getQuery()) //
.abortOnVersionConflict(false) //
.refresh(true);
@ -283,16 +406,20 @@ class RequestFactory {
SearchRequestBuilder source = requestBuilder.source();
if (query.isLimiting()) {
// noinspection ConstantConditions
source.setSize(query.getMaxResults());
}
if (query.hasScrollTime()) {
// noinspection ConstantConditions
source.setScroll(TimeValue.timeValueMillis(query.getScrollTime().toMillis()));
}
return requestBuilder;
}
// endregion
// region get
public GetRequest getRequest(String id, IndexCoordinates index) {
return new GetRequest(index.getIndexName(), id);
}
@ -301,36 +428,45 @@ class RequestFactory {
return client.prepareGet(index.getIndexName(), null, id);
}
@Nullable
public HighlightBuilder highlightBuilder(Query query) {
HighlightBuilder highlightBuilder = query.getHighlightQuery().map(HighlightQuery::getHighlightBuilder).orElse(null);
if (highlightBuilder == null) {
if (query instanceof NativeSearchQuery) {
NativeSearchQuery searchQuery = (NativeSearchQuery) query;
if (searchQuery.getHighlightFields() != null || searchQuery.getHighlightBuilder() != null) {
highlightBuilder = searchQuery.getHighlightBuilder();
if (highlightBuilder == null) {
highlightBuilder = new HighlightBuilder();
}
if (searchQuery.getHighlightFields() != null) {
for (HighlightBuilder.Field highlightField : searchQuery.getHighlightFields()) {
highlightBuilder.field(highlightField);
}
}
}
}
}
return highlightBuilder;
public MultiGetRequest multiGetRequest(Query query, IndexCoordinates index) {
MultiGetRequest multiGetRequest = new MultiGetRequest();
getMultiRequestItems(query, index).forEach(multiGetRequest::add);
return multiGetRequest;
}
public IndexRequest indexRequest(IndexQuery query, IndexCoordinates index) {
String indexName = index.getIndexName();
public MultiGetRequestBuilder multiGetRequestBuilder(Client client, Query searchQuery, IndexCoordinates index) {
MultiGetRequestBuilder multiGetRequestBuilder = client.prepareMultiGet();
getMultiRequestItems(searchQuery, index).forEach(multiGetRequestBuilder::add);
return multiGetRequestBuilder;
}
private List<MultiGetRequest.Item> getMultiRequestItems(Query searchQuery, IndexCoordinates index) {
List<MultiGetRequest.Item> items = new ArrayList<>();
if (!isEmpty(searchQuery.getFields())) {
searchQuery.addSourceFilter(new FetchSourceFilter(toArray(searchQuery.getFields()), null));
}
if (!isEmpty(searchQuery.getIds())) {
String indexName = index.getIndexName();
for (String id : searchQuery.getIds()) {
MultiGetRequest.Item item = new MultiGetRequest.Item(indexName, id);
if (searchQuery.getRoute() != null) {
item = item.routing(searchQuery.getRoute());
}
items.add(item);
}
}
return items;
}
// endregion
// region indexing
public IndexRequest indexRequest(IndexQuery query, IndexCoordinates index) {
String indexName = index.getIndexName();
IndexRequest indexRequest;
if (query.getObject() != null) {
@ -405,9 +541,40 @@ class RequestFactory {
return indexRequestBuilder;
}
// endregion
// region search
@Nullable
public HighlightBuilder highlightBuilder(Query query) {
HighlightBuilder highlightBuilder = query.getHighlightQuery().map(HighlightQuery::getHighlightBuilder).orElse(null);
if (highlightBuilder == null) {
if (query instanceof NativeSearchQuery) {
NativeSearchQuery searchQuery = (NativeSearchQuery) query;
if (searchQuery.getHighlightFields() != null || searchQuery.getHighlightBuilder() != null) {
highlightBuilder = searchQuery.getHighlightBuilder();
if (highlightBuilder == null) {
highlightBuilder = new HighlightBuilder();
}
if (searchQuery.getHighlightFields() != null) {
for (HighlightBuilder.Field highlightField : searchQuery.getHighlightFields()) {
highlightBuilder.field(highlightField);
}
}
}
}
}
return highlightBuilder;
}
public MoreLikeThisQueryBuilder moreLikeThisQueryBuilder(MoreLikeThisQuery query, IndexCoordinates index) {
MoreLikeThisQueryBuilder.Item item = new MoreLikeThisQueryBuilder.Item(index.getIndexName(), query.getId());
String indexName = index.getIndexName();
MoreLikeThisQueryBuilder.Item item = new MoreLikeThisQueryBuilder.Item(indexName, query.getId());
String[] fields = null;
if (query.getFields() != null) {
@ -452,6 +619,21 @@ class RequestFactory {
return moreLikeThisQueryBuilder;
}
public SearchRequest searchRequest(SuggestBuilder suggestion, IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
SearchRequest searchRequest = new SearchRequest(indexNames);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.suggest(suggestion);
searchRequest.source(sourceBuilder);
return searchRequest;
}
public SearchRequestBuilder searchRequestBuilder(Client client, SuggestBuilder suggestion, IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
return client.prepareSearch(indexNames).suggest(suggestion);
}
public SearchRequest searchRequest(Query query, @Nullable Class<?> clazz, IndexCoordinates index) {
SearchRequest searchRequest = prepareSearchRequest(query, clazz, index);
@ -492,158 +674,13 @@ class RequestFactory {
return searchRequestBuilder;
}
public UpdateRequest updateRequest(UpdateQuery query, IndexCoordinates index) {
private SearchRequest prepareSearchRequest(Query query, @Nullable Class<?> clazz, IndexCoordinates indexCoordinates) {
UpdateRequest updateRequest = new UpdateRequest(index.getIndexName(), query.getId());
String[] indexNames = indexCoordinates.getIndexNames();
Assert.notNull(indexNames, "No index defined for Query");
Assert.notEmpty(indexNames, "No index defined for Query");
if (query.getScript() != null) {
Map<String, Object> params = query.getParams();
if (params == null) {
params = new HashMap<>();
}
Script script = new Script(ScriptType.INLINE, query.getLang(), query.getScript(), params);
updateRequest.script(script);
}
if (query.getDocument() != null) {
updateRequest.doc(query.getDocument());
}
if (query.getUpsert() != null) {
updateRequest.upsert(query.getUpsert());
}
if (query.getRouting() != null) {
updateRequest.routing(query.getRouting());
}
if (query.getScriptedUpsert() != null) {
updateRequest.scriptedUpsert(query.getScriptedUpsert());
}
if (query.getDocAsUpsert() != null) {
updateRequest.docAsUpsert(query.getDocAsUpsert());
}
if (query.getFetchSource() != null) {
updateRequest.fetchSource(query.getFetchSource());
}
if (query.getFetchSourceIncludes() != null || query.getFetchSourceExcludes() != null) {
List<String> includes = query.getFetchSourceIncludes() != null ? query.getFetchSourceIncludes()
: Collections.emptyList();
List<String> excludes = query.getFetchSourceExcludes() != null ? query.getFetchSourceExcludes()
: Collections.emptyList();
updateRequest.fetchSource(includes.toArray(new String[0]), excludes.toArray(new String[0]));
}
if (query.getIfSeqNo() != null) {
updateRequest.setIfSeqNo(query.getIfSeqNo());
}
if (query.getIfPrimaryTerm() != null) {
updateRequest.setIfPrimaryTerm(query.getIfPrimaryTerm());
}
if (query.getRefresh() != null) {
updateRequest.setRefreshPolicy(query.getRefresh().name().toLowerCase());
}
if (query.getRetryOnConflict() != null) {
updateRequest.retryOnConflict(query.getRetryOnConflict());
}
if (query.getTimeout() != null) {
updateRequest.timeout(query.getTimeout());
}
if (query.getWaitForActiveShards() != null) {
updateRequest.waitForActiveShards(ActiveShardCount.parseString(query.getWaitForActiveShards()));
}
return updateRequest;
}
public UpdateRequestBuilder updateRequestBuilderFor(Client client, UpdateQuery query, IndexCoordinates index) {
UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate(index.getIndexName(), IndexCoordinates.TYPE,
query.getId());
if (query.getScript() != null) {
Map<String, Object> params = query.getParams();
if (params == null) {
params = new HashMap<>();
}
Script script = new Script(ScriptType.INLINE, query.getLang(), query.getScript(), params);
updateRequestBuilder.setScript(script);
}
if (query.getDocument() != null) {
updateRequestBuilder.setDoc(query.getDocument());
}
if (query.getUpsert() != null) {
updateRequestBuilder.setUpsert(query.getUpsert());
}
if (query.getRouting() != null) {
updateRequestBuilder.setRouting(query.getRouting());
}
if (query.getScriptedUpsert() != null) {
updateRequestBuilder.setScriptedUpsert(query.getScriptedUpsert());
}
if (query.getDocAsUpsert() != null) {
updateRequestBuilder.setDocAsUpsert(query.getDocAsUpsert());
}
if (query.getFetchSource() != null) {
updateRequestBuilder.setFetchSource(query.getFetchSource());
}
if (query.getFetchSourceIncludes() != null || query.getFetchSourceExcludes() != null) {
List<String> includes = query.getFetchSourceIncludes() != null ? query.getFetchSourceIncludes()
: Collections.emptyList();
List<String> excludes = query.getFetchSourceExcludes() != null ? query.getFetchSourceExcludes()
: Collections.emptyList();
updateRequestBuilder.setFetchSource(includes.toArray(new String[0]), excludes.toArray(new String[0]));
}
if (query.getIfSeqNo() != null) {
updateRequestBuilder.setIfSeqNo(query.getIfSeqNo());
}
if (query.getIfPrimaryTerm() != null) {
updateRequestBuilder.setIfPrimaryTerm(query.getIfPrimaryTerm());
}
if (query.getRefresh() != null) {
updateRequestBuilder.setRefreshPolicy(query.getRefresh().name().toLowerCase());
}
if (query.getRetryOnConflict() != null) {
updateRequestBuilder.setRetryOnConflict(query.getRetryOnConflict());
}
if (query.getTimeout() != null) {
updateRequestBuilder.setTimeout(query.getTimeout());
}
if (query.getWaitForActiveShards() != null) {
updateRequestBuilder.setWaitForActiveShards(ActiveShardCount.parseString(query.getWaitForActiveShards()));
}
return updateRequestBuilder;
}
private SearchRequest prepareSearchRequest(Query query, @Nullable Class<?> clazz, IndexCoordinates index) {
Assert.notNull(index.getIndexNames(), "No index defined for Query");
Assert.notEmpty(index.getIndexNames(), "No index defined for Query");
SearchRequest request = new SearchRequest(index.getIndexNames());
SearchRequest request = new SearchRequest(indexNames);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.version(true);
sourceBuilder.trackScores(query.getTrackScores());
@ -684,9 +721,7 @@ class RequestFactory {
request.preference(query.getPreference());
}
if (query.getSearchType() != null) {
request.searchType(query.getSearchType());
}
request.searchType(query.getSearchType());
prepareSort(query, sourceBuilder, getPersistentEntity(clazz));
@ -713,120 +748,14 @@ class RequestFactory {
return request;
}
private boolean hasSeqNoPrimaryTermProperty(@Nullable Class<?> entityClass) {
if (entityClass == null) {
return false;
}
if (!elasticsearchConverter.getMappingContext().hasPersistentEntityFor(entityClass)) {
return false;
}
ElasticsearchPersistentEntity<?> entity = elasticsearchConverter.getMappingContext()
.getRequiredPersistentEntity(entityClass);
return entity.hasSeqNoPrimaryTermProperty();
}
public PutMappingRequest putMappingRequest(IndexCoordinates index, Document mapping) {
PutMappingRequest request = new PutMappingRequest(index.getIndexName());
request.source(mapping);
return request;
}
@SuppressWarnings("rawtypes")
public PutMappingRequestBuilder putMappingRequestBuilder(Client client, IndexCoordinates index, Document mapping) {
PutMappingRequestBuilder requestBuilder = client.admin().indices().preparePutMapping(index.getIndexName())
.setType(IndexCoordinates.TYPE);
requestBuilder.setSource(mapping);
return requestBuilder;
}
public MultiGetRequest multiGetRequest(Query query, IndexCoordinates index) {
MultiGetRequest multiGetRequest = new MultiGetRequest();
getMultiRequestItems(query, index).forEach(multiGetRequest::add);
return multiGetRequest;
}
public MultiGetRequestBuilder multiGetRequestBuilder(Client client, Query searchQuery, IndexCoordinates index) {
MultiGetRequestBuilder multiGetRequestBuilder = client.prepareMultiGet();
getMultiRequestItems(searchQuery, index).forEach(multiGetRequestBuilder::add);
return multiGetRequestBuilder;
}
private List<MultiGetRequest.Item> getMultiRequestItems(Query searchQuery, IndexCoordinates index) {
List<MultiGetRequest.Item> items = new ArrayList<>();
if (!isEmpty(searchQuery.getFields())) {
searchQuery.addSourceFilter(new FetchSourceFilter(toArray(searchQuery.getFields()), null));
}
for (String id : searchQuery.getIds()) {
MultiGetRequest.Item item = new MultiGetRequest.Item(index.getIndexName(), id);
if (searchQuery.getRoute() != null) {
item = item.routing(searchQuery.getRoute());
}
items.add(item);
}
return items;
}
private void prepareNativeSearch(NativeSearchQuery query, SearchSourceBuilder sourceBuilder) {
if (!query.getScriptFields().isEmpty()) {
for (ScriptField scriptedField : query.getScriptFields()) {
sourceBuilder.scriptField(scriptedField.fieldName(), scriptedField.script());
}
}
if (query.getCollapseBuilder() != null) {
sourceBuilder.collapse(query.getCollapseBuilder());
}
if (!isEmpty(query.getIndicesBoost())) {
for (IndexBoost indexBoost : query.getIndicesBoost()) {
sourceBuilder.indexBoost(indexBoost.getIndexName(), indexBoost.getBoost());
}
}
if (!isEmpty(query.getAggregations())) {
for (AbstractAggregationBuilder<?> aggregationBuilder : query.getAggregations()) {
sourceBuilder.aggregation(aggregationBuilder);
}
}
}
private void prepareNativeSearch(SearchRequestBuilder searchRequestBuilder, NativeSearchQuery nativeSearchQuery) {
if (!isEmpty(nativeSearchQuery.getScriptFields())) {
for (ScriptField scriptedField : nativeSearchQuery.getScriptFields()) {
searchRequestBuilder.addScriptField(scriptedField.fieldName(), scriptedField.script());
}
}
if (nativeSearchQuery.getCollapseBuilder() != null) {
searchRequestBuilder.setCollapse(nativeSearchQuery.getCollapseBuilder());
}
if (!isEmpty(nativeSearchQuery.getIndicesBoost())) {
for (IndexBoost indexBoost : nativeSearchQuery.getIndicesBoost()) {
searchRequestBuilder.addIndexBoost(indexBoost.getIndexName(), indexBoost.getBoost());
}
}
if (!isEmpty(nativeSearchQuery.getAggregations())) {
for (AbstractAggregationBuilder<?> aggregationBuilder : nativeSearchQuery.getAggregations()) {
searchRequestBuilder.addAggregation(aggregationBuilder);
}
}
}
private SearchRequestBuilder prepareSearchRequestBuilder(Query query, Client client, @Nullable Class<?> clazz,
IndexCoordinates index) {
Assert.notNull(index.getIndexNames(), "No index defined for Query");
Assert.notEmpty(index.getIndexNames(), "No index defined for Query");
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index.getIndexNames()) //
String[] indexNames = index.getIndexNames();
Assert.notNull(indexNames, "No index defined for Query");
Assert.notEmpty(indexNames, "No index defined for Query");
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(indexNames) //
.setSearchType(query.getSearchType()) //
.setVersion(true) //
.setTrackScores(query.getTrackScores());
@ -890,6 +819,56 @@ class RequestFactory {
return searchRequestBuilder;
}
private void prepareNativeSearch(NativeSearchQuery query, SearchSourceBuilder sourceBuilder) {
if (!query.getScriptFields().isEmpty()) {
for (ScriptField scriptedField : query.getScriptFields()) {
sourceBuilder.scriptField(scriptedField.fieldName(), scriptedField.script());
}
}
if (query.getCollapseBuilder() != null) {
sourceBuilder.collapse(query.getCollapseBuilder());
}
if (!isEmpty(query.getIndicesBoost())) {
for (IndexBoost indexBoost : query.getIndicesBoost()) {
sourceBuilder.indexBoost(indexBoost.getIndexName(), indexBoost.getBoost());
}
}
if (!isEmpty(query.getAggregations())) {
for (AbstractAggregationBuilder<?> aggregationBuilder : query.getAggregations()) {
sourceBuilder.aggregation(aggregationBuilder);
}
}
}
private void prepareNativeSearch(SearchRequestBuilder searchRequestBuilder, NativeSearchQuery nativeSearchQuery) {
if (!isEmpty(nativeSearchQuery.getScriptFields())) {
for (ScriptField scriptedField : nativeSearchQuery.getScriptFields()) {
searchRequestBuilder.addScriptField(scriptedField.fieldName(), scriptedField.script());
}
}
if (nativeSearchQuery.getCollapseBuilder() != null) {
searchRequestBuilder.setCollapse(nativeSearchQuery.getCollapseBuilder());
}
if (!isEmpty(nativeSearchQuery.getIndicesBoost())) {
for (IndexBoost indexBoost : nativeSearchQuery.getIndicesBoost()) {
searchRequestBuilder.addIndexBoost(indexBoost.getIndexName(), indexBoost.getBoost());
}
}
if (!isEmpty(nativeSearchQuery.getAggregations())) {
for (AbstractAggregationBuilder<?> aggregationBuilder : nativeSearchQuery.getAggregations()) {
searchRequestBuilder.addAggregation(aggregationBuilder);
}
}
}
@SuppressWarnings("rawtypes")
private void prepareSort(Query query, SearchSourceBuilder sourceBuilder,
@Nullable ElasticsearchPersistentEntity<?> entity) {
@ -961,7 +940,159 @@ class RequestFactory {
}
}
}
// endregion
// region update
public UpdateRequest updateRequest(UpdateQuery query, IndexCoordinates index) {
String indexName = index.getIndexName();
UpdateRequest updateRequest = new UpdateRequest(indexName, query.getId());
if (query.getScript() != null) {
Map<String, Object> params = query.getParams();
if (params == null) {
params = new HashMap<>();
}
Script script = new Script(ScriptType.INLINE, query.getLang(), query.getScript(), params);
updateRequest.script(script);
}
if (query.getDocument() != null) {
updateRequest.doc(query.getDocument());
}
if (query.getUpsert() != null) {
updateRequest.upsert(query.getUpsert());
}
if (query.getRouting() != null) {
updateRequest.routing(query.getRouting());
}
if (query.getScriptedUpsert() != null) {
updateRequest.scriptedUpsert(query.getScriptedUpsert());
}
if (query.getDocAsUpsert() != null) {
updateRequest.docAsUpsert(query.getDocAsUpsert());
}
if (query.getFetchSource() != null) {
updateRequest.fetchSource(query.getFetchSource());
}
if (query.getFetchSourceIncludes() != null || query.getFetchSourceExcludes() != null) {
List<String> includes = query.getFetchSourceIncludes() != null ? query.getFetchSourceIncludes()
: Collections.emptyList();
List<String> excludes = query.getFetchSourceExcludes() != null ? query.getFetchSourceExcludes()
: Collections.emptyList();
updateRequest.fetchSource(includes.toArray(new String[0]), excludes.toArray(new String[0]));
}
if (query.getIfSeqNo() != null) {
updateRequest.setIfSeqNo(query.getIfSeqNo());
}
if (query.getIfPrimaryTerm() != null) {
updateRequest.setIfPrimaryTerm(query.getIfPrimaryTerm());
}
if (query.getRefresh() != null) {
updateRequest.setRefreshPolicy(query.getRefresh().name().toLowerCase());
}
if (query.getRetryOnConflict() != null) {
updateRequest.retryOnConflict(query.getRetryOnConflict());
}
if (query.getTimeout() != null) {
updateRequest.timeout(query.getTimeout());
}
if (query.getWaitForActiveShards() != null) {
updateRequest.waitForActiveShards(ActiveShardCount.parseString(query.getWaitForActiveShards()));
}
return updateRequest;
}
public UpdateRequestBuilder updateRequestBuilderFor(Client client, UpdateQuery query, IndexCoordinates index) {
String indexName = index.getIndexName();
UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate(indexName, IndexCoordinates.TYPE, query.getId());
if (query.getScript() != null) {
Map<String, Object> params = query.getParams();
if (params == null) {
params = new HashMap<>();
}
Script script = new Script(ScriptType.INLINE, query.getLang(), query.getScript(), params);
updateRequestBuilder.setScript(script);
}
if (query.getDocument() != null) {
updateRequestBuilder.setDoc(query.getDocument());
}
if (query.getUpsert() != null) {
updateRequestBuilder.setUpsert(query.getUpsert());
}
if (query.getRouting() != null) {
updateRequestBuilder.setRouting(query.getRouting());
}
if (query.getScriptedUpsert() != null) {
updateRequestBuilder.setScriptedUpsert(query.getScriptedUpsert());
}
if (query.getDocAsUpsert() != null) {
updateRequestBuilder.setDocAsUpsert(query.getDocAsUpsert());
}
if (query.getFetchSource() != null) {
updateRequestBuilder.setFetchSource(query.getFetchSource());
}
if (query.getFetchSourceIncludes() != null || query.getFetchSourceExcludes() != null) {
List<String> includes = query.getFetchSourceIncludes() != null ? query.getFetchSourceIncludes()
: Collections.emptyList();
List<String> excludes = query.getFetchSourceExcludes() != null ? query.getFetchSourceExcludes()
: Collections.emptyList();
updateRequestBuilder.setFetchSource(includes.toArray(new String[0]), excludes.toArray(new String[0]));
}
if (query.getIfSeqNo() != null) {
updateRequestBuilder.setIfSeqNo(query.getIfSeqNo());
}
if (query.getIfPrimaryTerm() != null) {
updateRequestBuilder.setIfPrimaryTerm(query.getIfPrimaryTerm());
}
if (query.getRefresh() != null) {
updateRequestBuilder.setRefreshPolicy(query.getRefresh().name().toLowerCase());
}
if (query.getRetryOnConflict() != null) {
updateRequestBuilder.setRetryOnConflict(query.getRetryOnConflict());
}
if (query.getTimeout() != null) {
updateRequestBuilder.setTimeout(query.getTimeout());
}
if (query.getWaitForActiveShards() != null) {
updateRequestBuilder.setWaitForActiveShards(ActiveShardCount.parseString(query.getWaitForActiveShards()));
}
return updateRequestBuilder;
}
// endregion
// region helper functions
private QueryBuilder getQuery(Query query) {
QueryBuilder elasticsearchQuery;
@ -981,22 +1112,7 @@ class RequestFactory {
return elasticsearchQuery;
}
public IndicesAliasesRequest indicesAddAliasesRequest(AliasQuery query, IndexCoordinates index) {
IndicesAliasesRequest.AliasActions aliasAction = aliasAction(query, index);
IndicesAliasesRequest request = new IndicesAliasesRequest();
request.addAliasAction(aliasAction);
return request;
}
public IndicesAliasesRequest indicesRemoveAliasesRequest(AliasQuery query, IndexCoordinates index) {
IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.remove() //
.index(index.getIndexName()) //
.alias(query.getAliasName());
return Requests.indexAliasesRequest() //
.addAliasAction(aliasAction);
}
@Nullable
private QueryBuilder getFilter(Query query) {
QueryBuilder elasticsearchFilter;
@ -1036,10 +1152,10 @@ class RequestFactory {
private VersionType retrieveVersionTypeFromPersistentEntity(Class<?> clazz) {
if (clazz != null) {
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz).getVersionType();
}
return VersionType.EXTERNAL;
VersionType versionType = elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz)
.getVersionType();
return versionType != null ? versionType : VersionType.EXTERNAL;
}
private String[] toArray(List<String> values) {
@ -1047,4 +1163,21 @@ class RequestFactory {
return values.toArray(valuesAsArray);
}
private boolean hasSeqNoPrimaryTermProperty(@Nullable Class<?> entityClass) {
if (entityClass == null) {
return false;
}
if (!elasticsearchConverter.getMappingContext().hasPersistentEntityFor(entityClass)) {
return false;
}
ElasticsearchPersistentEntity<?> entity = elasticsearchConverter.getMappingContext()
.getRequiredPersistentEntity(entityClass);
return entity.hasSeqNoPrimaryTermProperty();
}
// endregion
}

View File

@ -241,6 +241,16 @@ public interface SearchOperations {
// endregion
/**
* Does a suggest query
*
* @param suggestion the query
* @param the entity class
* @return the suggest response
* @since 4.1
*/
SearchResponse suggest(SuggestBuilder suggestion, Class<?> clazz);
/**
* Does a suggest query
*
@ -277,6 +287,17 @@ public interface SearchOperations {
return content.isEmpty() ? null : content.get(0);
}
/**
* Execute the multi search query against elasticsearch and return result as {@link List} of {@link SearchHits}.
*
* @param queries the queries to execute
* @param clazz the entity clazz
* @param <T> element return type
* @return list of SearchHits
* @since 4.1
*/
<T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz);
/**
* Execute the multi search query against elasticsearch and return result as {@link List} of {@link SearchHits}.
*
@ -288,6 +309,16 @@ public interface SearchOperations {
*/
<T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz, IndexCoordinates index);
/**
* Execute the multi search query against elasticsearch and return result as {@link List} of {@link SearchHits}.
*
* @param queries the queries to execute
* @param classes the entity classes
* @return list of SearchHits
* @since 4.1
*/
List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes);
/**
* Execute the multi search query against elasticsearch and return result as {@link List} of {@link SearchHits}.
*

View File

@ -73,8 +73,12 @@ public interface ElasticsearchConverter
* @return will not be {@literal null}.
*/
default Document mapObject(@Nullable Object source) {
Document target = Document.create();
write(source, target);
if (source != null) {
write(source, target);
}
return target;
}
// endregion

View File

@ -87,9 +87,9 @@ public class MappingElasticsearchConverter
private final GenericConversionService conversionService;
private CustomConversions conversions = new ElasticsearchCustomConversions(Collections.emptyList());
private EntityInstantiators instantiators = new EntityInstantiators();
private final EntityInstantiators instantiators = new EntityInstantiators();
private ElasticsearchTypeMapper typeMapper;
private final ElasticsearchTypeMapper typeMapper;
private ConcurrentHashMap<String, Integer> propertyWarnings = new ConcurrentHashMap<>();

View File

@ -58,6 +58,7 @@ public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, El
@Nullable
ElasticsearchPersistentProperty getParentIdProperty();
@Nullable
String settingPath();
@Nullable

View File

@ -15,14 +15,10 @@
*/
package org.springframework.data.elasticsearch.core.mapping;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
/**
* SimpleElasticsearchMappingContext
@ -30,21 +26,14 @@ import org.springframework.lang.Nullable;
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Mark Paluch
* @author Peter-Josef Meisch
*/
public class SimpleElasticsearchMappingContext
extends AbstractMappingContext<SimpleElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty>
implements ApplicationContextAware {
private @Nullable ApplicationContext context;
extends AbstractMappingContext<SimpleElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> {
@Override
protected <T> SimpleElasticsearchPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
SimpleElasticsearchPersistentEntity<T> persistentEntity = new SimpleElasticsearchPersistentEntity<>(
typeInformation);
if (context != null) {
persistentEntity.setApplicationContext(context);
}
return persistentEntity;
return new SimpleElasticsearchPersistentEntity<>(typeInformation);
}
@Override
@ -52,9 +41,4 @@ public class SimpleElasticsearchMappingContext
SimpleElasticsearchPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new SimpleElasticsearchPersistentProperty(property, owner, simpleTypeHolder);
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
}

View File

@ -22,11 +22,6 @@ import java.util.concurrent.atomic.AtomicReference;
import org.elasticsearch.index.VersionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryAccessor;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Parent;
import org.springframework.data.elasticsearch.annotations.Setting;
@ -35,10 +30,11 @@ import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@ -55,12 +51,10 @@ import org.springframework.util.Assert;
* @author Roman Puchkovskiy
*/
public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntity<T, ElasticsearchPersistentProperty>
implements ElasticsearchPersistentEntity<T>, ApplicationContextAware {
implements ElasticsearchPersistentEntity<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleElasticsearchPersistentEntity.class);
private final StandardEvaluationContext context;
private final SpelExpressionParser parser;
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private @Nullable String indexName;
private boolean useServerConfiguration;
@ -76,12 +70,11 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
private @Nullable VersionType versionType;
private boolean createIndexAndMapping;
private final Map<String, ElasticsearchPersistentProperty> fieldNamePropertyCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Expression> indexNameExpressions = new ConcurrentHashMap<>();
public SimpleElasticsearchPersistentEntity(TypeInformation<T> typeInformation) {
super(typeInformation);
this.context = new StandardEvaluationContext();
this.parser = new SpelExpressionParser();
Class<T> clazz = typeInformation.getType();
if (clazz.isAnnotationPresent(Document.class)) {
@ -102,26 +95,13 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context.addPropertyAccessor(new BeanFactoryAccessor());
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
context.setRootObject(applicationContext);
}
private String getIndexName() {
if (indexName != null) {
Expression expression = parser.parseExpression(indexName, ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(context, String.class);
}
return getTypeInformation().getType().getSimpleName();
return indexName != null ? indexName : getTypeInformation().getType().getSimpleName();
}
@Override
public IndexCoordinates getIndexCoordinates() {
return IndexCoordinates.of(getIndexName());
return resolve(IndexCoordinates.of(getIndexName()));
}
@Nullable
@ -294,4 +274,47 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
public ElasticsearchPersistentProperty getSeqNoPrimaryTermProperty() {
return seqNoPrimaryTermProperty;
}
// region SpEL handling
/**
* resolves all the names in the IndexCoordinates object. If a name cannot be resolved, the original name is returned.
*
* @param indexCoordinates IndexCoordinates with names to resolve
* @return IndexCoordinates with resolved names
*/
private IndexCoordinates resolve(IndexCoordinates indexCoordinates) {
EvaluationContext context = getEvaluationContext(null);
String[] indexNames = indexCoordinates.getIndexNames();
String[] resolvedNames = new String[indexNames.length];
for (int i = 0; i < indexNames.length; i++) {
String indexName = indexNames[i];
resolvedNames[i] = resolve(context, indexName);
}
return IndexCoordinates.of(resolvedNames);
}
/**
* tries to resolve the given name. If this is not successful, the original value is returned
*
* @param context SpEL evaluation context
* @param name name to resolve
* @return the resolved name or the input name if it cannot be resolved
*/
private String resolve(EvaluationContext context, String name) {
Assert.notNull(name, "name must not be null");
Expression expression = indexNameExpressions.computeIfAbsent(name, s -> {
Expression expr = PARSER.parseExpression(name, ParserContext.TEMPLATE_EXPRESSION);
return expr instanceof LiteralExpression ? null : expr;
});
String resolvedName = expression != null ? expression.getValue(context, String.class) : null;
return resolvedName != null ? resolvedName : name;
}
// endregion
}

View File

@ -20,6 +20,9 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@ -50,6 +53,8 @@ import org.springframework.util.StringUtils;
public class SimpleElasticsearchPersistentProperty extends
AnnotationBasedPersistentProperty<ElasticsearchPersistentProperty> implements ElasticsearchPersistentProperty {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleElasticsearchPersistentProperty.class);
private static final List<String> SUPPORTED_ID_PROPERTY_NAMES = Arrays.asList("id", "document");
private final boolean isScore;
@ -66,6 +71,15 @@ public class SimpleElasticsearchPersistentProperty extends
this.annotatedFieldName = getAnnotatedFieldName();
this.isId = super.isIdProperty() || SUPPORTED_ID_PROPERTY_NAMES.contains(getFieldName());
// deprecated since 4.1
@Deprecated
boolean isIdWithoutAnnotation = isId && !isAnnotationPresent(Id.class);
if (isIdWithoutAnnotation) {
LOGGER.warn("Using the property name of '{}' to identify the id property is deprecated."
+ " Please annotate the id property with '@Id'", getName());
}
this.isScore = isAnnotationPresent(Score.class);
this.isParent = isAnnotationPresent(Parent.class);
this.isSeqNoPrimaryTerm = SeqNoPrimaryTerm.class.isAssignableFrom(getRawType());

View File

@ -39,6 +39,7 @@ public interface ElasticsearchEntityInformation<T, ID> extends EntityInformation
@Nullable
Long getVersion(T entity);
@Nullable
VersionType getVersionType();
@Nullable

View File

@ -16,9 +16,9 @@
package org.springframework.data.elasticsearch.repository.support;
import org.elasticsearch.index.VersionType;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.repository.core.support.PersistentEntityInformation;
/**
@ -35,44 +35,34 @@ import org.springframework.data.repository.core.support.PersistentEntityInformat
* @author Christoph Strobl
* @author Ivan Greene
* @author Sylvain Laurent
* @author Peter-Josef Meisch
*/
public class MappingElasticsearchEntityInformation<T, ID> extends PersistentEntityInformation<T, ID>
implements ElasticsearchEntityInformation<T, ID> {
private final ElasticsearchPersistentEntity<T> entityMetadata;
private final IndexCoordinates indexCoordinates;
private final VersionType versionType;
private final ElasticsearchPersistentEntity<T> persistentEntity;
public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity<T> entity) {
this(entity, entity.getIndexCoordinates(), entity.getVersionType());
}
public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity<T> entity,
IndexCoordinates indexCoordinates, VersionType versionType) {
super(entity);
this.entityMetadata = entity;
this.indexCoordinates = indexCoordinates;
this.versionType = versionType;
public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity<T> persistentEntity) {
super(persistentEntity);
this.persistentEntity = persistentEntity;
}
@Override
public String getIdAttribute() {
return entityMetadata.getRequiredIdProperty().getFieldName();
return persistentEntity.getRequiredIdProperty().getFieldName();
}
@Override
public IndexCoordinates getIndexCoordinates() {
return indexCoordinates;
return persistentEntity.getIndexCoordinates();
}
@Override
public Long getVersion(T entity) {
ElasticsearchPersistentProperty versionProperty = entityMetadata.getVersionProperty();
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
try {
return versionProperty != null ? (Long) entityMetadata.getPropertyAccessor(entity).getProperty(versionProperty)
return versionProperty != null ? (Long) persistentEntity.getPropertyAccessor(entity).getProperty(versionProperty)
: null;
} catch (Exception e) {
throw new IllegalStateException("failed to load version field", e);
@ -81,15 +71,15 @@ public class MappingElasticsearchEntityInformation<T, ID> extends PersistentEnti
@Override
public VersionType getVersionType() {
return versionType;
return persistentEntity.getVersionType();
}
@Override
public String getParentId(T entity) {
ElasticsearchPersistentProperty parentProperty = entityMetadata.getParentIdProperty();
ElasticsearchPersistentProperty parentProperty = persistentEntity.getParentIdProperty();
try {
return parentProperty != null ? (String) entityMetadata.getPropertyAccessor(entity).getProperty(parentProperty)
return parentProperty != null ? (String) persistentEntity.getPropertyAccessor(entity).getProperty(parentProperty)
: null;
} catch (Exception e) {
throw new IllegalStateException("failed to load parent ID: " + e, e);

View File

@ -113,8 +113,7 @@ public class ReactiveElasticsearchRepositoryFactory extends ReactiveRepositoryFa
@Nullable RepositoryInformation information) {
ElasticsearchPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(domainClass);
return new MappingElasticsearchEntityInformation<>((ElasticsearchPersistentEntity<T>) entity,
entity.getIndexCoordinates(), entity.getVersionType());
return new MappingElasticsearchEntityInformation<>((ElasticsearchPersistentEntity<T>) entity);
}
@Override

View File

@ -492,6 +492,7 @@ class ElasticsearchPartQueryTests {
private String getQueryBuilder(String methodName, Class<?>[] parameterClasses, Object[] parameters)
throws NoSuchMethodException {
Method method = SampleRepository.class.getMethod(methodName, parameterClasses);
ElasticsearchQueryMethod queryMethod = new ElasticsearchQueryMethod(method,
new DefaultRepositoryMetadata(SampleRepository.class), new SpelAwareProxyProjectionFactory(),

View File

@ -2781,13 +2781,23 @@ public abstract class ElasticsearchTemplateTests {
AliasMetaData aliasMetaData1 = aliasMetaData.get(0);
assertThat(aliasMetaData1).isNotNull();
assertThat(aliasMetaData1.alias()).isEqualTo(alias1);
assertThat(aliasMetaData1.indexRouting()).isEqualTo("0");
if (aliasMetaData1.alias().equals(alias1)) {
assertThat(aliasMetaData1.indexRouting()).isEqualTo("0");
} else if (aliasMetaData1.alias().equals(alias2)) {
assertThat(aliasMetaData1.searchRouting()).isEqualTo("1");
} else {
fail("unknown alias");
}
AliasMetaData aliasMetaData2 = aliasMetaData.get(1);
assertThat(aliasMetaData2).isNotNull();
assertThat(aliasMetaData2.alias()).isEqualTo(alias2);
assertThat(aliasMetaData2.searchRouting()).isEqualTo("1");
if (aliasMetaData2.alias().equals(alias1)) {
assertThat(aliasMetaData2.indexRouting()).isEqualTo("0");
} else if (aliasMetaData2.alias().equals(alias2)) {
assertThat(aliasMetaData2.searchRouting()).isEqualTo("1");
} else {
fail("unknown alias");
}
// cleanup
indexOperations.removeAlias(aliasQuery1);

View File

@ -87,35 +87,37 @@ public class DynamicIndexEntityTests {
int initialCallsCount = indexNameProvider.callsCount;
indexNameProvider.setIndexName("index1");
indexNameProvider.indexName = "index1";
repository.save(new DynamicIndexEntity());
assertThat(indexNameProvider.callsCount > initialCallsCount).isTrue();
assertThat(repository.count()).isEqualTo(1L);
indexNameProvider.setIndexName("index2");
indexNameProvider.indexName = "index2";
assertThat(repository.count()).isEqualTo(0L);
}
@Test // DATAES-821
void indexOpsShouldUseDynamicallyProvidedName() {
indexNameProvider.setIndexName("test-dynamic");
IndexOperations indexOps = operations.indexOps(DynamicIndexEntity.class);
indexNameProvider.indexName = "index-dynamic";
indexNameProvider.callsCount = 0;
int initialCallsCount = indexNameProvider.callsCount;
operations.indexOps(IndexCoordinates.of("index-dynamic")).delete();
IndexOperations indexOps = operations.indexOps(DynamicIndexEntity.class);
indexOps.create();
indexOps.refresh();
indexOps.refresh();
indexOps.delete();
indexOps.delete(); // internally calls doExists
assertThat(indexNameProvider.callsCount - initialCallsCount).isEqualTo(4);
assertThat(indexNameProvider.callsCount).isGreaterThan(0);
}
static class IndexNameProvider {
private String indexName;
int callsCount;
private int callsCount;
public String getIndexName() {
@ -123,10 +125,6 @@ public class DynamicIndexEntityTests {
return indexName;
}
public void setIndexName(String indexName) {
this.indexName = indexName;
}
}
@Document(indexName = "#{@indexNameProvider.getIndexName()}", createIndex = false)