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. 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]] [[elasticsearch-migration-guide-4.0-4.1.removal]]
== Removals == 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. 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.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.AliasQuery; import org.springframework.data.elasticsearch.core.query.AliasQuery;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -55,20 +56,24 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
protected final RequestFactory requestFactory; protected final RequestFactory requestFactory;
@Nullable protected final Class<?> boundClass; @Nullable protected final Class<?> boundClass;
private final IndexCoordinates boundIndex; @Nullable private final IndexCoordinates boundIndex;
public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter, Class<?> boundClass) { public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter, Class<?> boundClass) {
Assert.notNull(boundClass, "boundClass may not be null");
this.elasticsearchConverter = elasticsearchConverter; this.elasticsearchConverter = elasticsearchConverter;
requestFactory = new RequestFactory(elasticsearchConverter); requestFactory = new RequestFactory(elasticsearchConverter);
this.boundClass = boundClass; this.boundClass = boundClass;
this.boundIndex = getIndexCoordinatesFor(boundClass); this.boundIndex = null;
} }
public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter, IndexCoordinates boundIndex) { public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter, IndexCoordinates boundIndex) {
Assert.notNull(boundIndex, "boundIndex may not be null");
this.elasticsearchConverter = elasticsearchConverter; this.elasticsearchConverter = elasticsearchConverter;
requestFactory = new RequestFactory(elasticsearchConverter); requestFactory = new RequestFactory(elasticsearchConverter);
this.boundClass = null; this.boundClass = null;
this.boundIndex = boundIndex; this.boundIndex = boundIndex;
} }
@ -85,48 +90,59 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
@Override @Override
public boolean create() { public boolean create() {
IndexCoordinates index;
Document settings = null;
if (boundClass != null) { if (boundClass != null) {
Class<?> clazz = boundClass; Class<?> clazz = boundClass;
String indexName = getIndexCoordinates().getIndexName(); ElasticsearchPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(clazz);
index = persistentEntity.getIndexCoordinates();
if (clazz.isAnnotationPresent(Setting.class)) { if (clazz.isAnnotationPresent(Setting.class)) {
String settingPath = clazz.getAnnotation(Setting.class).settingPath(); String settingPath = clazz.getAnnotation(Setting.class).settingPath();
if (hasText(settingPath)) { if (hasText(settingPath)) {
String settings = ResourceUtil.readFileFromClasspath(settingPath); String fileSettings = ResourceUtil.readFileFromClasspath(settingPath);
if (hasText(settings)) { if (hasText(fileSettings)) {
return doCreate(indexName, Document.parse(settings)); settings = Document.parse(fileSettings);
} }
} else { } else {
LOGGER.info("settingPath in @Setting has to be defined. Using default instead."); 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 @Override
public boolean create(Document settings) { 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 @Override
public boolean delete() { public boolean delete() {
return doDelete(getIndexCoordinates().getIndexName()); return doDelete(getIndexCoordinates());
} }
protected abstract boolean doDelete(String indexName); protected abstract boolean doDelete(IndexCoordinates index);
@Override @Override
public boolean exists() { public boolean exists() {
return doExists(getIndexCoordinates().getIndexName()); return doExists(getIndexCoordinates());
} }
protected abstract boolean doExists(String indexName); protected abstract boolean doExists(IndexCoordinates index);
@Override @Override
public boolean putMapping(Document mapping) { public boolean putMapping(Document mapping) {
@ -149,10 +165,10 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
@Override @Override
public Map<String, Object> getSettings(boolean includeDefaults) { 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 @Override
public void refresh() { public void refresh() {
@ -170,10 +186,10 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
@Override @Override
public List<AliasMetaData> queryForAlias() { 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 @Override
public boolean removeAlias(AliasQuery query) { public boolean removeAlias(AliasQuery query) {
@ -238,14 +254,15 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz); 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() { 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) { 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.action.search.SearchResponse;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder; import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; 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.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; 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.GetQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery; import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder; 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.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm; 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.elasticsearch.support.VersionInfo;
import org.springframework.data.mapping.callback.EntityCallbacks; import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.util.CloseableIterator; import org.springframework.data.util.CloseableIterator;
@ -199,6 +202,11 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return get(query.getId(), clazz, index); return get(query.getId(), clazz, index);
} }
@Override
public <T> List<T> multiGet(Query query, Class<T> clazz) {
return multiGet(query, clazz, getIndexCoordinatesFor(clazz));
}
@Override @Override
@Nullable @Nullable
public <T> T queryForObject(GetQuery query, Class<T> clazz) { 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)); return this.delete(id, getIndexCoordinatesFor(entityType));
} }
@Override
public void delete(Query query, Class<?> clazz) {
delete(query, getIndexCoordinatesFor(clazz));
}
@Override @Override
public String delete(Object entity) { public String delete(Object entity) {
return delete(entity, getIndexCoordinatesFor(entity.getClass())); return delete(entity, getIndexCoordinatesFor(entity.getClass()));
@ -235,6 +248,22 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
public String delete(Object entity, IndexCoordinates index) { public String delete(Object entity, IndexCoordinates index) {
return this.delete(getEntityId(entity), 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 // endregion
// region SearchOperations // region SearchOperations
@ -282,6 +311,11 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return search(new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).build(), clazz, index); 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 @Override
public <T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz, IndexCoordinates index) { public <T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz, IndexCoordinates index) {
MultiSearchRequest request = new MultiSearchRequest(); MultiSearchRequest request = new MultiSearchRequest();
@ -300,9 +334,46 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return res; 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 @Override
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes, public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes,
IndexCoordinates index) { 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(); MultiSearchRequest request = new MultiSearchRequest();
Iterator<Class<?>> it = classes.iterator(); Iterator<Class<?>> it = classes.iterator();
for (Query query : queries) { for (Query query : queries) {
@ -356,6 +427,12 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
abstract protected void searchScrollClear(List<String> scrollIds); abstract protected void searchScrollClear(List<String> scrollIds);
abstract protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request); abstract protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request);
@Override
public SearchResponse suggest(SuggestBuilder suggestion, Class<?> clazz) {
return suggest(suggestion, getIndexCoordinatesFor(clazz));
}
// endregion // endregion
// region Helper methods // region Helper methods

View File

@ -15,40 +15,31 @@
*/ */
package org.springframework.data.elasticsearch.core; package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.client.Requests.*;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; 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.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; 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.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest; 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.client.indices.PutMappingRequest;
import org.elasticsearch.cluster.metadata.AliasMetaData; 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.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.AliasQuery; import org.springframework.data.elasticsearch.core.query.AliasQuery;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; 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. * {@link IndexOperations} implementation using the RestClient.
* *
@ -58,7 +49,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
*/ */
class DefaultIndexOperations extends AbstractDefaultIndexOperations implements IndexOperations { class DefaultIndexOperations extends AbstractDefaultIndexOperations implements IndexOperations {
private ElasticsearchRestTemplate restTemplate; private final ElasticsearchRestTemplate restTemplate;
public DefaultIndexOperations(ElasticsearchRestTemplate restTemplate, Class<?> boundClass) { public DefaultIndexOperations(ElasticsearchRestTemplate restTemplate, Class<?> boundClass) {
super(restTemplate.getElasticsearchConverter(), boundClass); super(restTemplate.getElasticsearchConverter(), boundClass);
@ -71,27 +62,29 @@ class DefaultIndexOperations extends AbstractDefaultIndexOperations implements I
} }
@Override @Override
protected boolean doCreate(String indexName, @Nullable Document settings) { protected boolean doCreate(IndexCoordinates index, @Nullable Document settings) {
CreateIndexRequest request = requestFactory.createIndexRequest(indexName, settings); CreateIndexRequest request = requestFactory.createIndexRequest(index, settings);
return restTemplate.execute(client -> client.indices().create(request, RequestOptions.DEFAULT).isAcknowledged()); return restTemplate.execute(client -> client.indices().create(request, RequestOptions.DEFAULT).isAcknowledged());
} }
@Override @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)) { if (doExists(index)) {
DeleteIndexRequest request = new DeleteIndexRequest(indexName); DeleteIndexRequest deleteIndexRequest = requestFactory.deleteIndexRequest(index);
return restTemplate.execute(client -> client.indices().delete(request, RequestOptions.DEFAULT).isAcknowledged()); return restTemplate
.execute(client -> client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT).isAcknowledged());
} }
return false; return false;
} }
@Override @Override
protected boolean doExists(String indexName) { protected boolean doExists(IndexCoordinates index) {
GetIndexRequest request = new GetIndexRequest(indexName);
return restTemplate.execute(client -> client.indices().exists(request, RequestOptions.DEFAULT)); GetIndexRequest getIndexRequest = requestFactory.getIndexRequest(index);
return restTemplate.execute(client -> client.indices().exists(getIndexRequest, RequestOptions.DEFAULT));
} }
@Override @Override
@ -109,16 +102,20 @@ class DefaultIndexOperations extends AbstractDefaultIndexOperations implements I
Assert.notNull(index, "No index defined for getMapping()"); Assert.notNull(index, "No index defined for getMapping()");
GetMappingsRequest mappingsRequest = requestFactory.getMappingsRequest(index);
return restTemplate.execute(client -> { return restTemplate.execute(client -> {
RestClient restClient = client.getLowLevelClient(); GetMappingsResponse mapping = client.indices().getMapping(mappingsRequest, RequestOptions.DEFAULT);
Request request = new Request("GET", '/' + index.getIndexName() + "/_mapping"); // we only return data for the first index name that was requested (always have done so)
Response response = restClient.performRequest(request); String index1 = mappingsRequest.indices()[0];
return convertMappingResponse(EntityUtils.toString(response.getEntity())); Map<String, Object> result = new LinkedHashMap<>();
return mapping.mappings().get(index1).getSourceAsMap();
}); });
} }
@Override @Override
protected boolean doAddAlias(AliasQuery query, IndexCoordinates index) { protected boolean doAddAlias(AliasQuery query, IndexCoordinates index) {
IndicesAliasesRequest request = requestFactory.indicesAddAliasesRequest(query, index); IndicesAliasesRequest request = requestFactory.indicesAddAliasesRequest(query, index);
return restTemplate return restTemplate
.execute(client -> client.indices().updateAliases(request, RequestOptions.DEFAULT).isAcknowledged()); .execute(client -> client.indices().updateAliases(request, RequestOptions.DEFAULT).isAcknowledged());
@ -136,34 +133,28 @@ class DefaultIndexOperations extends AbstractDefaultIndexOperations implements I
} }
@Override @Override
protected List<AliasMetaData> doQueryForAlias(String indexName) { protected List<AliasMetaData> doQueryForAlias(IndexCoordinates index) {
List<AliasMetaData> aliases = null;
GetAliasesRequest getAliasesRequest = requestFactory.getAliasesRequest(index);
return restTemplate.execute(client -> { return restTemplate.execute(client -> {
RestClient restClient = client.getLowLevelClient(); GetAliasesResponse alias = client.indices().getAlias(getAliasesRequest, RequestOptions.DEFAULT);
Response response; // we only return data for the first index name that was requested (always have done so)
String aliasResponse; String index1 = getAliasesRequest.indices()[0];
return new ArrayList<>(alias.getAliases().get(index1));
response = restClient.performRequest(new Request("GET", '/' + indexName + "/_alias/*"));
aliasResponse = EntityUtils.toString(response.getEntity());
return convertAliasResponse(aliasResponse);
}); });
} }
@Override @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() // GetSettingsRequest getSettingsRequest = requestFactory.getSettingsRequest(index, includeDefaults);
.indices(indexName) //
.includeDefaults(includeDefaults);
//
GetSettingsResponse response = restTemplate.execute(client -> client.indices() // 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 @Override
@ -171,61 +162,9 @@ class DefaultIndexOperations extends AbstractDefaultIndexOperations implements I
Assert.notNull(index, "No index defined for refresh()"); Assert.notNull(index, "No index defined for refresh()");
restTemplate RefreshRequest refreshRequest = requestFactory.refreshRequest(index);
.execute(client -> client.indices().refresh(refreshRequest(index.getIndexNames()), RequestOptions.DEFAULT)); 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 // endregion
} }

View File

@ -21,16 +21,18 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; 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.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; 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.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder; 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.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.AliasMetaData; 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.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
@ -62,26 +64,29 @@ class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations imp
} }
@Override @Override
protected boolean doCreate(String indexName, @Nullable Document settings) { protected boolean doCreate(IndexCoordinates index, @Nullable Document settings) {
CreateIndexRequestBuilder createIndexRequestBuilder = requestFactory.createIndexRequestBuilder(client, indexName, CreateIndexRequestBuilder createIndexRequestBuilder = requestFactory.createIndexRequestBuilder(client, index,
settings); settings);
return createIndexRequestBuilder.execute().actionGet().isAcknowledged(); return createIndexRequestBuilder.execute().actionGet().isAcknowledged();
} }
@Override @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)) { if (doExists(index)) {
return client.admin().indices().delete(new DeleteIndexRequest(indexName)).actionGet().isAcknowledged(); DeleteIndexRequest deleteIndexRequest = requestFactory.deleteIndexRequest(index);
return client.admin().indices().delete(deleteIndexRequest).actionGet().isAcknowledged();
} }
return false; return false;
} }
@Override @Override
protected boolean doExists(String indexName) { protected boolean doExists(IndexCoordinates index) {
return client.admin().indices().exists(indicesExistsRequest(indexName)).actionGet().isExists();
IndicesExistsRequest indicesExistsRequest = requestFactory.indicesExistsRequest(index);
return client.admin().indices().exists(indicesExistsRequest).actionGet().isExists();
} }
@Override @Override
@ -98,14 +103,12 @@ class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations imp
Assert.notNull(index, "No index defined for getMapping()"); Assert.notNull(index, "No index defined for getMapping()");
try { GetMappingsRequest mappingsRequest = requestFactory.getMappingsRequest(client, index);
return client.admin().indices().getMappings( //
new GetMappingsRequest().indices(index.getIndexNames())).actionGet() // return client.admin().indices().getMappings( //
.getMappings().get(index.getIndexName()).get(IndexCoordinates.TYPE) // mappingsRequest).actionGet() //
.getSourceAsMap(); .getMappings().get(mappingsRequest.indices()[0]).get(IndexCoordinates.TYPE) //
} catch (Exception e) { .getSourceAsMap();
throw new ElasticsearchException("Error while getting mapping for indexName : " + index.getIndexName(), e);
}
} }
@Override @Override
@ -120,39 +123,39 @@ class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations imp
Assert.notNull(index, "No index defined for Alias"); Assert.notNull(index, "No index defined for Alias");
Assert.notNull(query.getAliasName(), "No alias defined"); Assert.notNull(query.getAliasName(), "No alias defined");
return client.admin().indices().prepareAliases().removeAlias(index.getIndexName(), query.getAliasName()).execute() IndicesAliasesRequestBuilder indicesAliasesRequestBuilder = requestFactory
.actionGet().isAcknowledged(); .indicesRemoveAliasesRequestBuilder(client, query, index);
return indicesAliasesRequestBuilder.execute().actionGet().isAcknowledged();
} }
@Override @Override
protected List<AliasMetaData> doQueryForAlias(String indexName) { protected List<AliasMetaData> doQueryForAlias(IndexCoordinates index) {
return client.admin().indices().getAliases(new GetAliasesRequest().indices(indexName)).actionGet().getAliases()
.get(indexName); GetAliasesRequest getAliasesRequest = requestFactory.getAliasesRequest(index);
return client.admin().indices().getAliases(getAliasesRequest).actionGet().getAliases().get(index.getIndexName());
} }
@Override @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 = client.admin() // GetSettingsResponse response = client.admin() //
.indices() // .indices() //
.getSettings(request) // .getSettings(getSettingsRequest) //
.actionGet(); .actionGet();
return convertSettingsResponseToMap(response, indexName); return convertSettingsResponseToMap(response, getSettingsRequest.indices()[0]);
} }
@Override @Override
protected void doRefresh(IndexCoordinates index) { 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 @Nullable
<T> T get(String id, Class<T> clazz, IndexCoordinates index); <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. * Execute a multiGet against elasticsearch for the given ids.
* *
@ -142,6 +152,18 @@ public interface DocumentOperations {
*/ */
boolean exists(String id, IndexCoordinates index); 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. * Bulk index all objects. Will do save or update.
* *
@ -152,6 +174,17 @@ public interface DocumentOperations {
return bulkIndex(queries, BulkOptions.defaultOptions(), index); 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. * Bulk index all objects. Will do save or update.
* *
@ -170,6 +203,14 @@ public interface DocumentOperations {
bulkUpdate(queries, BulkOptions.defaultOptions(), index); 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. * Bulk update all objects. Will do update.
* *
@ -213,6 +254,16 @@ public interface DocumentOperations {
*/ */
String delete(Object entity, IndexCoordinates index); 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. * Delete all records matching the query.
* *

View File

@ -60,17 +60,17 @@ public interface ElasticsearchOperations extends DocumentOperations, SearchOpera
IndexCoordinates getIndexCoordinatesFor(Class<?> clazz); IndexCoordinates getIndexCoordinatesFor(Class<?> clazz);
// region IndexOperations // region IndexOperations
/** /**
* Create an index for given indexName . * Create an index for given indexName .
* *
* @param indexName the name of the index * @param indexName the name of the index
* @return {@literal true} if the index was created * @return {@literal true} if the index was created
* @deprecated since 4.0, use {@link IndexOperations#create()} * @deprecated since 4.0, use {@link IndexOperations#create()}
*/ */
@Deprecated @Deprecated
default boolean createIndex(String indexName) { default boolean createIndex(String indexName) {
return indexOps(IndexCoordinates.of(indexName)).create(); return indexOps(IndexCoordinates.of(indexName)).create();
} }
/** /**
* Create an index for given indexName and Settings. * 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.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.reindex.DeleteByQueryRequest; import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext; import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.suggest.SuggestBuilder; import org.elasticsearch.search.suggest.SuggestBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -315,10 +314,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
@Override @Override
public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) { public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) {
SearchRequest searchRequest = new SearchRequest(index.getIndexNames()); SearchRequest searchRequest = requestFactory.searchRequest(suggestion, index);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.suggest(suggestion);
searchRequest.source(sourceBuilder);
return execute(client -> client.search(searchRequest, RequestOptions.DEFAULT)); 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.NodesInfoRequestBuilder;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.delete.DeleteRequestBuilder;
import org.elasticsearch.action.get.GetRequestBuilder; import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.get.MultiGetRequestBuilder; 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(id, "id must not be null");
Assert.notNull(index, "index must not be null"); Assert.notNull(index, "index must not be null");
return client.prepareDelete(index.getIndexName(), IndexCoordinates.TYPE, elasticsearchConverter.convertId(id)) DeleteRequestBuilder deleteRequestBuilder = requestFactory.deleteRequestBuilder(client,
.execute().actionGet().getId(); elasticsearchConverter.convertId(id), index);
return deleteRequestBuilder.execute().actionGet().getId();
} }
@Override @Override
@ -293,8 +295,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(query.getPageable(), "pageable of query must not be null."); Assert.notNull(query.getPageable(), "pageable of query must not be null.");
ActionFuture<SearchResponse> action = requestFactory // ActionFuture<SearchResponse> action = requestFactory.searchRequestBuilder(client, query, clazz, index) //
.searchRequestBuilder(client, query, clazz, index) //
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)) // .setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)) //
.execute(); .execute();
@ -332,7 +333,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
@Override @Override
public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) { 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 @Override

View File

@ -28,7 +28,6 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/** /**
* Common operations performed on an entity in the context of it's mapping metadata. * 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 static final String ID_FIELD = "id";
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context;
public EntityOperations( public EntityOperations(
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context) { MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context) {
@ -51,8 +52,6 @@ class EntityOperations {
this.context = context; this.context = context;
} }
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context;
/** /**
* Creates a new {@link Entity} for the given bean. * 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 index index name override can be {@literal null}.
* @param type index type override can be {@literal null}. * @param type index type override can be {@literal null}.
* @return the {@link IndexCoordinates} containing index name and index type. * @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) { IndexCoordinates determineIndex(Entity<?> entity, @Nullable String index, @Nullable String type) {
return determineIndex(entity.getPersistentEntity(), index, 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} * 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 * 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 index index name override can be {@literal null}.
* @param type index type override can be {@literal null}. * @param type index type override can be {@literal null}.
* @return the {@link IndexCoordinates} containing index name and index type. * @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, IndexCoordinates determineIndex(ElasticsearchPersistentEntity<?> persistentEntity, @Nullable String index,
@Nullable String type) { @Nullable String type) {
return persistentEntity.getIndexCoordinates(); return determineIndex(persistentEntity, index);
} }
private static String indexName(@Nullable ElasticsearchPersistentEntity<?> entity, @Nullable String index) { /**
* Determine index name and type name from {@link ElasticsearchPersistentEntity} with {@code index} and {@code type}
if (StringUtils.isEmpty(index)) { * overrides. Allows using preferred values for index and type if provided, otherwise fall back to index and type
Assert.notNull(entity, "Cannot determine index name"); * defined on entity level.
return entity.getIndexCoordinates().getIndexName(); *
} * @param persistentEntity the entity to determine the index name. Can be {@literal null} if {@code index} and
* {@literal type} are provided.
return index; * @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 { 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 entityPublisher must not be {@literal null}.
* @param <T> * @param <T>
@ -50,15 +50,6 @@ public interface ReactiveDocumentOperations {
return entityPublisher.flatMap(this::save); 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 * 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 * {@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)); 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 * 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}. * {@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); <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. * {@literal null} or empty the index name provided via entity metadata is used.
* *
* @param entities must not be {@literal null}. * @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. * {@literal null} or empty the index name provided via entity metadata is used.
* *
* @param entities must not be {@literal null}. * @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); <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. * 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); 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}. * @param entity must not be {@literal null}.
* @return a {@link Mono} emitting the {@literal id} of the removed document. * @return a {@link Mono} emitting the {@literal id} of the removed document.
@ -231,7 +265,7 @@ public interface ReactiveDocumentOperations {
Mono<String> delete(Object entity); 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 entity must not be {@literal null}.
* @param index the target index, 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); 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 id must not be {@literal null}.
* @param entityType 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); 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 id must not be {@literal null}.
* @param entityType 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 query must not be {@literal null}.
* @param entityType 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); 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 query must not be {@literal null}.
* @param entityType 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())); 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 @Override
public <T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entitiesPublisher, IndexCoordinates index) { 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 @Override
public <T> Flux<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index) { 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 java.util.Map;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; 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.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.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.BulkRequest;
import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteRequestBuilder;
import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetRequestBuilder; import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.get.MultiGetRequest; 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.Client;
import org.elasticsearch.client.Requests; import org.elasticsearch.client.Requests;
import org.elasticsearch.client.indices.CreateIndexRequest; 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.client.indices.PutMappingRequest;
import org.elasticsearch.common.geo.GeoDistance; import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.unit.DistanceUnit; 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.SortBuilders;
import org.elasticsearch.search.sort.SortMode; import org.elasticsearch.search.sort.SortMode;
import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.ElasticsearchException; import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
@ -98,11 +108,15 @@ class RequestFactory {
this.elasticsearchConverter = elasticsearchConverter; this.elasticsearchConverter = elasticsearchConverter;
} }
// region alias
public IndicesAliasesRequest.AliasActions aliasAction(AliasQuery query, IndexCoordinates index) { public IndicesAliasesRequest.AliasActions aliasAction(AliasQuery query, IndexCoordinates index) {
Assert.notNull(index, "No index defined for Alias"); Assert.notNull(index, "No index defined for Alias");
Assert.notNull(query.getAliasName(), "No alias defined"); Assert.notNull(query.getAliasName(), "No alias defined");
String[] indexNames = index.getIndexNames();
IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add() IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add()
.alias(query.getAliasName()).index(index.getIndexName()); .alias(query.getAliasName()).indices(indexNames);
if (query.getFilterBuilder() != null) { if (query.getFilterBuilder() != null) {
aliasAction.filter(query.getFilterBuilder()); aliasAction.filter(query.getFilterBuilder());
@ -125,6 +139,40 @@ class RequestFactory {
return aliasAction; 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) { public BulkRequest bulkRequest(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
BulkRequest bulkRequest = new BulkRequest(); BulkRequest bulkRequest = new BulkRequest();
@ -195,9 +243,12 @@ class RequestFactory {
return bulkRequestBuilder; return bulkRequestBuilder;
} }
@SuppressWarnings("unchecked") // endregion
public CreateIndexRequest createIndexRequest(String indexName, @Nullable Document settings) {
CreateIndexRequest request = new CreateIndexRequest(indexName); // region index management
public CreateIndexRequest createIndexRequest(IndexCoordinates index, @Nullable Document settings) {
CreateIndexRequest request = new CreateIndexRequest(index.getIndexName());
if (settings != null) { if (settings != null) {
request.settings(settings); request.settings(settings);
@ -205,9 +256,10 @@ class RequestFactory {
return request; return request;
} }
@SuppressWarnings("unchecked") public CreateIndexRequestBuilder createIndexRequestBuilder(Client client, IndexCoordinates index,
public CreateIndexRequestBuilder createIndexRequestBuilder(Client client, String indexName,
@Nullable Document settings) { @Nullable Document settings) {
String indexName = index.getIndexName();
CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(indexName); CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(indexName);
if (settings != null) { if (settings != null) {
@ -216,9 +268,71 @@ class RequestFactory {
return createIndexRequestBuilder; 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 @Deprecated
public DeleteByQueryRequest deleteByQueryRequest(DeleteQuery deleteQuery, IndexCoordinates index) { 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()) // .setQuery(deleteQuery.getQuery()) //
.setAbortOnVersionConflict(false) // .setAbortOnVersionConflict(false) //
.setRefresh(true); .setRefresh(true);
@ -251,14 +365,23 @@ class RequestFactory {
} }
public DeleteRequest deleteRequest(String id, IndexCoordinates index) { 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 @Deprecated
public DeleteByQueryRequestBuilder deleteByQueryRequestBuilder(Client client, DeleteQuery deleteQuery, public DeleteByQueryRequestBuilder deleteByQueryRequestBuilder(Client client, DeleteQuery deleteQuery,
IndexCoordinates index) { IndexCoordinates index) {
String[] indexNames = index.getIndexNames();
DeleteByQueryRequestBuilder requestBuilder = new DeleteByQueryRequestBuilder(client, DeleteByQueryAction.INSTANCE) // DeleteByQueryRequestBuilder requestBuilder = new DeleteByQueryRequestBuilder(client, DeleteByQueryAction.INSTANCE) //
.source(index.getIndexNames()) // .source(indexNames) //
.filter(deleteQuery.getQuery()) // .filter(deleteQuery.getQuery()) //
.abortOnVersionConflict(false) // .abortOnVersionConflict(false) //
.refresh(true); .refresh(true);
@ -283,16 +406,20 @@ class RequestFactory {
SearchRequestBuilder source = requestBuilder.source(); SearchRequestBuilder source = requestBuilder.source();
if (query.isLimiting()) { if (query.isLimiting()) {
// noinspection ConstantConditions
source.setSize(query.getMaxResults()); source.setSize(query.getMaxResults());
} }
if (query.hasScrollTime()) { if (query.hasScrollTime()) {
// noinspection ConstantConditions
source.setScroll(TimeValue.timeValueMillis(query.getScrollTime().toMillis())); source.setScroll(TimeValue.timeValueMillis(query.getScrollTime().toMillis()));
} }
return requestBuilder; return requestBuilder;
} }
// endregion
// region get
public GetRequest getRequest(String id, IndexCoordinates index) { public GetRequest getRequest(String id, IndexCoordinates index) {
return new GetRequest(index.getIndexName(), id); return new GetRequest(index.getIndexName(), id);
} }
@ -301,36 +428,45 @@ class RequestFactory {
return client.prepareGet(index.getIndexName(), null, id); return client.prepareGet(index.getIndexName(), null, id);
} }
@Nullable public MultiGetRequest multiGetRequest(Query query, IndexCoordinates index) {
public HighlightBuilder highlightBuilder(Query query) { MultiGetRequest multiGetRequest = new MultiGetRequest();
HighlightBuilder highlightBuilder = query.getHighlightQuery().map(HighlightQuery::getHighlightBuilder).orElse(null); getMultiRequestItems(query, index).forEach(multiGetRequest::add);
return multiGetRequest;
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 IndexRequest indexRequest(IndexQuery query, IndexCoordinates index) { public MultiGetRequestBuilder multiGetRequestBuilder(Client client, Query searchQuery, IndexCoordinates index) {
String indexName = index.getIndexName(); 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; IndexRequest indexRequest;
if (query.getObject() != null) { if (query.getObject() != null) {
@ -405,9 +541,40 @@ class RequestFactory {
return indexRequestBuilder; 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) { 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; String[] fields = null;
if (query.getFields() != null) { if (query.getFields() != null) {
@ -452,6 +619,21 @@ class RequestFactory {
return moreLikeThisQueryBuilder; 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) { public SearchRequest searchRequest(Query query, @Nullable Class<?> clazz, IndexCoordinates index) {
SearchRequest searchRequest = prepareSearchRequest(query, clazz, index); SearchRequest searchRequest = prepareSearchRequest(query, clazz, index);
@ -492,158 +674,13 @@ class RequestFactory {
return searchRequestBuilder; 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) { SearchRequest request = new SearchRequest(indexNames);
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());
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.version(true); sourceBuilder.version(true);
sourceBuilder.trackScores(query.getTrackScores()); sourceBuilder.trackScores(query.getTrackScores());
@ -684,9 +721,7 @@ class RequestFactory {
request.preference(query.getPreference()); request.preference(query.getPreference());
} }
if (query.getSearchType() != null) { request.searchType(query.getSearchType());
request.searchType(query.getSearchType());
}
prepareSort(query, sourceBuilder, getPersistentEntity(clazz)); prepareSort(query, sourceBuilder, getPersistentEntity(clazz));
@ -713,120 +748,14 @@ class RequestFactory {
return request; 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, private SearchRequestBuilder prepareSearchRequestBuilder(Query query, Client client, @Nullable Class<?> clazz,
IndexCoordinates index) { 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()) // .setSearchType(query.getSearchType()) //
.setVersion(true) // .setVersion(true) //
.setTrackScores(query.getTrackScores()); .setTrackScores(query.getTrackScores());
@ -890,6 +819,56 @@ class RequestFactory {
return searchRequestBuilder; 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") @SuppressWarnings("rawtypes")
private void prepareSort(Query query, SearchSourceBuilder sourceBuilder, private void prepareSort(Query query, SearchSourceBuilder sourceBuilder,
@Nullable ElasticsearchPersistentEntity<?> entity) { @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) { private QueryBuilder getQuery(Query query) {
QueryBuilder elasticsearchQuery; QueryBuilder elasticsearchQuery;
@ -981,22 +1112,7 @@ class RequestFactory {
return elasticsearchQuery; return elasticsearchQuery;
} }
public IndicesAliasesRequest indicesAddAliasesRequest(AliasQuery query, IndexCoordinates index) { @Nullable
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);
}
private QueryBuilder getFilter(Query query) { private QueryBuilder getFilter(Query query) {
QueryBuilder elasticsearchFilter; QueryBuilder elasticsearchFilter;
@ -1036,10 +1152,10 @@ class RequestFactory {
private VersionType retrieveVersionTypeFromPersistentEntity(Class<?> clazz) { private VersionType retrieveVersionTypeFromPersistentEntity(Class<?> clazz) {
if (clazz != null) { VersionType versionType = elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz)
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz).getVersionType(); .getVersionType();
}
return VersionType.EXTERNAL; return versionType != null ? versionType : VersionType.EXTERNAL;
} }
private String[] toArray(List<String> values) { private String[] toArray(List<String> values) {
@ -1047,4 +1163,21 @@ class RequestFactory {
return values.toArray(valuesAsArray); 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 // 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 * Does a suggest query
* *
@ -277,6 +287,17 @@ public interface SearchOperations {
return content.isEmpty() ? null : content.get(0); 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}. * 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); <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}. * 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}. * @return will not be {@literal null}.
*/ */
default Document mapObject(@Nullable Object source) { default Document mapObject(@Nullable Object source) {
Document target = Document.create(); Document target = Document.create();
write(source, target);
if (source != null) {
write(source, target);
}
return target; return target;
} }
// endregion // endregion

View File

@ -87,9 +87,9 @@ public class MappingElasticsearchConverter
private final GenericConversionService conversionService; private final GenericConversionService conversionService;
private CustomConversions conversions = new ElasticsearchCustomConversions(Collections.emptyList()); 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<>(); private ConcurrentHashMap<String, Integer> propertyWarnings = new ConcurrentHashMap<>();

View File

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

View File

@ -15,14 +15,10 @@
*/ */
package org.springframework.data.elasticsearch.core.mapping; 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.context.AbstractMappingContext;
import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation; import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
/** /**
* SimpleElasticsearchMappingContext * SimpleElasticsearchMappingContext
@ -30,21 +26,14 @@ import org.springframework.lang.Nullable;
* @author Rizwan Idrees * @author Rizwan Idrees
* @author Mohsin Husen * @author Mohsin Husen
* @author Mark Paluch * @author Mark Paluch
* @author Peter-Josef Meisch
*/ */
public class SimpleElasticsearchMappingContext public class SimpleElasticsearchMappingContext
extends AbstractMappingContext<SimpleElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> extends AbstractMappingContext<SimpleElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> {
implements ApplicationContextAware {
private @Nullable ApplicationContext context;
@Override @Override
protected <T> SimpleElasticsearchPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) { protected <T> SimpleElasticsearchPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
SimpleElasticsearchPersistentEntity<T> persistentEntity = new SimpleElasticsearchPersistentEntity<>( return new SimpleElasticsearchPersistentEntity<>(typeInformation);
typeInformation);
if (context != null) {
persistentEntity.setApplicationContext(context);
}
return persistentEntity;
} }
@Override @Override
@ -52,9 +41,4 @@ public class SimpleElasticsearchMappingContext
SimpleElasticsearchPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) { SimpleElasticsearchPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new SimpleElasticsearchPersistentProperty(property, owner, 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.elasticsearch.index.VersionType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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.Document;
import org.springframework.data.elasticsearch.annotations.Parent; import org.springframework.data.elasticsearch.annotations.Parent;
import org.springframework.data.elasticsearch.annotations.Setting; 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.BasicPersistentEntity;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory; import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
import org.springframework.data.util.TypeInformation; import org.springframework.data.util.TypeInformation;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext; import org.springframework.expression.ParserContext;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -55,12 +51,10 @@ import org.springframework.util.Assert;
* @author Roman Puchkovskiy * @author Roman Puchkovskiy
*/ */
public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntity<T, ElasticsearchPersistentProperty> 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 static final Logger LOGGER = LoggerFactory.getLogger(SimpleElasticsearchPersistentEntity.class);
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private final StandardEvaluationContext context;
private final SpelExpressionParser parser;
private @Nullable String indexName; private @Nullable String indexName;
private boolean useServerConfiguration; private boolean useServerConfiguration;
@ -76,12 +70,11 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
private @Nullable VersionType versionType; private @Nullable VersionType versionType;
private boolean createIndexAndMapping; private boolean createIndexAndMapping;
private final Map<String, ElasticsearchPersistentProperty> fieldNamePropertyCache = new ConcurrentHashMap<>(); private final Map<String, ElasticsearchPersistentProperty> fieldNamePropertyCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Expression> indexNameExpressions = new ConcurrentHashMap<>();
public SimpleElasticsearchPersistentEntity(TypeInformation<T> typeInformation) { public SimpleElasticsearchPersistentEntity(TypeInformation<T> typeInformation) {
super(typeInformation); super(typeInformation);
this.context = new StandardEvaluationContext();
this.parser = new SpelExpressionParser();
Class<T> clazz = typeInformation.getType(); Class<T> clazz = typeInformation.getType();
if (clazz.isAnnotationPresent(Document.class)) { 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() { private String getIndexName() {
return indexName != null ? indexName : getTypeInformation().getType().getSimpleName();
if (indexName != null) {
Expression expression = parser.parseExpression(indexName, ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(context, String.class);
}
return getTypeInformation().getType().getSimpleName();
} }
@Override @Override
public IndexCoordinates getIndexCoordinates() { public IndexCoordinates getIndexCoordinates() {
return IndexCoordinates.of(getIndexName()); return resolve(IndexCoordinates.of(getIndexName()));
} }
@Nullable @Nullable
@ -294,4 +274,47 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
public ElasticsearchPersistentProperty getSeqNoPrimaryTermProperty() { public ElasticsearchPersistentProperty getSeqNoPrimaryTermProperty() {
return seqNoPrimaryTermProperty; 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.Date;
import java.util.List; 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.DateFormat;
import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType; import org.springframework.data.elasticsearch.annotations.FieldType;
@ -50,6 +53,8 @@ import org.springframework.util.StringUtils;
public class SimpleElasticsearchPersistentProperty extends public class SimpleElasticsearchPersistentProperty extends
AnnotationBasedPersistentProperty<ElasticsearchPersistentProperty> implements ElasticsearchPersistentProperty { 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 static final List<String> SUPPORTED_ID_PROPERTY_NAMES = Arrays.asList("id", "document");
private final boolean isScore; private final boolean isScore;
@ -66,6 +71,15 @@ public class SimpleElasticsearchPersistentProperty extends
this.annotatedFieldName = getAnnotatedFieldName(); this.annotatedFieldName = getAnnotatedFieldName();
this.isId = super.isIdProperty() || SUPPORTED_ID_PROPERTY_NAMES.contains(getFieldName()); 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.isScore = isAnnotationPresent(Score.class);
this.isParent = isAnnotationPresent(Parent.class); this.isParent = isAnnotationPresent(Parent.class);
this.isSeqNoPrimaryTerm = SeqNoPrimaryTerm.class.isAssignableFrom(getRawType()); this.isSeqNoPrimaryTerm = SeqNoPrimaryTerm.class.isAssignableFrom(getRawType());

View File

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

View File

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

View File

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

View File

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

View File

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