DATAES-634 - Rearrange methods in Template API.

Original PR: #352
This commit is contained in:
Peter-Josef Meisch 2019-12-10 19:04:37 +01:00 committed by GitHub
parent ff381c63b6
commit 11a6430a90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 2932 additions and 2368 deletions

View File

@ -0,0 +1,171 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import static org.springframework.util.StringUtils.*;
import java.util.HashMap;
import java.util.Map;
import org.elasticsearch.common.collect.MapBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.index.MappingBuilder;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.util.StringUtils;
/**
* Base implementation of {@link IndexOperations} common to Transport and Rest based Implementations of IndexOperations.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
abstract class AbstractDefaultIndexOperations implements IndexOperations {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDefaultIndexOperations.class);
protected final ElasticsearchConverter elasticsearchConverter;
protected final RequestFactory requestFactory;
public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter) {
this.elasticsearchConverter = elasticsearchConverter;
requestFactory = new RequestFactory(elasticsearchConverter);
}
// region IndexOperations
@Override
public boolean createIndex(String indexName) {
return createIndex(indexName, null);
}
@Override
public boolean createIndex(Class<?> clazz) {
String indexName = getRequiredPersistentEntity(clazz).getIndexCoordinates().getIndexName();
if (clazz.isAnnotationPresent(Setting.class)) {
String settingPath = clazz.getAnnotation(Setting.class).settingPath();
if (hasText(settingPath)) {
String settings = ResourceUtil.readFileFromClasspath(settingPath);
if (hasText(settings)) {
return createIndex(indexName, settings);
}
} else {
LOGGER.info("settingPath in @Setting has to be defined. Using default instead.");
}
}
return createIndex(indexName, getDefaultSettings(getRequiredPersistentEntity(clazz)));
}
@Override
public boolean createIndex(Class<?> clazz, Object settings) {
return createIndex(getRequiredPersistentEntity(clazz).getIndexCoordinates().getIndexName(), settings);
}
@Override
public boolean deleteIndex(Class<?> clazz) {
return deleteIndex(getRequiredPersistentEntity(clazz).getIndexCoordinates().getIndexName());
}
@Override
public boolean indexExists(Class<?> clazz) {
return indexExists(getIndexCoordinatesFor(clazz).getIndexName());
}
@Override
public Map<String, Object> getMapping(Class<?> clazz) {
return getMapping(getIndexCoordinatesFor(clazz));
}
@Override
public boolean putMapping(Class<?> clazz) {
return putMapping(clazz, buildMapping(clazz));
}
@Override
public <T> boolean putMapping(Class<T> clazz, Object mapping) {
return putMapping(getIndexCoordinatesFor(clazz), mapping);
}
@Override
public boolean putMapping(IndexCoordinates index, Class<?> clazz) {
return putMapping(index, buildMapping(clazz));
}
@Override
public Map<String, Object> getSetting(Class<?> clazz) {
return getSetting(getRequiredPersistentEntity(clazz).getIndexCoordinates().getIndexName());
}
@Override
public void refresh(Class<?> clazz) {
refresh(getIndexCoordinatesFor(clazz));
}
protected String buildMapping(Class<?> clazz) {
// load mapping specified in Mapping annotation if present
if (clazz.isAnnotationPresent(Mapping.class)) {
String mappingPath = clazz.getAnnotation(Mapping.class).mappingPath();
if (!StringUtils.isEmpty(mappingPath)) {
String mappings = ResourceUtil.readFileFromClasspath(mappingPath);
if (!StringUtils.isEmpty(mappings)) {
return mappings;
}
} else {
LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
}
}
// build mapping from field annotations
try {
return new MappingBuilder(elasticsearchConverter).buildPropertyMapping(clazz);
} catch (Exception e) {
throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
}
}
// endregion
// region Helper functions
private <T> Map getDefaultSettings(ElasticsearchPersistentEntity<T> persistentEntity) {
if (persistentEntity.isUseServerConfiguration())
return new HashMap();
return new MapBuilder<String, String>().put("index.number_of_shards", String.valueOf(persistentEntity.getShards()))
.put("index.number_of_replicas", String.valueOf(persistentEntity.getReplicas()))
.put("index.refresh_interval", persistentEntity.getRefreshInterval())
.put("index.store.type", persistentEntity.getIndexStoreType()).map();
}
ElasticsearchPersistentEntity<?> getRequiredPersistentEntity(Class<?> clazz) {
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz);
}
public IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
return getRequiredPersistentEntity(clazz).getIndexCoordinates();
}
// endregion
}

View File

@ -1,7 +1,5 @@
package org.springframework.data.elasticsearch.core;
import static org.springframework.util.StringUtils.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@ -14,32 +12,28 @@ import org.elasticsearch.action.search.MultiSearchRequest;
import org.elasticsearch.action.search.MultiSearchResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.Page;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.index.MappingBuilder;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.util.CloseableIterator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* AbstractElasticsearchTemplate
@ -49,22 +43,18 @@ import org.springframework.util.StringUtils;
*/
public abstract class AbstractElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractElasticsearchTemplate.class);
protected ElasticsearchConverter elasticsearchConverter;
protected RequestFactory requestFactory;
protected IndexOperations indexOperations;
/**
* @since 4.0
*/
public RequestFactory getRequestFactory() {
return requestFactory;
}
// region Initialization
protected void initialize(ElasticsearchConverter elasticsearchConverter, IndexOperations indexOperations) {
protected void initialize(ElasticsearchConverter elasticsearchConverter) {
Assert.notNull(elasticsearchConverter, "elasticsearchConverter must not be null.");
this.elasticsearchConverter = elasticsearchConverter;
this.requestFactory = new RequestFactory(elasticsearchConverter);
requestFactory = new RequestFactory(elasticsearchConverter);
this.indexOperations = indexOperations;
}
protected ElasticsearchConverter createElasticsearchConverter() {
@ -76,129 +66,54 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
if (elasticsearchConverter instanceof ApplicationContextAware) {
((ApplicationContextAware) elasticsearchConverter).setApplicationContext(context);
}
}
// endregion
protected String buildMapping(Class<?> clazz) {
// load mapping specified in Mapping annotation if present
if (clazz.isAnnotationPresent(Mapping.class)) {
String mappingPath = clazz.getAnnotation(Mapping.class).mappingPath();
if (!StringUtils.isEmpty(mappingPath)) {
String mappings = ResourceUtil.readFileFromClasspath(mappingPath);
if (!StringUtils.isEmpty(mappings)) {
return mappings;
}
} else {
LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
}
}
// build mapping from field annotations
try {
MappingBuilder mappingBuilder = new MappingBuilder(elasticsearchConverter);
return mappingBuilder.buildPropertyMapping(clazz);
} catch (Exception e) {
throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
}
}
// region getter/setter
@Override
public boolean createIndex(String indexName) {
return createIndexIfNotCreated(indexName);
public IndexOperations getIndexOperations() {
return indexOperations;
}
// endregion
private <T> boolean createIndexIfNotCreated(String indexName) {
return indexExists(indexName) || createIndex(indexName, null);
}
@Override
public <T> boolean createIndex(Class<T> clazz) {
return createIndexIfNotCreated(clazz);
}
private <T> boolean createIndexIfNotCreated(Class<T> clazz) {
return indexExists(getPersistentEntityFor(clazz).getIndexName()) || createIndexWithSettings(clazz);
}
private <T> boolean createIndexWithSettings(Class<T> clazz) {
if (clazz.isAnnotationPresent(Setting.class)) {
String settingPath = clazz.getAnnotation(Setting.class).settingPath();
if (hasText(settingPath)) {
String settings = ResourceUtil.readFileFromClasspath(settingPath);
if (hasText(settings)) {
return createIndex(getPersistentEntityFor(clazz).getIndexName(), settings);
}
} else {
LOGGER.info("settingPath in @Setting has to be defined. Using default instead.");
}
}
return createIndex(getPersistentEntityFor(clazz).getIndexName(), getDefaultSettings(getPersistentEntityFor(clazz)));
}
@Override
public <T> boolean createIndex(Class<T> clazz, Object settings) {
return createIndex(getPersistentEntityFor(clazz).getIndexName(), settings);
}
@Override
// region DocumentOperations
public void delete(Query query, Class<?> clazz, IndexCoordinates index) {
Assert.notNull(query, "Query must not be null.");
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
DeleteQuery deleteQuery = new DeleteQuery();
deleteQuery.setQuery(searchRequest.source().query());
delete(deleteQuery, index);
}
// endregion
// region SearchOperations
@Override
public <T> CloseableIterator<T> stream(Query query, Class<T> clazz, IndexCoordinates index) {
long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
return StreamQueries.streamResults(startScroll(scrollTimeInMillis, query, clazz, index),
scrollId -> continueScroll(scrollId, scrollTimeInMillis, clazz), this::clearScroll);
}
@Override
public <T> Page<T> moreLikeThis(MoreLikeThisQuery query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(query.getId(), "No document id defined for MoreLikeThisQuery");
MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = requestFactory.moreLikeThisQueryBuilder(query, index);
return queryForPage(new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).build(), clazz, index);
}
protected static String[] toArray(List<String> values) {
String[] valuesAsArray = new String[values.size()];
return values.toArray(valuesAsArray);
}
@Override
public ElasticsearchConverter getElasticsearchConverter() {
return elasticsearchConverter;
}
@Override
public ElasticsearchPersistentEntity getPersistentEntityFor(Class clazz) {
Assert.isTrue(clazz.isAnnotationPresent(Document.class), "Unable to identify index name. " + clazz.getSimpleName()
+ " is not a Document. Make sure the document class is annotated with @Document(indexName=\"foo\")");
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz);
}
private <T> Map getDefaultSettings(ElasticsearchPersistentEntity<T> persistentEntity) {
if (persistentEntity.isUseServerConfiguration())
return new HashMap();
return new MapBuilder<String, String>().put("index.number_of_shards", String.valueOf(persistentEntity.getShards()))
.put("index.number_of_replicas", String.valueOf(persistentEntity.getReplicas()))
.put("index.refresh_interval", persistentEntity.getRefreshInterval())
.put("index.store.type", persistentEntity.getIndexStoreType()).map();
}
protected void checkForBulkOperationFailure(BulkResponse bulkResponse) {
if (bulkResponse.hasFailures()) {
Map<String, String> failedDocuments = new HashMap<>();
for (BulkItemResponse item : bulkResponse.getItems()) {
if (item.isFailed())
failedDocuments.put(item.getId(), item.getFailureMessage());
}
throw new ElasticsearchException(
"Bulk operation has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages ["
+ failedDocuments + "]",
failedDocuments);
}
public <T> List<T> queryForList(Query query, Class<T> clazz, IndexCoordinates index) {
return queryForPage(query, clazz, index).getContent();
}
@Override
@ -244,34 +159,80 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return res;
}
@Override
public <T> boolean putMapping(Class<T> clazz) {
return putMapping(clazz, buildMapping(clazz));
}
@Override
public <T> boolean putMapping(Class<T> clazz, Object mapping) {
return putMapping(getIndexCoordinatesFor(clazz), mapping);
}
@Override
public <T> boolean putMapping(IndexCoordinates index, Class<T> clazz) {
return putMapping(index, buildMapping(clazz));
}
abstract protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request);
protected List<String> extractIds(SearchResponse response) {
List<String> ids = new ArrayList<>();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
ids.add(hit.getId());
}
}
return ids;
}
// endregion
// region Helper methods
@Override
public ElasticsearchConverter getElasticsearchConverter() {
return elasticsearchConverter;
}
/**
* @since 4.0
*/
public RequestFactory getRequestFactory() {
return requestFactory;
}
protected static String[] toArray(List<String> values) {
String[] valuesAsArray = new String[values.size()];
return values.toArray(valuesAsArray);
}
@Override
public abstract SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index);
/**
* @param clazz
* @return the IndexCoordinates defined on the entity.
* @since 4.0
*/
@Override
public IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
return getRequiredPersistentEntity(clazz).getIndexCoordinates();
}
protected void checkForBulkOperationFailure(BulkResponse bulkResponse) {
if (bulkResponse.hasFailures()) {
Map<String, String> failedDocuments = new HashMap<>();
for (BulkItemResponse item : bulkResponse.getItems()) {
if (item.isFailed())
failedDocuments.put(item.getId(), item.getFailureMessage());
}
throw new ElasticsearchException(
"Bulk operation has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages ["
+ failedDocuments + ']',
failedDocuments);
}
}
protected void setPersistentEntityId(Object entity, String id) {
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntityFor(entity.getClass());
ElasticsearchPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
// Only deal with text because ES generated Ids are strings !
// Only deal with text because ES generated Ids are strings!
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
persistentEntity.getPropertyAccessor(entity).setProperty(idProperty, id);
}
}
ElasticsearchPersistentEntity<?> getRequiredPersistentEntity(Class<?> clazz) {
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz);
}
// endregion
}

View File

@ -0,0 +1,281 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.client.Requests.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.DeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.core.client.support.AliasData;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.AliasQuery;
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.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
class DefaultIndexOperations extends AbstractDefaultIndexOperations implements IndexOperations {
private RestHighLevelClient client;
public DefaultIndexOperations(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter) {
super(elasticsearchConverter);
this.client = client;
}
@Override
public boolean createIndex(String indexName, Object settings) {
CreateIndexRequest request = requestFactory.createIndexRequest(indexName, settings);
try {
return client.indices().create(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (IOException e) {
throw new ElasticsearchException("Error for creating index: " + request.toString(), e);
}
}
@Override
public boolean deleteIndex(String indexName) {
Assert.notNull(indexName, "No index defined for delete operation");
if (indexExists(indexName)) {
DeleteIndexRequest request = new DeleteIndexRequest(indexName);
try {
return client.indices().delete(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (IOException e) {
throw new ElasticsearchException("Error while deleting index request: " + request.toString(), e);
}
}
return false;
}
@Override
public boolean indexExists(String indexName) {
GetIndexRequest request = new GetIndexRequest(indexName);
try {
return client.indices().exists(request, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new ElasticsearchException("Error while for indexExists request: " + request.toString(), e);
}
}
@Override
public boolean putMapping(IndexCoordinates index, Object mapping) {
Assert.notNull(index, "No index defined for putMapping()");
PutMappingRequest request = requestFactory.putMappingRequest(index, mapping);
try {
return client.indices().putMapping(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (IOException e) {
throw new ElasticsearchException("Failed to put mapping for " + index.getIndexName(), e);
}
}
@Override
public Map<String, Object> getMapping(IndexCoordinates index) {
Assert.notNull(index, "No index defined for getMapping()");
RestClient restClient = client.getLowLevelClient();
try {
Request request = new Request("GET",
'/' + index.getIndexName() + "/_mapping/" + index.getTypeName() + "?include_type_name=true");
Response response = restClient.performRequest(request);
return convertMappingResponse(EntityUtils.toString(response.getEntity()), index.getTypeName());
} catch (Exception e) {
throw new ElasticsearchException("Error while getting mapping for indexName : " + index.getIndexName()
+ " type : " + index.getTypeName() + ' ', e);
}
}
@Override
public boolean addAlias(AliasQuery query, IndexCoordinates index) {
IndicesAliasesRequest request = requestFactory.indicesAddAliasesRequest(query, index);
try {
return client.indices().updateAliases(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (IOException e) {
throw new ElasticsearchException("failed to update aliases with request: " + request, e);
}
}
@Override
public boolean removeAlias(AliasQuery query, IndexCoordinates index) {
Assert.notNull(index, "No index defined for Alias");
Assert.notNull(query.getAliasName(), "No alias defined");
IndicesAliasesRequest indicesAliasesRequest = requestFactory.indicesRemoveAliasesRequest(query, index);
try {
return client.indices().updateAliases(indicesAliasesRequest, RequestOptions.DEFAULT).isAcknowledged();
} catch (IOException e) {
throw new ElasticsearchException(
"failed to update aliases with indicesRemoveAliasesRequest: " + indicesAliasesRequest, e);
}
}
@Override
public List<AliasMetaData> queryForAlias(String indexName) {
List<AliasMetaData> aliases = null;
RestClient restClient = client.getLowLevelClient();
Response response;
String aliasResponse;
try {
response = restClient.performRequest(new Request("GET", '/' + indexName + "/_alias/*"));
aliasResponse = EntityUtils.toString(response.getEntity());
} catch (Exception e) {
throw new ElasticsearchException("Error while getting mapping for indexName : " + indexName, e);
}
return convertAliasResponse(aliasResponse);
}
@Override
public Map getSetting(String indexName) {
Assert.notNull(indexName, "No index defined for getSettings");
ObjectMapper objMapper = new ObjectMapper();
Map settings = null;
RestClient restClient = client.getLowLevelClient();
try {
Response response = restClient.performRequest(new Request("GET", "/" + indexName + "/_settings"));
settings = convertSettingResponse(EntityUtils.toString(response.getEntity()), indexName);
} catch (Exception e) {
throw new ElasticsearchException("Error while getting settings for indexName : " + indexName, e);
}
return settings;
}
@Override
public void refresh(IndexCoordinates index) {
Assert.notNull(index, "No index defined for refresh()");
try {
client.indices().refresh(refreshRequest(index.getIndexNames()), RequestOptions.DEFAULT);
} catch (IOException e) {
throw new ElasticsearchException("failed to refresh index: " + index, e);
}
}
// region Helper methods
private Map<String, Object> convertMappingResponse(String mappingResponse, String type) {
ObjectMapper mapper = new ObjectMapper();
try {
Map<String, Object> result = null;
JsonNode node = mapper.readTree(mappingResponse);
node = node.findValue("mappings").findValue(type);
result = mapper.readValue(mapper.writeValueAsString(node), HashMap.class);
return result;
} catch (IOException e) {
throw new ElasticsearchException("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<AliasMetaData>();
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 ElasticsearchException("Could not map alias response : " + aliasResponse, e);
}
}
private Map<String, String> convertSettingResponse(String settingResponse, String indexName) {
ObjectMapper mapper = new ObjectMapper();
try {
Settings settings = Settings.fromXContent(XContentType.JSON.xContent().createParser(NamedXContentRegistry.EMPTY,
DeprecationHandler.THROW_UNSUPPORTED_OPERATION, settingResponse));
String prefix = indexName + ".settings.";
// Backwards compatibility. TODO Change to return Settings object.
Map<String, String> result = new HashMap<String, String>();
Set<String> keySet = settings.keySet();
for (String key : keySet) {
result.put(key.substring(prefix.length()), settings.get(key));
}
return result;
} catch (IOException e) {
throw new ElasticsearchException("Could not map alias response : " + settingResponse, e);
}
}
// endregion
}

View File

@ -0,0 +1,141 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.client.Requests.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.common.settings.Settings;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.AliasQuery;
import org.springframework.util.Assert;
/**
* {@link IndexOperations} implementation using the TransportClient.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations implements IndexOperations {
private final Client client;
public DefaultTransportIndexOperations(Client client, ElasticsearchConverter elasticsearchConverter) {
super(elasticsearchConverter);
this.client = client;
}
@Override
public boolean createIndex(String indexName, Object settings) {
CreateIndexRequestBuilder createIndexRequestBuilder = requestFactory.createIndexRequestBuilder(client, indexName,
settings);
return createIndexRequestBuilder.execute().actionGet().isAcknowledged();
}
@Override
public boolean deleteIndex(String indexName) {
Assert.notNull(indexName, "No index defined for delete operation");
if (indexExists(indexName)) {
return client.admin().indices().delete(new DeleteIndexRequest(indexName)).actionGet().isAcknowledged();
}
return false;
}
@Override
public boolean indexExists(String indexName) {
return client.admin().indices().exists(indicesExistsRequest(indexName)).actionGet().isExists();
}
@Override
public boolean putMapping(IndexCoordinates index, Object mapping) {
Assert.notNull(index, "No index defined for putMapping()");
PutMappingRequestBuilder requestBuilder = requestFactory.putMappingRequestBuilder(client, index, mapping);
return requestBuilder.execute().actionGet().isAcknowledged();
}
@Override
public Map<String, Object> getMapping(IndexCoordinates index) {
Assert.notNull(index, "No index defined for putMapping()");
try {
return client.admin().indices()
.getMappings(new GetMappingsRequest().indices(index.getIndexNames()).types(index.getTypeNames())).actionGet()
.getMappings().get(index.getIndexName()).get(index.getTypeName()).getSourceAsMap();
} catch (Exception e) {
throw new ElasticsearchException("Error while getting mapping for indexName : " + index.getIndexName()
+ " type : " + index.getTypeName() + ' ' + e.getMessage());
}
}
@Override
public boolean addAlias(AliasQuery query, IndexCoordinates index) {
IndicesAliasesRequest.AliasActions aliasAction = requestFactory.aliasAction(query, index);
return client.admin().indices().prepareAliases().addAliasAction(aliasAction).execute().actionGet().isAcknowledged();
}
@Override
public boolean removeAlias(AliasQuery query, IndexCoordinates index) {
Assert.notNull(index, "No index defined for Alias");
Assert.notNull(query.getAliasName(), "No alias defined");
return client.admin().indices().prepareAliases().removeAlias(index.getIndexName(), query.getAliasName()).execute()
.actionGet().isAcknowledged();
}
@Override
public List<AliasMetaData> queryForAlias(String indexName) {
return client.admin().indices().getAliases(new GetAliasesRequest().indices(indexName)).actionGet().getAliases()
.get(indexName);
}
@Override
public Map<String, Object> getSetting(String indexName) {
Assert.notNull(indexName, "No index defined for getSettings");
Settings settings = client.admin().indices().getSettings(new GetSettingsRequest()).actionGet().getIndexToSettings()
.get(indexName);
return settings.keySet().stream().collect(Collectors.toMap((key) -> key, (key) -> settings.get(key)));
}
@Override
public void refresh(IndexCoordinates index) {
Assert.notNull(index, "No index defined for refresh()");
client.admin().indices().refresh(refreshRequest(index.getIndexNames())).actionGet();
}
}

View File

@ -0,0 +1,136 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import java.util.List;
import org.elasticsearch.action.update.UpdateResponse;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
/**
* The operations for the
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html">Elasticsearch Document APIs</a>.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
public interface DocumentOperations {
/**
* Index an object. Will do save or update.
*
* @param query the query defining the object
* @param index the index from which the object is read.
* @return returns the document id
*/
String index(IndexQuery query, IndexCoordinates index);
/**
* Retrieves an object from an index.
*
* @param query the query defining the id of the object to get
* @param clazz the type of the object to be returned
* @param index the index from which the object is read.
* @return the found object
*/
<T> T get(GetQuery query, 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
* @param index the index(es) from which the objects are read.
* @return list of objects
*/
<T> List<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index);
/**
* Bulk index all objects. Will do save or update.
*
* @param queries the queries to execute in bulk
*/
default void bulkIndex(List<IndexQuery> queries, IndexCoordinates index) {
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
*/
void bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions, IndexCoordinates index);
/**
* Bulk update all objects. Will do update.
*
* @param queries the queries to execute in bulk
*/
default void bulkUpdate(List<UpdateQuery> queries, IndexCoordinates index) {
bulkUpdate(queries, BulkOptions.defaultOptions(), index);
}
/**
* Bulk update all objects. Will do update.
*
* @param queries the queries to execute in bulk
* @param bulkOptions options to be added to the bulk request
*/
void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index);
/**
* Delete the one object with provided id.
*
* @param id the document ot delete
* @param index the index from which to delete
* @return documentId of the document deleted
*/
String delete(String id, 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}
* @param index the index from which to delete
*/
void delete(Query query, Class<?> clazz, IndexCoordinates index);
/**
* Delete all records matching the query.
*
* @param query query defining the objects
* @param index the index where to delete the records
*/
void delete(DeleteQuery query, IndexCoordinates index);
/**
* Partial update of the document.
*
* @param updateQuery query defining the update
* @param index the index where to update the records
* @return the update response
*/
UpdateResponse update(UpdateQuery updateQuery, IndexCoordinates index);
}

View File

@ -17,27 +17,17 @@ package org.springframework.data.elasticsearch.core;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.springframework.data.domain.Page;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.AliasQuery;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.util.CloseableIterator;
import org.springframework.lang.Nullable;
/**
* ElasticsearchOperations
* ElasticsearchOperations. Since 4.0 this interface only contains common helper functions, the other methods have been
* moved to the different interfaces that are extended by ElasticsearchOperations. The interfaces now reflect the <a
* href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html>REST API structure</a> of
* Elasticsearch.
*
* @author Rizwan Idrees
* @author Mohsin Husen
@ -46,451 +36,280 @@ import org.springframework.lang.Nullable;
* @author Dmitriy Yakovlev
* @author Peter-Josef Meisch
*/
public interface ElasticsearchOperations {
public interface ElasticsearchOperations extends DocumentOperations, SearchOperations {
/**
* adding new alias
*
* @param query
* @param index
* @return
*/
boolean addAlias(AliasQuery query, IndexCoordinates index);
IndexOperations getIndexOperations();
/**
* removing previously created alias
*
* @param query
* @param index
* @return
*/
boolean removeAlias(AliasQuery query, IndexCoordinates index);
/**
* Create an index for given indexName if it does not already exist
*
* @param indexName
*/
boolean createIndex(String indexName);
/**
* Create an index for given indexName and Settings
*
* @param indexName
* @param settings
*/
boolean createIndex(String indexName, Object settings);
/**
* Create an index for a class if it does not already exist
*
* @param clazz
* @param <T>
*/
<T> boolean createIndex(Class<T> clazz);
/**
* Create an index for given class and Settings
*
* @param clazz
* @param settings
*/
<T> boolean createIndex(Class<T> clazz, Object settings);
/**
* Create mapping for a class
*
* @param clazz
* @param <T>
*/
<T> boolean putMapping(Class<T> clazz);
/**
* Create mapping for the given class and put the mapping to the given index
*
* @param index
* @param clazz
* @since 3.2
*/
<T> boolean putMapping(IndexCoordinates index, Class<T> clazz);
/**
* Create mapping for a given index
* @param index
* @param mappings
* @param index
*/
boolean putMapping(IndexCoordinates index, Object mappings);
/**
* Create mapping for a class
*
* @param clazz
* @param mappings
*/
<T> boolean putMapping(Class<T> clazz, Object mappings);
/**
* Get mapping for a class
*
* @param clazz
*/
default Map<String, Object> getMapping(Class<?> clazz) {
return getMapping(getIndexCoordinatesFor(clazz));
}
/**
* Get mapping for a given index coordinates
*
* @param index
*/
Map<String, Object> getMapping(IndexCoordinates index);
/**
* Get settings for a given indexName
*
* @param indexName
*/
Map<String, Object> getSetting(String indexName);
/**
* Get settings for a given class
*
* @param clazz
*/
<T> Map<String, Object> getSetting(Class<T> clazz);
/**
* get all the alias pointing to specified index
*
* @param indexName
* @return
*/
List<AliasMetaData> queryForAlias(String indexName);
<T> T query(Query query, ResultsExtractor<T> resultsExtractor, Class<T> clazz, IndexCoordinates index);
/**
* Retrieves an object from an index
*
* @param query the query defining the id of the object to get
* @param clazz the type of the object to be returned
* @param index the index from which the object is read.
* @return the found object
*/
<T> T get(GetQuery query, Class<T> clazz, IndexCoordinates index);
/**
* Execute the query against elasticsearch and return the first returned object
*
* @param query
* @param clazz
* @param index
* @return the first matching object
*/
default <T> T queryForObject(Query query, Class<T> clazz, IndexCoordinates index) {
List<T> content = queryForPage(query, clazz, index).getContent();
return content.isEmpty() ? null : content.get(0);
}
/**
* Execute the query against elasticsearch and return result as {@link Page}
*
* @param query
* @param clazz
* @return
*/
<T> AggregatedPage<T> queryForPage(Query query, Class<T> clazz, IndexCoordinates index);
/**
* Execute the multi-search against elasticsearch and return result as {@link List} of {@link Page}
*
* @param queries
* @param clazz
* @param index
* @return
*/
<T> List<Page<T>> queryForPage(List<? extends Query> queries, Class<T> clazz, IndexCoordinates index);
/**
* Execute the multi-search against elasticsearch and return result as {@link List} of {@link Page}
*
* @param queries
* @param classes
* @param index
* @return
*/
List<Page<?>> queryForPage(List<? extends Query> queries, List<Class<?>> classes, IndexCoordinates index);
/**
* Executes the given {@link Query} against elasticsearch and return result as {@link CloseableIterator}.
* <p>
* Returns a {@link CloseableIterator} that wraps an Elasticsearch scroll context that needs to be closed in case of
* error.
*
* @param <T> element return type
* @param query
* @param clazz
* @param index
* @return
* @since 1.3
*/
<T> CloseableIterator<T> stream(Query query, Class<T> clazz, IndexCoordinates index);
/**
* Execute the criteria query against elasticsearch and return result as {@link List}
*
* @param query
* @param clazz
* @param index
* @param <T>
* @return
*/
<T> List<T> queryForList(Query query, Class<T> clazz, IndexCoordinates index);
/**
* Execute the multi search query against elasticsearch and return result as {@link List}
*
* @param <T>
* @param queries
* @param clazz
* @param index
* @return
*/
default <T> List<List<T>> queryForList(List<Query> queries, Class<T> clazz, IndexCoordinates index) {
return queryForPage(queries, clazz, index).stream().map(Page::getContent).collect(Collectors.toList());
}
/**
* Execute the multi search query against elasticsearch and return result as {@link List}
*
* @param queries
* @param classes
* @param index
* @return
*/
default List<List<?>> queryForList(List<Query> queries, List<Class<?>> classes, IndexCoordinates index) {
return queryForPage(queries, classes, index).stream().map(Page::getContent).collect(Collectors.toList());
}
/**
* Execute the query against elasticsearch and return ids
*
* @param query
* @param clazz
* @param index
* @return
*/
List<String> queryForIds(Query query, Class<?> clazz, IndexCoordinates index);
/**
* return number of elements found by given query
*
* @param query the query to execute
* @param index the index to run the query against
* @return count
*/
default long count(Query query, IndexCoordinates index) {
return count(query, null, index);
}
/**
* return number of elements found by given query
*
* @param query the query to execute
* @param clazz the entity clazz used for property mapping
* @param index the index to run the query against
* @return count
*/
long count(Query query, @Nullable Class<?> clazz, IndexCoordinates index);
/**
* Execute a multiGet against elasticsearch for the given ids
*
* @param query
* @param clazz
* @param index
* @return
*/
<T> List<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index);
/**
* Index an object. Will do save or update
*
* @param query
* @return returns the document id
*/
String index(IndexQuery query, IndexCoordinates index);
/**
* Partial update of the document
*
* @param updateQuery
* @return
*/
UpdateResponse update(UpdateQuery updateQuery, IndexCoordinates index);
/**
* Bulk index all objects. Will do save or update.
*
* @param queries the queries to execute in bulk
*/
default void bulkIndex(List<IndexQuery> queries, IndexCoordinates index) {
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
* @since 3.2
*/
void bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions, IndexCoordinates index);
/**
* Bulk update all objects. Will do update
*
* @param queries the queries to execute in bulk
*/
default void bulkUpdate(List<UpdateQuery> queries, IndexCoordinates index) {
bulkUpdate(queries, BulkOptions.defaultOptions(), index);
}
/**
* Bulk update all objects. Will do update
*
* @param queries the queries to execute in bulk
* @param bulkOptions options to be added to the bulk request
* @since 3.2
*/
void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index);
/**
* Delete the one object with provided id.
*
* @param id the document ot delete
* @param index the index from which to delete
* @return documentId of the document deleted
*/
String delete(String id, IndexCoordinates index);
/**
* Delete all records matching the criteria
*
* @param query
* @param clazz
* @param index
*/
void delete(Query query, Class<?> clazz, IndexCoordinates index);
/**
* Delete all records matching the query
*
* @param query
* @param index the index where to delete the records
*/
void delete(DeleteQuery query, IndexCoordinates index);
/**
* Deletes an index for given entity
*
* @param clazz
* @return
*/
default boolean deleteIndex(Class<?> clazz) {
return deleteIndex(getPersistentEntityFor(clazz).getIndexName());
}
/**
* Deletes an index for given indexName
*
* @param indexName
* @return
*/
boolean deleteIndex(String indexName);
/**
* check if index is exists
*
* @param clazz
* @return
*/
default boolean indexExists(Class<?> clazz) {
return indexExists(getIndexCoordinatesFor(clazz).getIndexName());
}
/**
* check if index is exists for given IndexName
*
* @param indexName
* @return
*/
boolean indexExists(String indexName);
/**
* refresh the index(es)
*
* @param index
*/
void refresh(IndexCoordinates index);
/**
* refresh the index
*
* @param clazz
*/
default <T> void refresh(Class<T> clazz) {
refresh(getIndexCoordinatesFor(clazz));
}
/**
* Returns scrolled page for given query
*
* @param scrollTimeInMillis The time in millisecond for scroll feature
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setScroll(org.elasticsearch.common.unit.TimeValue)}.
* @param query The search query.
* @param clazz The class of entity to retrieve.
* @param index
* @return The scan id for input query.
*/
<T> ScrolledPage<T> startScroll(long scrollTimeInMillis, Query query, Class<T> clazz, IndexCoordinates index);
<T> ScrolledPage<T> continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class<T> clazz);
/**
* Clears the search contexts associated with specified scroll ids.
*
* @param scrollId
*/
void clearScroll(String scrollId);
/**
* more like this query to search for documents that are "like" a specific document.
*
* @param query
* @param clazz
* @param index
* @param <T>
* @return
*/
<T> Page<T> moreLikeThis(MoreLikeThisQuery query, Class<T> clazz, IndexCoordinates index);
ElasticsearchPersistentEntity getPersistentEntityFor(Class clazz);
/**
* @return Converter in use
*/
ElasticsearchConverter getElasticsearchConverter();
IndexCoordinates getIndexCoordinatesFor(Class<?> clazz);
// region IndexOperations
/**
* @param clazz
* @return the IndexCoordinates defined on the entity.
* @since 4.0
* Create an index for given indexName if it does not already exist.
*
* @param indexName the name of the index
* @return {@literal true} if the index was created
* @deprecated since 4.0, use {@link IndexOperations#createIndex(String) instead}
*/
default IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
ElasticsearchPersistentEntity entity = getPersistentEntityFor(clazz);
return IndexCoordinates.of(entity.getIndexName()).withTypes(entity.getIndexType());
@Deprecated
default boolean createIndex(String indexName) {
return getIndexOperations().createIndex(indexName);
}
/**
* Create an index for given indexName and Settings.
*
* @param indexName the name of the index
* @param settings the index settings
* @return {@literal true} if the index was created
* @deprecated since 4.0, use {@link IndexOperations#createIndex(String, Object)} instead}
*/
@Deprecated
default boolean createIndex(String indexName, Object settings) {
return getIndexOperations().createIndex(indexName, settings);
}
/**
* Create an index for a class if it does not already exist.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return {@literal true} if the index was created
* @deprecated since 4.0, use {@link IndexOperations#createIndex(Class)} instead}
*/
@Deprecated
default boolean createIndex(Class<?> clazz) {
return getIndexOperations().createIndex(clazz);
}
/**
* Create an index for given class and Settings.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @param settings the index settings
* @return {@literal true} if the index was created
* @deprecated since 4.0, use {@link IndexOperations#createIndex(Class, Object)} instead}
*/
@Deprecated
default boolean createIndex(Class<?> clazz, Object settings) {
return getIndexOperations().createIndex(clazz, settings);
}
/**
* Deletes an index for given entity.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return {@literal true} if the index was deleted
* @deprecated since 4.0, use {@link IndexOperations#deleteIndex(Class)} instead}
*/
@Deprecated
default boolean deleteIndex(Class<?> clazz) {
return getIndexOperations().deleteIndex(clazz);
}
/**
* Deletes an index.
*
* @param indexName the name of the index to delete
* @return {@literal true} if the index was deleted
* @deprecated since 4.0, use {@link IndexOperations#deleteIndex(String)} instead}
*/
@Deprecated
default boolean deleteIndex(String indexName) {
return getIndexOperations().deleteIndex(indexName);
}
/**
* check if index exists.
*
* @param indexName the name of the index
* @return {@literal true} if the index exists
* @deprecated since 4.0, use {@link IndexOperations#indexExists(String)} instead}
*/
@Deprecated
default boolean indexExists(String indexName) {
return getIndexOperations().indexExists(indexName);
}
/**
* check if index is exists.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return {@literal true} if the index exists
* @deprecated since 4.0, use {@link IndexOperations#indexExists(Class)} instead}
*/
@Deprecated
default boolean indexExists(Class<?> clazz) {
return getIndexOperations().indexExists(clazz);
}
/**
* Create mapping for a class and store it to the index.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return {@literal true} if the mapping could be stored
* @deprecated since 4.0, use {@link IndexOperations#putMapping(Class)} instead}
*/
@Deprecated
default boolean putMapping(Class<?> clazz) {
return getIndexOperations().putMapping(clazz);
}
/**
* Create mapping for the given class and put the mapping to the given index.
*
* @param index the index to store the mapping to
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return {@literal true} if the mapping could be stored
* @deprecated since 4.0, use {@link IndexOperations#putMapping(IndexCoordinates, Class)} instead}
*/
@Deprecated
default boolean putMapping(IndexCoordinates index, Class<?> clazz) {
return getIndexOperations().putMapping(index, clazz);
}
/**
* Stores a mapping to an index.
*
* @param index the index to store the mapping to
* @param mappings can be a JSON String or a {@link Map}
* @return {@literal true} if the mapping could be stored
* @deprecated since 4.0, use {@link IndexOperations#putMapping(IndexCoordinates, Object)} instead}
*/
@Deprecated
default boolean putMapping(IndexCoordinates index, Object mappings) {
return getIndexOperations().putMapping(index, mappings);
}
/**
* Create mapping for a class Stores a mapping to an index.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @param mappings can be a JSON String or a {@link Map}
* @return {@literal true} if the mapping could be stored
* @deprecated since 4.0, use {@link IndexOperations#putMapping(Class, Object)} instead}
*/
@Deprecated
default boolean putMapping(Class<?> clazz, Object mappings) {
return getIndexOperations().putMapping(clazz, mappings);
}
/**
* Get mapping for an index defined by a class.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}.
* @return the mapping
* @deprecated since 4.0, use {@link IndexOperations#getMapping(Class)} instead}
*/
@Deprecated
default Map<String, Object> getMapping(Class<?> clazz) {
return getIndexOperations().getMapping(clazz);
}
/**
* Get mapping for a given index.
*
* @param index the index to read the mapping from
* @return the mapping
* @deprecated since 4.0, use {@link IndexOperations#getMapping(IndexCoordinates)} instead}
*/
@Deprecated
default Map<String, Object> getMapping(IndexCoordinates index) {
return getIndexOperations().getMapping(index);
}
/**
* Add an alias.
*
* @param query query defining the alias
* @param index the index for which to add an alias
* @return true if the alias was created
* @deprecated since 4.0, use {@link IndexOperations#addAlias(AliasQuery, IndexCoordinates)} instead}
*/
@Deprecated
default boolean addAlias(AliasQuery query, IndexCoordinates index) {
return getIndexOperations().addAlias(query, index);
}
/**
* Remove an alias.
*
* @param query query defining the alias
* @param index the index for which to remove an alias
* @return true if the alias was removed
* @deprecated since 4.0, use {@link IndexOperations#removeAlias(AliasQuery, IndexCoordinates)} instead}
*/
@Deprecated
default boolean removeAlias(AliasQuery query, IndexCoordinates index) {
return getIndexOperations().removeAlias(query, index);
}
/**
* Get the alias informations for a specified index.
*
* @param indexName the name of the index
* @return alias information
* @deprecated since 4.0, use {@link IndexOperations#queryForAlias(String)} instead}
*/
@Deprecated
default List<AliasMetaData> queryForAlias(String indexName) {
return getIndexOperations().queryForAlias(indexName);
}
/**
* Get settings for a given indexName.
*
* @param indexName the name of the index
* @return the settings
* @deprecated since 4.0, use {@link IndexOperations#getSetting(String)} )} instead}
*/
@Deprecated
default Map<String, Object> getSetting(String indexName) {
return getIndexOperations().getSetting(indexName);
}
/**
* Get settings for a given class.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return the settings
* @deprecated since 4.0, use {@link IndexOperations#getSetting(Class)} instead}
*/
@Deprecated
default Map<String, Object> getSetting(Class<?> clazz) {
return getIndexOperations().getSetting(clazz);
}
/**
* Refresh the index(es).
*
* @param index the index to refresh
* @deprecated since 4.0, use {@link IndexOperations#refresh(IndexCoordinates)} instead}
*/
@Deprecated
default void refresh(IndexCoordinates index) {
getIndexOperations().refresh(index);
}
/**
* Refresh the index.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @deprecated since 4.0, use {@link IndexOperations#refresh(Class)} instead}
*/
@Deprecated
default void refresh(Class<?> clazz) {
getIndexOperations().refresh(clazz);
}
// endregion
}

View File

@ -15,21 +15,9 @@
*/
package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.client.Requests.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
@ -45,41 +33,29 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.DeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.client.support.AliasData;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.DocumentAdapters;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.support.SearchHitsUtil;
import org.springframework.data.util.CloseableIterator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* ElasticsearchRestTemplate
*
@ -116,6 +92,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
private RestHighLevelClient client;
// region Initialization
public ElasticsearchRestTemplate(RestHighLevelClient client) {
initialize(client, createElasticsearchConverter());
}
@ -127,192 +104,11 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
private void initialize(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter) {
Assert.notNull(client, "Client must not be null!");
this.client = client;
initialize(elasticsearchConverter);
}
@Override
public boolean addAlias(AliasQuery query, IndexCoordinates index) {
IndicesAliasesRequest request = requestFactory.indicesAddAliasesRequest(query, index);
try {
return client.indices().updateAliases(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (IOException e) {
throw new ElasticsearchException("failed to update aliases with request: " + request, e);
}
}
@Override
public boolean removeAlias(AliasQuery query, IndexCoordinates index) {
Assert.notNull(index, "No index defined for Alias");
Assert.notNull(query.getAliasName(), "No alias defined");
IndicesAliasesRequest indicesAliasesRequest = requestFactory.indicesRemoveAliasesRequest(query, index);
try {
return client.indices().updateAliases(indicesAliasesRequest, RequestOptions.DEFAULT).isAcknowledged();
} catch (IOException e) {
throw new ElasticsearchException("failed to update aliases with indicesRemoveAliasesRequest: " + indicesAliasesRequest, e);
}
}
@Override
public boolean createIndex(String indexName, Object settings) {
CreateIndexRequest request = requestFactory.createIndexRequest(indexName, settings);
try {
return client.indices().create(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (IOException e) {
throw new ElasticsearchException("Error for creating index: " + request.toString(), e);
}
}
@Override
public boolean putMapping(IndexCoordinates index, Object mapping) {
Assert.notNull(index, "No index defined for putMapping()");
PutMappingRequest request = requestFactory.putMappingRequest(index, mapping);
try {
return client.indices().putMapping(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (IOException e) {
throw new ElasticsearchException("Failed to put mapping for " + index.getIndexName(), e);
}
}
@Override
public Map<String, Object> getMapping(IndexCoordinates index) {
Assert.notNull(index, "No index defined for getMapping()");
RestClient restClient = client.getLowLevelClient();
try {
Request request = new Request("GET",
'/' + index.getIndexName() + "/_mapping/" + index.getTypeName() + "?include_type_name=true");
Response response = restClient.performRequest(request);
return convertMappingResponse(EntityUtils.toString(response.getEntity()), index.getTypeName());
} catch (Exception e) {
throw new ElasticsearchException("Error while getting mapping for indexName : " + index.getIndexName()
+ " type : " + index.getTypeName() + ' ', e);
}
}
private Map<String, Object> convertMappingResponse(String mappingResponse, String type) {
ObjectMapper mapper = new ObjectMapper();
try {
Map<String, Object> result = null;
JsonNode node = mapper.readTree(mappingResponse);
node = node.findValue("mappings").findValue(type);
result = mapper.readValue(mapper.writeValueAsString(node), HashMap.class);
return result;
} catch (IOException e) {
throw new ElasticsearchException("Could not map alias response : " + mappingResponse, e);
}
}
@Override
public <T> T get(GetQuery query, Class<T> clazz, IndexCoordinates index) {
GetRequest request = requestFactory.getRequest(query, index);
try {
GetResponse response = client.get(request, RequestOptions.DEFAULT);
return elasticsearchConverter.mapDocument(DocumentAdapters.from(response), clazz);
} catch (IOException e) {
throw new ElasticsearchException("Error while getting for request: " + request.toString(), e);
}
}
@Nullable
private <T> T getObjectFromPage(Page<T> page) {
int contentSize = page.getContent().size();
Assert.isTrue(contentSize < 2, "Expected 1 but found " + contentSize + " results");
return contentSize > 0 ? page.getContent().get(0) : null;
}
@Override
protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request) {
MultiSearchResponse response;
try {
response = client.multiSearch(request, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new ElasticsearchException("Error for search request: " + request.toString(), e);
}
MultiSearchResponse.Item[] items = response.getResponses();
Assert.isTrue(items.length == request.requests().size(), "Response should has same length with queries");
return items;
}
@Override
public <T> AggregatedPage<T> queryForPage(Query query, Class<T> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
SearchResponse response;
try {
response = client.search(searchRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
}
return elasticsearchConverter.mapResults(SearchDocumentResponse.from(response), clazz, query.getPageable());
}
@Override
public <T> T query(Query query, ResultsExtractor<T> resultsExtractor, @Nullable Class<T> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
try {
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
return resultsExtractor.extract(result);
} catch (IOException e) {
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
}
}
@Override
public <T> List<T> queryForList(Query query, Class<T> clazz, IndexCoordinates index) {
return queryForPage(query, clazz, index).getContent();
}
@Override
public List<String> queryForIds(Query query, Class<?> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
try {
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
return extractIds(response);
} catch (IOException e) {
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
}
}
@Override
public <T> CloseableIterator<T> stream(Query query, Class<T> clazz, IndexCoordinates index) {
long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
return doStream(scrollTimeInMillis, startScroll(scrollTimeInMillis, query, clazz, index), clazz);
}
private <T> CloseableIterator<T> doStream(long scrollTimeInMillis, ScrolledPage<T> page, Class<T> clazz) {
return StreamQueries.streamResults(page, scrollId -> continueScroll(scrollId, scrollTimeInMillis, clazz),
this::clearScroll);
}
@Override
public long count(Query query, Class<?> clazz, IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
searchRequest.source().size(0);
try {
return SearchHitsUtil.getTotalCount(client.search(searchRequest, RequestOptions.DEFAULT).getHits());
} catch (IOException e) {
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
}
}
@Override
public <T> List<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
Assert.notEmpty(query.getIds(), "No Id define for Query");
MultiGetRequest request = requestFactory.multiGetRequest(query, index);
try {
MultiGetResponse result = client.mget(request, RequestOptions.DEFAULT);
return elasticsearchConverter.mapDocuments(DocumentAdapters.from(result), clazz);
} catch (IOException e) {
throw new ElasticsearchException("Error while multiget for request: " + request.toString(), e);
}
initialize(elasticsearchConverter, new DefaultIndexOperations(client, elasticsearchConverter));
}
// endregion
// region DocumentOperations
@Override
public String index(IndexQuery query, IndexCoordinates index) {
IndexRequest request = requestFactory.indexRequest(query, index);
@ -330,17 +126,34 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
}
@Override
public UpdateResponse update(UpdateQuery query, IndexCoordinates index) {
UpdateRequest request = requestFactory.updateRequest(query, index);
public <T> T get(GetQuery query, Class<T> clazz, IndexCoordinates index) {
GetRequest request = requestFactory.getRequest(query, index);
try {
return client.update(request, RequestOptions.DEFAULT);
GetResponse response = client.get(request, RequestOptions.DEFAULT);
return elasticsearchConverter.mapDocument(DocumentAdapters.from(response), clazz);
} catch (IOException e) {
throw new ElasticsearchException("Error while update for request: " + request.toString(), e);
throw new ElasticsearchException("Error while getting for request: " + request.toString(), e);
}
}
@Override
public <T> List<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
Assert.notEmpty(query.getIds(), "No Id define for Query");
MultiGetRequest request = requestFactory.multiGetRequest(query, index);
try {
MultiGetResponse result = client.mget(request, RequestOptions.DEFAULT);
return elasticsearchConverter.mapDocuments(DocumentAdapters.from(result), clazz);
} catch (IOException e) {
throw new ElasticsearchException("Error while multiget for request: " + request.toString(), e);
}
}
@Override
public void bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
Assert.notNull(queries, "List of IndexQuery must not be null");
Assert.notNull(bulkOptions, "BulkOptions must not be null");
@ -349,48 +162,16 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
@Override
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
Assert.notNull(queries, "List of UpdateQuery must not be null");
Assert.notNull(bulkOptions, "BulkOptions must not be null");
doBulkOperation(queries, bulkOptions, index);
}
private void doBulkOperation(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
BulkRequest bulkRequest = requestFactory.bulkRequest(queries, bulkOptions, index);
try {
checkForBulkOperationFailure(client.bulk(bulkRequest, RequestOptions.DEFAULT));
} catch (IOException e) {
throw new ElasticsearchException("Error while bulk for request: " + bulkRequest.toString(), e);
}
}
@Override
public boolean indexExists(String indexName) {
GetIndexRequest request = new GetIndexRequest(indexName);
try {
return client.indices().exists(request, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new ElasticsearchException("Error while for indexExists request: " + request.toString(), e);
}
}
@Override
public boolean deleteIndex(String indexName) {
Assert.notNull(indexName, "No index defined for delete operation");
if (indexExists(indexName)) {
DeleteIndexRequest request = new DeleteIndexRequest(indexName);
try {
return client.indices().delete(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (IOException e) {
throw new ElasticsearchException("Error while deleting index request: " + request.toString(), e);
}
}
return false;
}
@Override
public String delete(String id, IndexCoordinates index) {
DeleteRequest request = new DeleteRequest(index.getIndexName(), index.getTypeName(), id);
DeleteRequest request = new DeleteRequest(index.getIndexName(), id);
try {
return client.delete(request, RequestOptions.DEFAULT).getId();
} catch (IOException e) {
@ -408,9 +189,82 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
}
}
@Override
public UpdateResponse update(UpdateQuery query, IndexCoordinates index) {
UpdateRequest request = requestFactory.updateRequest(query, index);
try {
return client.update(request, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new ElasticsearchException("Error while update for request: " + request.toString(), e);
}
}
private void doBulkOperation(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
BulkRequest bulkRequest = requestFactory.bulkRequest(queries, bulkOptions, index);
try {
checkForBulkOperationFailure(client.bulk(bulkRequest, RequestOptions.DEFAULT));
} catch (IOException e) {
throw new ElasticsearchException("Error while bulk for request: " + bulkRequest.toString(), e);
}
}
// endregion
// region SearchOperations
@Override
public long count(Query query, Class<?> clazz, IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
searchRequest.source().size(0);
try {
return SearchHitsUtil.getTotalCount(client.search(searchRequest, RequestOptions.DEFAULT).getHits());
} catch (IOException e) {
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
}
}
@Override
public <T> T query(Query query, ResultsExtractor<T> resultsExtractor, @Nullable Class<T> clazz,
IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
try {
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
return resultsExtractor.extract(result);
} catch (IOException e) {
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
}
}
@Override
public <T> AggregatedPage<T> queryForPage(Query query, Class<T> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
SearchResponse response;
try {
response = client.search(searchRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
}
return elasticsearchConverter.mapResults(SearchDocumentResponse.from(response), clazz, query.getPageable());
}
@Override
public List<String> queryForIds(Query query, Class<?> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
try {
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
return extractIds(response);
} catch (IOException e) {
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
}
}
@Override
public <T> ScrolledPage<T> startScroll(long scrollTimeInMillis, Query query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(query.getPageable(), "Query.pageable is required for scan & scroll");
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
searchRequest.scroll(TimeValue.timeValueMillis(scrollTimeInMillis));
@ -422,7 +276,6 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
}
}
@Override
public <T> ScrolledPage<T> continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class<T> clazz) {
SearchScrollRequest request = new SearchScrollRequest(scrollId);
request.scroll(TimeValue.timeValueMillis(scrollTimeInMillis));
@ -447,119 +300,6 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
}
@Override
public <T> Map getSetting(Class<T> clazz) {
return getSetting(getPersistentEntityFor(clazz).getIndexName());
}
@Override // TODO change interface to return Settings.
public Map getSetting(String indexName) {
Assert.notNull(indexName, "No index defined for getSettings");
ObjectMapper objMapper = new ObjectMapper();
Map settings = null;
RestClient restClient = client.getLowLevelClient();
try {
Response response = restClient.performRequest(new Request("GET", "/" + indexName + "/_settings"));
settings = convertSettingResponse(EntityUtils.toString(response.getEntity()), indexName);
} catch (Exception e) {
throw new ElasticsearchException("Error while getting settings for indexName : " + indexName, e);
}
return settings;
}
private Map<String, String> convertSettingResponse(String settingResponse, String indexName) {
ObjectMapper mapper = new ObjectMapper();
try {
Settings settings = Settings.fromXContent(XContentType.JSON.xContent().createParser(NamedXContentRegistry.EMPTY,
DeprecationHandler.THROW_UNSUPPORTED_OPERATION, settingResponse));
String prefix = indexName + ".settings.";
// Backwards compatibility. TODO Change to return Settings object.
Map<String, String> result = new HashMap<String, String>();
Set<String> keySet = settings.keySet();
for (String key : keySet) {
result.put(key.substring(prefix.length()), settings.get(key));
}
return result;
} catch (IOException e) {
throw new ElasticsearchException("Could not map alias response : " + settingResponse, e);
}
}
@Override
public void refresh(IndexCoordinates index) {
Assert.notNull(index, "No index defined for refresh()");
try {
client.indices().refresh(refreshRequest(index.getIndexNames()), RequestOptions.DEFAULT);
} catch (IOException e) {
throw new ElasticsearchException("failed to refresh index: " + index, e);
}
}
@Override
public List<AliasMetaData> queryForAlias(String indexName) {
List<AliasMetaData> aliases = null;
RestClient restClient = client.getLowLevelClient();
Response response;
String aliasResponse;
try {
response = restClient.performRequest(new Request("GET", '/' + indexName + "/_alias/*"));
aliasResponse = EntityUtils.toString(response.getEntity());
} catch (Exception e) {
throw new ElasticsearchException("Error while getting mapping for indexName : " + indexName, e);
}
return convertAliasResponse(aliasResponse);
}
/**
* 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<AliasMetaData>();
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 ElasticsearchException("Could not map alias response : " + aliasResponse, e);
}
}
private List<String> extractIds(SearchResponse response) {
List<String> ids = new ArrayList<>();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
ids.add(hit.getId());
}
}
return ids;
}
public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) {
SearchRequest searchRequest = new SearchRequest(index.getIndexNames());
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
@ -571,5 +311,20 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
} catch (IOException e) {
throw new ElasticsearchException("Could not execute search request : " + searchRequest.toString(), e);
}
}
@Override
protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request) {
MultiSearchResponse response;
try {
response = client.multiSearch(request, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new ElasticsearchException("Error for search request: " + request.toString(), e);
}
MultiSearchResponse.Item[] items = response.getResponses();
Assert.isTrue(items.length == request.requests().size(), "Response should has same length with queries");
return items;
}
// endregion
}

View File

@ -15,21 +15,9 @@
*/
package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.client.Requests.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.get.GetResponse;
@ -42,23 +30,16 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequestBuilder;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.DocumentAdapters;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.query.AliasQuery;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.GetQuery;
@ -66,7 +47,6 @@ import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.support.SearchHitsUtil;
import org.springframework.data.util.CloseableIterator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@ -100,13 +80,13 @@ import org.springframework.util.Assert;
*/
@Deprecated
public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
private static final Logger QUERY_LOGGER = LoggerFactory
.getLogger("org.springframework.data.elasticsearch.core.QUERY");
private Client client;
private String searchTimeout;
// region Initialization
public ElasticsearchTemplate(Client client) {
initialize(client, createElasticsearchConverter());
}
@ -118,132 +98,21 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
private void initialize(Client client, ElasticsearchConverter elasticsearchConverter) {
Assert.notNull(client, "Client must not be null!");
this.client = client;
initialize(elasticsearchConverter);
initialize(elasticsearchConverter, new DefaultTransportIndexOperations(client, elasticsearchConverter));
}
// endregion
// region getter/setter
public String getSearchTimeout() {
return searchTimeout;
}
public void setSearchTimeout(String searchTimeout) {
this.searchTimeout = searchTimeout;
}
// endregion
@Override
public boolean addAlias(AliasQuery query, IndexCoordinates index) {
IndicesAliasesRequest.AliasActions aliasAction = requestFactory.aliasAction(query, index);
return client.admin().indices().prepareAliases().addAliasAction(aliasAction).execute().actionGet().isAcknowledged();
}
@Override
public boolean removeAlias(AliasQuery query, IndexCoordinates index) {
Assert.notNull(index, "No index defined for Alias");
Assert.notNull(query.getAliasName(), "No alias defined");
return client.admin().indices().prepareAliases().removeAlias(index.getIndexName(), query.getAliasName()).execute()
.actionGet().isAcknowledged();
}
@Override
public boolean createIndex(String indexName, Object settings) {
CreateIndexRequestBuilder createIndexRequestBuilder = requestFactory.createIndexRequestBuilder(client, indexName,
settings);
return createIndexRequestBuilder.execute().actionGet().isAcknowledged();
}
@Override
public boolean putMapping(IndexCoordinates index, Object mapping) {
Assert.notNull(index, "No index defined for putMapping()");
PutMappingRequestBuilder requestBuilder = requestFactory.putMappingRequestBuilder(client, index, mapping);
return requestBuilder.execute().actionGet().isAcknowledged();
}
@Override
public Map<String, Object> getMapping(IndexCoordinates index) {
Assert.notNull(index, "No index defined for putMapping()");
try {
return client.admin().indices()
.getMappings(new GetMappingsRequest().indices(index.getIndexNames()).types(index.getTypeNames())).actionGet()
.getMappings().get(index.getIndexName()).get(index.getTypeName()).getSourceAsMap();
} catch (Exception e) {
throw new ElasticsearchException("Error while getting mapping for indexName : " + index.getIndexName()
+ " type : " + index.getTypeName() + ' ' + e.getMessage());
}
}
@Override
public <T> T get(GetQuery query, Class<T> clazz, IndexCoordinates index) {
GetRequestBuilder getRequestBuilder = requestFactory.getRequestBuilder(client, query, index);
GetResponse response = getRequestBuilder.execute().actionGet();
T entity = elasticsearchConverter.mapDocument(DocumentAdapters.from(response), clazz);
return entity;
}
@Nullable
private <T> T getObjectFromPage(Page<T> page) {
int contentSize = page.getContent().size();
Assert.isTrue(contentSize < 2, "Expected 1 but found " + contentSize + " results");
return contentSize > 0 ? page.getContent().get(0) : null;
}
@Override
protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request) {
ActionFuture<MultiSearchResponse> future = client.multiSearch(request);
MultiSearchResponse response = future.actionGet();
MultiSearchResponse.Item[] items = response.getResponses();
Assert.isTrue(items.length == request.requests().size(), "Response should have same length with queries");
return items;
}
@Override
public <T> T query(Query query, ResultsExtractor<T> resultsExtractor, Class<T> clazz, IndexCoordinates index) {
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, query, clazz, index);
SearchResponse response = getSearchResponse(searchRequestBuilder);
return resultsExtractor.extract(response);
}
@Override
public <T> List<T> queryForList(Query query, Class<T> clazz, IndexCoordinates index) {
return queryForPage(query, clazz, index).getContent();
}
@Override
public List<String> queryForIds(Query query, Class<?> clazz, IndexCoordinates index) {
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, query, clazz, index);
SearchResponse response = getSearchResponse(searchRequestBuilder);
return extractIds(response);
}
@Override
public <T> AggregatedPage<T> queryForPage(Query query, Class<T> clazz, IndexCoordinates index) {
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, query, clazz, index);
SearchResponse response = getSearchResponse(searchRequestBuilder);
return elasticsearchConverter.mapResults(SearchDocumentResponse.from(response), clazz, query.getPageable());
}
@Override
public <T> CloseableIterator<T> stream(Query query, Class<T> clazz, IndexCoordinates index) {
long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
return doStream(scrollTimeInMillis, startScroll(scrollTimeInMillis, query, clazz, index), clazz);
}
private <T> CloseableIterator<T> doStream(long scrollTimeInMillis, ScrolledPage<T> page, Class<T> clazz) {
return StreamQueries.streamResults(page, scrollId -> continueScroll(scrollId, scrollTimeInMillis, clazz),
this::clearScroll);
}
@Override
public long count(Query query, @Nullable Class<?> clazz, IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, query, clazz, index);
searchRequestBuilder.setSize(0);
return SearchHitsUtil.getTotalCount(getSearchResponse(searchRequestBuilder).getHits());
}
@Override
public <T> List<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
Assert.notEmpty(query.getIds(), "No Id define for Query");
MultiGetRequestBuilder builder = requestFactory.multiGetRequestBuilder(client, query, index);
return elasticsearchConverter.mapDocuments(DocumentAdapters.from(builder.execute().actionGet()), clazz);
}
// region DocumentOperations
@Override
public String index(IndexQuery query, IndexCoordinates index) {
IndexRequestBuilder indexRequestBuilder = requestFactory.indexRequestBuilder(client, query, index);
@ -256,13 +125,27 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
}
@Override
public UpdateResponse update(UpdateQuery query, IndexCoordinates index) {
UpdateRequestBuilder updateRequestBuilder = requestFactory.updateRequestBuilderFor(client, query, index);
return updateRequestBuilder.execute().actionGet();
public <T> T get(GetQuery query, Class<T> clazz, IndexCoordinates index) {
GetRequestBuilder getRequestBuilder = requestFactory.getRequestBuilder(client, query, index);
GetResponse response = getRequestBuilder.execute().actionGet();
T entity = elasticsearchConverter.mapDocument(DocumentAdapters.from(response), clazz);
return entity;
}
@Override
public <T> List<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
Assert.notEmpty(query.getIds(), "No Id define for Query");
MultiGetRequestBuilder builder = requestFactory.multiGetRequestBuilder(client, query, index);
return elasticsearchConverter.mapDocuments(DocumentAdapters.from(builder.execute().actionGet()), clazz);
}
@Override
public void bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
Assert.notNull(queries, "List of IndexQuery must not be null");
Assert.notNull(bulkOptions, "BulkOptions must not be null");
@ -271,31 +154,13 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
@Override
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
Assert.notNull(queries, "List of UpdateQuery must not be null");
Assert.notNull(bulkOptions, "BulkOptions must not be null");
doBulkOperation(queries, bulkOptions, index);
}
private void doBulkOperation(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
BulkRequestBuilder bulkRequest = requestFactory.bulkRequestBuilder(client, queries, bulkOptions, index);
checkForBulkOperationFailure(bulkRequest.execute().actionGet());
}
@Override
public boolean indexExists(String indexName) {
return client.admin().indices().exists(indicesExistsRequest(indexName)).actionGet().isExists();
}
@Override
public boolean deleteIndex(String indexName) {
Assert.notNull(indexName, "No index defined for delete operation");
if (indexExists(indexName)) {
return client.admin().indices().delete(new DeleteIndexRequest(indexName)).actionGet().isAcknowledged();
}
return false;
}
@Override
public String delete(String id, IndexCoordinates index) {
return client.prepareDelete(index.getIndexName(), index.getTypeName(), id).execute().actionGet().getId();
@ -306,9 +171,55 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
requestFactory.deleteByQueryRequestBuilder(client, deleteQuery, index).get();
}
@Override
public UpdateResponse update(UpdateQuery query, IndexCoordinates index) {
UpdateRequestBuilder updateRequestBuilder = requestFactory.updateRequestBuilderFor(client, query, index);
return updateRequestBuilder.execute().actionGet();
}
private void doBulkOperation(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
BulkRequestBuilder bulkRequest = requestFactory.bulkRequestBuilder(client, queries, bulkOptions, index);
checkForBulkOperationFailure(bulkRequest.execute().actionGet());
}
// endregion
// region SearchOperations
@Override
public long count(Query query, @Nullable Class<?> clazz, IndexCoordinates index) {
Assert.notNull(index, "index must not be null");
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, query, clazz, index);
searchRequestBuilder.setSize(0);
return SearchHitsUtil.getTotalCount(getSearchResponse(searchRequestBuilder).getHits());
}
@Override
public <T> T query(Query query, ResultsExtractor<T> resultsExtractor, Class<T> clazz, IndexCoordinates index) {
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, query, clazz, index);
SearchResponse response = getSearchResponse(searchRequestBuilder);
return resultsExtractor.extract(response);
}
@Override
public <T> AggregatedPage<T> queryForPage(Query query, Class<T> clazz, IndexCoordinates index) {
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, query, clazz, index);
SearchResponse response = getSearchResponse(searchRequestBuilder);
return elasticsearchConverter.mapResults(SearchDocumentResponse.from(response), clazz, query.getPageable());
}
@Override
public List<String> queryForIds(Query query, Class<?> clazz, IndexCoordinates index) {
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, query, clazz, index);
SearchResponse response = getSearchResponse(searchRequestBuilder);
return extractIds(response);
}
@Override
public <T> ScrolledPage<T> startScroll(long scrollTimeInMillis, Query query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(query.getPageable(), "Query.pageable is required for scan & scroll");
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, query, clazz, index);
searchRequestBuilder.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis));
SearchResponse response = getSearchResponse(searchRequestBuilder);
@ -327,6 +238,20 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
client.prepareClearScroll().addScrollId(scrollId).execute().actionGet();
}
@Override
public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) {
return client.prepareSearch(index.getIndexNames()).suggest(suggestion).get();
}
@Override
protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request) {
ActionFuture<MultiSearchResponse> future = client.multiSearch(request);
MultiSearchResponse response = future.actionGet();
MultiSearchResponse.Item[] items = response.getResponses();
Assert.isTrue(items.length == request.requests().size(), "Response should have same length with queries");
return items;
}
private SearchResponse getSearchResponse(SearchRequestBuilder requestBuilder) {
if (QUERY_LOGGER.isDebugEnabled()) {
@ -338,44 +263,5 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
private SearchResponse getSearchResponseWithTimeout(ActionFuture<SearchResponse> response) {
return searchTimeout == null ? response.actionGet() : response.actionGet(searchTimeout);
}
@Override
public <T> Map<String, Object> getSetting(Class<T> clazz) {
return getSetting(getPersistentEntityFor(clazz).getIndexName());
}
@Override
public Map<String, Object> getSetting(String indexName) {
Assert.notNull(indexName, "No index defined for getSettings");
Settings settings = client.admin().indices().getSettings(new GetSettingsRequest()).actionGet().getIndexToSettings()
.get(indexName);
return settings.keySet().stream().collect(Collectors.toMap((key) -> key, (key) -> settings.get(key)));
}
@Override
public void refresh(IndexCoordinates index) {
Assert.notNull(index, "No index defined for refresh()");
client.admin().indices().refresh(refreshRequest(index.getIndexNames())).actionGet();
}
@Override
public List<AliasMetaData> queryForAlias(String indexName) {
return client.admin().indices().getAliases(new GetAliasesRequest().indices(indexName)).actionGet().getAliases()
.get(indexName);
}
private List<String> extractIds(SearchResponse response) {
List<String> ids = new ArrayList<>();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
ids.add(hit.getId());
}
}
return ids;
}
public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) {
return client.prepareSearch(index.getIndexNames()).suggest(suggestion).get();
}
// endregion
}

View File

@ -24,6 +24,7 @@ import java.util.Map;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
@ -127,7 +128,7 @@ class EntityOperations {
if (StringUtils.isEmpty(index)) {
Assert.notNull(entity, "Cannot determine index name");
return entity.getIndexName();
return entity.getIndexCoordinates().getIndexName();
}
return index;
@ -137,7 +138,7 @@ class EntityOperations {
if (StringUtils.isEmpty(type)) {
Assert.notNull(entity, "Cannot determine index type");
return entity.getIndexType();
return entity.getIndexCoordinates().getTypeName();
}
return type;

View File

@ -0,0 +1,216 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import java.util.List;
import java.util.Map;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.AliasQuery;
/**
* The operations for the
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices.html">Elasticsearch Index APIs</a>.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
public interface IndexOperations {
/**
* Create an index for given indexName.
*
* @param indexName the name of the index
* @return {@literal true} if the index was created
*/
boolean createIndex(String indexName);
/**
* Create an index for given indexName and Settings.
*
* @param indexName the name of the index
* @param settings the index settings
* @return {@literal true} if the index was created
*/
boolean createIndex(String indexName, Object settings);
/**
* Create an index for a class.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return {@literal true} if the index was created
*/
boolean createIndex(Class<?> clazz);
/**
* Create an index for given class and Settings.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @param settings the index settings
* @return {@literal true} if the index was created
*/
boolean createIndex(Class<?> clazz, Object settings);
/**
* Deletes an index for given entity.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return {@literal true} if the index was deleted
*/
boolean deleteIndex(Class<?> clazz);
/**
* Deletes an index.
*
* @param indexName the name of the index to delete
* @return {@literal true} if the index was deleted
*/
boolean deleteIndex(String indexName);
/**
* check if index exists.
*
* @param indexName the name of the index
* @return {@literal true} if the index exists
*/
boolean indexExists(String indexName);
/**
* check if index is exists.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return {@literal true} if the index exists
*/
boolean indexExists(Class<?> clazz);
/**
* Create mapping for a class and store it to the index.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return {@literal true} if the mapping could be stored
*/
boolean putMapping(Class<?> clazz);
/**
* Create mapping for the given class and put the mapping to the given index.
*
* @param index the index to store the mapping to
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return {@literal true} if the mapping could be stored
*/
boolean putMapping(IndexCoordinates index, Class<?> clazz);
/**
* Stores a mapping to an index.
*
* @param index the index to store the mapping to
* @param mappings can be a JSON String or a {@link Map}
* @return {@literal true} if the mapping could be stored
*/
boolean putMapping(IndexCoordinates index, Object mappings);
/**
* Create mapping for a class Stores a mapping to an index.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @param mappings can be a JSON String or a {@link Map}
* @return {@literal true} if the mapping could be stored
*/
<T> boolean putMapping(Class<T> clazz, Object mappings);
/**
* Get mapping for an index defined by a class.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}.
* @return the mapping
*/
Map<String, Object> getMapping(Class<?> clazz);
/**
* Get mapping for a given index.
*
* @param index the index to read the mapping from
* @return the mapping
*/
Map<String, Object> getMapping(IndexCoordinates index);
/**
* Add an alias.
*
* @param query query defining the alias
* @param index the index for which to add an alias
* @return true if the alias was created
*/
boolean addAlias(AliasQuery query, IndexCoordinates index);
/**
* Remove an alias.
*
* @param query query defining the alias
* @param index the index for which to remove an alias
* @return true if the alias was removed
*/
boolean removeAlias(AliasQuery query, IndexCoordinates index);
/**
* Get the alias informations for a specified index.
*
* @param indexName the name of the index
* @return alias information
*/
List<AliasMetaData> queryForAlias(String indexName);
/**
* Get settings for a given indexName.
*
* @param indexName the name of the index
* @return the settings
*/
Map<String, Object> getSetting(String indexName);
/**
* Get settings for a given class.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return the settings
*/
Map<String, Object> getSetting(Class<?> clazz);
/**
* Refresh the index(es).
*
* @param index the index to refresh
*/
void refresh(IndexCoordinates index);
/**
* Refresh the index.
*
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
*/
void refresh(Class<?> clazz);
}

View File

@ -0,0 +1,186 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
import reactor.core.publisher.Mono;
import org.springframework.util.Assert;
/**
* The reactive operations for the
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html">Elasticsearch Document APIs</a>.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
public interface ReactiveDocumentOperations {
/**
* Index the given entity, once available, extracting index and type from entity metadata.
*
* @param entityPublisher must not be {@literal null}.
* @param <T>
* @return a {@link Mono} emitting the saved entity.
*/
default <T> Mono<T> save(Mono<? extends T> entityPublisher) {
Assert.notNull(entityPublisher, "EntityPublisher must not be null!");
return entityPublisher.flatMap(this::save);
}
/**
* Index the given entity extracting index and type from entity metadata.
*
* @param entity must not be {@literal null}.
* @param <T>
* @return a {@link Mono} emitting the saved entity.
*/
<T> Mono<T> save(T entity);
/**
* Index the entity, once available, under the given {@literal type} in the given {@literal index}. If the
* {@literal index} is {@literal null} or empty the index name provided via entity metadata is used. Same for the
* {@literal type}.
*
* @param entityPublisher must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @param <T>
* @return a {@link Mono} emitting the saved entity.
*/
default <T> Mono<T> save(Mono<? extends T> entityPublisher, IndexCoordinates index) {
Assert.notNull(entityPublisher, "EntityPublisher must not be null!");
return entityPublisher.flatMap(it -> save(it, index));
}
/**
* 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}.
*
* @param entity must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @param <T>
* @return a {@link Mono} emitting the saved entity.
*/
<T> Mono<T> save(T entity, IndexCoordinates index);
/**
* Find the document with the given {@literal id} mapped onto the given {@literal entityType}.
*
* @param id the {@literal _id} of the document to fetch.
* @param entityType the domain type used for mapping the document.
* @param <T>
* @return {@link Mono#empty()} if not found.
*/
<T> Mono<T> findById(String id, Class<T> entityType);
/**
* Fetch the entity with given {@literal id}.
*
* @param id must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @param <T>
* @return the {@link Mono} emitting the entity or signalling completion if none found.
*/
<T> Mono<T> findById(String id, Class<T> entityType, IndexCoordinates index);
/**
* Check if an entity with given {@literal id} exists.
*
* @param id the {@literal _id} of the document to look for.
* @param entityType the domain type used.
* @return a {@link Mono} emitting {@literal true} if a matching document exists, {@literal false} otherwise.
*/
Mono<Boolean> exists(String id, Class<?> entityType);
/**
* Check if an entity with given {@literal id} exists.
*
* @param id the {@literal _id} of the document to look for.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting {@literal true} if a matching document exists, {@literal false} otherwise.
*/
Mono<Boolean> exists(String id, Class<?> entityType, IndexCoordinates index);
/**
* Delete the given entity extracting index and type from entity metadata.
*
* @param entity must not be {@literal null}.
* @return a {@link Mono} emitting the {@literal id} of the removed document.
*/
Mono<String> delete(Object entity);
/**
* Delete the given entity extracting index and type from entity metadata.
*
* @param entity must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the {@literal id} of the removed document.
*/
Mono<String> delete(Object entity, IndexCoordinates index);
/**
* Delete the entity with given {@literal id}.
*
* @param id must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the {@literal id} of the removed document.
*/
default Mono<String> deleteById(String id, IndexCoordinates index) {
Assert.notNull(index, "Index must not be null!");
return deleteById(id, Object.class, index);
}
/**
* Delete the entity with given {@literal id} extracting index and type from entity metadata.
*
* @param id must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @return a {@link Mono} emitting the {@literal id} of the removed document.
*/
Mono<String> deleteById(String id, Class<?> entityType);
/**
* Delete the entity with given {@literal id} extracting index and type from entity metadata.
*
* @param id must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the {@literal id} of the removed document.
*/
Mono<String> deleteById(String id, Class<?> entityType, IndexCoordinates index);
/**
* Delete the documents matching the given {@link Query} extracting index and type from entity metadata.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @return a {@link Mono} emitting the number of the removed documents.
*/
Mono<Long> deleteBy(Query query, Class<?> entityType);
/**
* Delete the documents matching the given {@link Query} extracting index and type from entity metadata.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the number of the removed documents.
*/
Mono<Long> deleteBy(Query query, Class<?> entityType, IndexCoordinates index);
}

View File

@ -15,19 +15,12 @@
*/
package org.springframework.data.elasticsearch.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.elasticsearch.index.query.QueryBuilders;
import org.reactivestreams.Publisher;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Interface that specifies a basic set of Elasticsearch operations executed in a reactive way.
@ -41,7 +34,7 @@ import org.springframework.util.Assert;
* @author Peter-Josef Meisch
* @since 3.2
*/
public interface ReactiveElasticsearchOperations {
public interface ReactiveElasticsearchOperations extends ReactiveDocumentOperations, ReactiveSearchOperations {
/**
* Execute within a {@link ClientCallback} managing resources and translating errors.
@ -52,261 +45,6 @@ public interface ReactiveElasticsearchOperations {
*/
<T> Publisher<T> execute(ClientCallback<Publisher<T>> callback);
/**
* Index the given entity, once available, extracting index and type from entity metadata.
*
* @param entityPublisher must not be {@literal null}.
* @param <T>
* @return a {@link Mono} emitting the saved entity.
*/
default <T> Mono<T> save(Mono<? extends T> entityPublisher) {
Assert.notNull(entityPublisher, "EntityPublisher must not be null!");
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.
*/
default <T> Mono<T> save(T entity) {
return save(entity, getIndexCoordinatesFor(entity.getClass()));
}
/**
* 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 type}.
*
* @param entityPublisher must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @param <T>
* @return a {@link Mono} emitting the saved entity.
*/
default <T> Mono<T> save(Mono<? extends T> entityPublisher, IndexCoordinates index) {
Assert.notNull(entityPublisher, "EntityPublisher must not be null!");
return entityPublisher.flatMap(it -> save(it, index));
}
/**
* 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}.
*
* @param entity must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @param <T>
* @return a {@link Mono} emitting the saved entity.
*/
<T> Mono<T> save(T entity, IndexCoordinates index);
/**
* Find the document with the given {@literal id} mapped onto the given {@literal entityType}.
*
* @param id the {@literal _id} of the document to fetch.
* @param entityType the domain type used for mapping the document.
* @param <T>
* @return {@link Mono#empty()} if not found.
*/
default <T> Mono<T> findById(String id, Class<T> entityType) {
return findById(id, entityType, getIndexCoordinatesFor(entityType));
}
/**
* Fetch the entity with given {@literal id}.
*
* @param id must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @param <T>
* @return the {@link Mono} emitting the entity or signalling completion if none found.
*/
<T> Mono<T> findById(String id, Class<T> entityType, IndexCoordinates index);
/**
* Check if an entity with given {@literal id} exists.
*
* @param id the {@literal _id} of the document to look for.
* @param entityType the domain type used.
* @return a {@link Mono} emitting {@literal true} if a matching document exists, {@literal false} otherwise.
*/
default Mono<Boolean> exists(String id, Class<?> entityType) {
return exists(id, entityType, getIndexCoordinatesFor(entityType));
}
/**
* Check if an entity with given {@literal id} exists.
*
* @param id the {@literal _id} of the document to look for.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting {@literal true} if a matching document exists, {@literal false} otherwise.
*/
Mono<Boolean> exists(String id, Class<?> entityType, IndexCoordinates index);
/**
* Search the index for entities matching the given {@link Query query}. <br />
* {@link Pageable#isUnpaged() Unpaged} queries may overrule elasticsearch server defaults for page size by either
* delegating to the scroll API or using a max {@link org.elasticsearch.search.builder.SearchSourceBuilder#size(int)
* size}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param <T>
* @return a {@link Flux} emitting matching entities one by one.
*/
default <T> Flux<T> find(Query query, Class<T> entityType) {
return find(query, entityType, entityType);
}
/**
* Search the index for entities matching the given {@link Query query}. <br />
* {@link Pageable#isUnpaged() Unpaged} queries may overrule elasticsearch server defaults for page size by either *
* delegating to the scroll API or using a max {@link org.elasticsearch.search.builder.SearchSourceBuilder#size(int) *
* size}.
*
* @param query must not be {@literal null}.
* @param entityType The entity type for mapping the query. Must not be {@literal null}.
* @param returnType The mapping target type. Must not be {@literal null}. Th
* @param <T>
* @return a {@link Flux} emitting matching entities one by one.
*/
default <T> Flux<T> find(Query query, Class<?> entityType, Class<T> returnType) {
return find(query, entityType, returnType, getIndexCoordinatesFor(entityType));
}
/**
* Search the index for entities matching the given {@link Query query}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @param <T>
* @returnm a {@link Flux} emitting matching entities one by one.
*/
default <T> Flux<T> find(Query query, Class<T> entityType, IndexCoordinates index) {
return find(query, entityType, entityType, index);
}
/**
* Search the index for entities matching the given {@link Query query}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param resultType the projection result type.
* @param index the target index, must not be {@literal null}
* @param <T>
* @return a {@link Flux} emitting matching entities one by one.
*/
<T> Flux<T> find(Query query, Class<?> entityType, Class<T> resultType, IndexCoordinates index);
/**
* Count the number of documents matching the given {@link Query}.
*
* @param entityType must not be {@literal null}.
* @return a {@link Mono} emitting the nr of matching documents.
*/
default Mono<Long> count(Class<?> entityType) {
return count(new StringQuery(QueryBuilders.matchAllQuery().toString()), entityType);
}
/**
* Count the number of documents matching the given {@link Query}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @return a {@link Mono} emitting the nr of matching documents.
*/
default Mono<Long> count(Query query, Class<?> entityType) {
return count(query, entityType, getIndexCoordinatesFor(entityType));
}
/**
* Count the number of documents matching the given {@link Query}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the nr of matching documents.
*/
Mono<Long> count(Query query, Class<?> entityType, IndexCoordinates index);
/**
* Delete the given entity extracting index and type from entity metadata.
*
* @param entity must not be {@literal null}.
* @return a {@link Mono} emitting the {@literal id} of the removed document.
*/
default Mono<String> delete(Object entity) {
return delete(entity, getIndexCoordinatesFor(entity.getClass()));
}
/**
* Delete the given entity extracting index and type from entity metadata.
*
* @param entity must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the {@literal id} of the removed document.
*/
Mono<String> delete(Object entity, IndexCoordinates index);
/**
* Delete the entity with given {@literal id}.
*
* @param id must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the {@literal id} of the removed document.
*/
default Mono<String> deleteById(String id, IndexCoordinates index) {
Assert.notNull(index, "Index must not be null!");
return deleteById(id, Object.class, index);
}
/**
* Delete the entity with given {@literal id} extracting index and type from entity metadata.
*
* @param id must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @return a {@link Mono} emitting the {@literal id} of the removed document.
*/
default Mono<String> deleteById(String id, Class<?> entityType) {
return deleteById(id, entityType, getIndexCoordinatesFor(entityType));
}
/**
* Delete the entity with given {@literal id} extracting index and type from entity metadata.
*
* @param id must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the {@literal id} of the removed document.
*/
Mono<String> deleteById(String id, Class<?> entityType, IndexCoordinates index);
/**
* Delete the documents matching the given {@link Query} extracting index and type from entity metadata.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @return a {@link Mono} emitting the number of the removed documents.
*/
default Mono<Long> deleteBy(Query query, Class<?> entityType) {
return deleteBy(query, entityType, getIndexCoordinatesFor(entityType));
}
/**
* Delete the documents matching the given {@link Query} extracting index and type from entity metadata.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the number of the removed documents.
*/
Mono<Long> deleteBy(Query query, Class<?> entityType, IndexCoordinates index);
/**
* Get the {@link ElasticsearchConverter} used.
*
@ -322,10 +60,7 @@ public interface ReactiveElasticsearchOperations {
* @return the IndexCoordinates defined on the entity.
* @since 4.0
*/
default IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
ElasticsearchPersistentEntity entity = getPersistentEntityFor(clazz);
return IndexCoordinates.of(entity.getIndexName()).withTypes(entity.getIndexType());
}
IndexCoordinates getIndexCoordinatesFor(Class<?> clazz);
/**
* Callback interface to be used with {@link #execute(ClientCallback)} for operating directly on

View File

@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.core;
import static org.elasticsearch.index.VersionType.*;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -48,7 +49,6 @@ import org.elasticsearch.search.sort.SortOrder;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
@ -93,6 +93,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
private @Nullable RefreshPolicy refreshPolicy = RefreshPolicy.IMMEDIATE;
private @Nullable IndicesOptions indicesOptions = IndicesOptions.strictExpandOpenAndForbidClosedIgnoreThrottled();
// region Initialization
public ReactiveElasticsearchTemplate(ReactiveElasticsearchClient client) {
this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext()));
}
@ -109,19 +110,12 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
this.operations = new EntityOperations(this.mappingContext);
this.requestFactory = new RequestFactory(converter);
}
// endregion
// region DocumentOperations
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#exctute(ClientCallback)
*/
@Override
public <T> Publisher<T> execute(ClientCallback<Publisher<T>> callback) {
return Flux.defer(() -> callback.doWithClient(getClient())).onErrorMap(this::translateException);
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#index(Object, IndexCoordinates)
* @see org.springframework.data.elasticsearch.core.ReactiveDElasticsearchOperations#index(Object, IndexCoordinates)
*/
@Override
public <T> Mono<T> save(T entity, IndexCoordinates index) {
@ -136,6 +130,51 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
});
}
@Override
public <T> Mono<T> save(T entity) {
return save(entity, getIndexCoordinatesFor(entity.getClass()));
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
* You know what you're doing here? Well fair enough, go ahead on your own risk.
*
* @param request the already prepared {@link IndexRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<IndexResponse> doIndex(IndexRequest request) {
return Mono.from(execute(client -> client.index(request)));
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link GetRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<GetResult> doFindById(GetRequest request) {
return Mono.from(execute(client -> client.get(request))) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
}
@Override
public Mono<Boolean> exists(String id, Class<?> entityType) {
return exists(id, entityType, getIndexCoordinatesFor(entityType));
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link GetRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<Boolean> doExists(GetRequest request) {
return Mono.from(execute(client -> client.exists(request))) //
.onErrorReturn(NoSuchIndexException.class, false);
}
private Mono<IndexResponse> doIndex(Object value, AdaptibleEntity<?> entity, IndexCoordinates index) {
return Mono.defer(() -> {
@ -162,6 +201,11 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
});
}
@Override
public <T> Mono<T> findById(String id, Class<T> entityType) {
return findById(id, entityType, getIndexCoordinatesFor(entityType));
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#findById(String, Class, IndexCoordinates)
@ -200,6 +244,179 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
return Mono.defer(() -> doExists(new GetRequest(index.getIndexName(), index.getTypeName(), id)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#delete(Object, String, String)
*/
@Override
public Mono<String> delete(Object entity, IndexCoordinates index) {
Entity<?> elasticsearchEntity = operations.forEntity(entity);
return Mono.defer(() -> doDeleteById(entity, converter.convertId(elasticsearchEntity.getId()),
elasticsearchEntity.getPersistentEntity(), index));
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#delete(String, Class, IndexCoordinates)
*/
@Override
public Mono<String> deleteById(String id, Class<?> entityType, IndexCoordinates index) {
Assert.notNull(id, "Id must not be null!");
return doDeleteById(null, id, getPersistentEntityFor(entityType), index);
}
@Override
public Mono<String> delete(Object entity) {
return delete(entity, getIndexCoordinatesFor(entity.getClass()));
}
@Override
public Mono<String> deleteById(String id, Class<?> entityType) {
return deleteById(id, entityType, getIndexCoordinatesFor(entityType));
}
private Mono<String> doDeleteById(@Nullable Object source, String id, ElasticsearchPersistentEntity<?> entity,
IndexCoordinates index) {
return Mono.defer(() -> {
return doDelete(prepareDeleteRequest(source, new DeleteRequest(index.getIndexName(), index.getTypeName(), id)));
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#deleteBy(Query, Class, IndexCoordinates)
*/
@Override
public Mono<Long> deleteBy(Query query, Class<?> entityType, IndexCoordinates index) {
Assert.notNull(query, "Query must not be null!");
return doDeleteBy(query, getPersistentEntityFor(entityType), index).map(BulkByScrollResponse::getDeleted)
.publishNext();
}
@Override
public Mono<Long> deleteBy(Query query, Class<?> entityType) {
return deleteBy(query, entityType, getIndexCoordinatesFor(entityType));
}
private Flux<BulkByScrollResponse> doDeleteBy(Query query, ElasticsearchPersistentEntity<?> entity,
IndexCoordinates index) {
return Flux.defer(() -> {
DeleteByQueryRequest request = new DeleteByQueryRequest(index.getIndexNames());
request.types(index.getTypeNames());
request.setQuery(mappedQuery(query, entity));
return doDeleteBy(prepareDeleteByRequest(request));
});
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link DeleteRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<String> doDelete(DeleteRequest request) {
return Mono.from(execute(client -> client.delete(request))) //
.flatMap(it -> {
if (HttpStatus.valueOf(it.status().getStatus()).equals(HttpStatus.NOT_FOUND)) {
return Mono.empty();
}
return Mono.just(it.getId());
}) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link DeleteByQueryRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<BulkByScrollResponse> doDeleteBy(DeleteByQueryRequest request) {
return Mono.from(execute(client -> client.deleteBy(request))) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
}
/**
* Customization hook to modify a generated {@link DeleteRequest} prior to its execution. Eg. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param source the source object the {@link DeleteRequest} was derived from. My be {@literal null} if using the
* {@literal id} directly.
* @param request the generated {@link DeleteRequest}.
* @return never {@literal null}.
*/
protected DeleteRequest prepareDeleteRequest(@Nullable Object source, DeleteRequest request) {
return prepareWriteRequest(request);
}
/**
* Customization hook to modify a generated {@link DeleteByQueryRequest} prior to its execution. Eg. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param request the generated {@link DeleteByQueryRequest}.
* @return never {@literal null}.
*/
protected DeleteByQueryRequest prepareDeleteByRequest(DeleteByQueryRequest request) {
if (refreshPolicy != null && !RefreshPolicy.NONE.equals(refreshPolicy)) {
request = request.setRefresh(true);
}
if (indicesOptions != null) {
request = request.setIndicesOptions(indicesOptions);
}
return request;
}
/**
* Customization hook to modify a generated {@link IndexRequest} prior to its execution. Eg. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param source the source object the {@link IndexRequest} was derived from.
* @param request the generated {@link IndexRequest}.
* @return never {@literal null}.
*/
protected IndexRequest prepareIndexRequest(Object source, IndexRequest request) {
return prepareWriteRequest(request);
}
/**
* Pre process the write request before it is sent to the server, eg. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param request must not be {@literal null}.
* @param <R>
* @return the processed {@link WriteRequest}.
*/
protected <R extends WriteRequest<R>> R prepareWriteRequest(R request) {
if (refreshPolicy == null) {
return request;
}
return request.setRefreshPolicy(refreshPolicy);
}
// endregion
// region SearchOperations
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#find(Query, Class, Class, IndexCoordinates)
@ -210,6 +427,11 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
return doFind(query, entityType, index).map(it -> converter.mapDocument(DocumentAdapters.from(it), resultType));
}
@Override
public <T> Flux<T> find(Query query, Class<?> entityType, Class<T> returnType) {
return find(query, entityType, returnType, getIndexCoordinatesFor(entityType));
}
private Flux<SearchHit> doFind(Query query, Class<?> clazz, IndexCoordinates index) {
return Flux.defer(() -> {
@ -227,6 +449,11 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
});
}
@Override
public Mono<Long> count(Query query, Class<?> entityType) {
return count(query, entityType, getIndexCoordinatesFor(entityType));
}
@Override
public Mono<Long> count(Query query, Class<?> entityType, IndexCoordinates index) {
return doCount(query, getPersistentEntityFor(entityType), index);
@ -279,235 +506,6 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
return request;
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#delete(Object, String, String)
*/
@Override
public Mono<String> delete(Object entity, IndexCoordinates index) {
Entity<?> elasticsearchEntity = operations.forEntity(entity);
return Mono.defer(() -> doDeleteById(entity, converter.convertId(elasticsearchEntity.getId()),
elasticsearchEntity.getPersistentEntity(), index));
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#delete(String, Class, IndexCoordinates)
*/
@Override
public Mono<String> deleteById(String id, Class<?> entityType, IndexCoordinates index) {
Assert.notNull(id, "Id must not be null!");
return doDeleteById(null, id, getPersistentEntityFor(entityType), index);
}
private Mono<String> doDeleteById(@Nullable Object source, String id, ElasticsearchPersistentEntity<?> entity,
IndexCoordinates index) {
return Mono.defer(() -> {
return doDelete(prepareDeleteRequest(source, new DeleteRequest(index.getIndexName(), index.getTypeName(), id)));
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#deleteBy(Query, Class, IndexCoordinates)
*/
@Override
public Mono<Long> deleteBy(Query query, Class<?> entityType, IndexCoordinates index) {
Assert.notNull(query, "Query must not be null!");
return doDeleteBy(query, getPersistentEntityFor(entityType), index).map(BulkByScrollResponse::getDeleted)
.publishNext();
}
private Flux<BulkByScrollResponse> doDeleteBy(Query query, ElasticsearchPersistentEntity<?> entity,
IndexCoordinates index) {
return Flux.defer(() -> {
DeleteByQueryRequest request = new DeleteByQueryRequest(index.getIndexNames());
request.types(index.getTypeNames());
request.setQuery(mappedQuery(query, entity));
return doDeleteBy(prepareDeleteByRequest(request));
});
}
// Property Setters / Getters
/**
* Set the default {@link RefreshPolicy} to apply when writing to Elasticsearch.
*
* @param refreshPolicy can be {@literal null}.
*/
public void setRefreshPolicy(@Nullable RefreshPolicy refreshPolicy) {
this.refreshPolicy = refreshPolicy;
}
/**
* Set the default {@link IndicesOptions} for {@link SearchRequest search requests}.
*
* @param indicesOptions can be {@literal null}.
*/
public void setIndicesOptions(@Nullable IndicesOptions indicesOptions) {
this.indicesOptions = indicesOptions;
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#getElasticsearchConverter()
*/
@Override
public ElasticsearchConverter getElasticsearchConverter() {
return converter;
}
// Customization Hooks
/**
* Obtain the {@link ReactiveElasticsearchClient} to operate upon.
*
* @return never {@literal null}.
*/
protected ReactiveElasticsearchClient getClient() {
return this.client;
}
/**
* Pre process the write request before it is sent to the server, eg. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param request must not be {@literal null}.
* @param <R>
* @return the processed {@link WriteRequest}.
*/
protected <R extends WriteRequest<R>> R prepareWriteRequest(R request) {
if (refreshPolicy == null) {
return request;
}
return request.setRefreshPolicy(refreshPolicy);
}
/**
* Customization hook to modify a generated {@link IndexRequest} prior to its execution. Eg. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param source the source object the {@link IndexRequest} was derived from.
* @param request the generated {@link IndexRequest}.
* @return never {@literal null}.
*/
protected IndexRequest prepareIndexRequest(Object source, IndexRequest request) {
return prepareWriteRequest(request);
}
/**
* Customization hook to modify a generated {@link SearchRequest} prior to its execution. Eg. by setting the
* {@link SearchRequest#indicesOptions(IndicesOptions) indices options} if applicable.
*
* @param request the generated {@link CountRequest}.
* @return never {@literal null}.
*/
protected CountRequest prepareCountRequest(CountRequest request) {
if (indicesOptions == null) {
return request;
}
return request.indicesOptions(indicesOptions);
}
/**
* Customization hook to modify a generated {@link SearchRequest} prior to its execution. Eg. by setting the
* {@link SearchRequest#indicesOptions(IndicesOptions) indices options} if applicable.
*
* @param request the generated {@link SearchRequest}.
* @return never {@literal null}.
*/
protected SearchRequest prepareSearchRequest(SearchRequest request) {
if (indicesOptions == null) {
return request;
}
return request.indicesOptions(indicesOptions);
}
/**
* Customization hook to modify a generated {@link DeleteRequest} prior to its execution. Eg. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param source the source object the {@link DeleteRequest} was derived from. My be {@literal null} if using the
* {@literal id} directly.
* @param request the generated {@link DeleteRequest}.
* @return never {@literal null}.
*/
protected DeleteRequest prepareDeleteRequest(@Nullable Object source, DeleteRequest request) {
return prepareWriteRequest(request);
}
/**
* Customization hook to modify a generated {@link DeleteByQueryRequest} prior to its execution. Eg. by setting the
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
*
* @param request the generated {@link DeleteByQueryRequest}.
* @return never {@literal null}.
*/
protected DeleteByQueryRequest prepareDeleteByRequest(DeleteByQueryRequest request) {
if (refreshPolicy != null && !RefreshPolicy.NONE.equals(refreshPolicy)) {
request = request.setRefresh(true);
}
if (indicesOptions != null) {
request = request.setIndicesOptions(indicesOptions);
}
return request;
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
* You know what you're doing here? Well fair enough, go ahead on your own risk.
*
* @param request the already prepared {@link IndexRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<IndexResponse> doIndex(IndexRequest request) {
return Mono.from(execute(client -> client.index(request)));
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link GetRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<GetResult> doFindById(GetRequest request) {
return Mono.from(execute(client -> client.get(request))) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link GetRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<Boolean> doExists(GetRequest request) {
return Mono.from(execute(client -> client.exists(request))) //
.onErrorReturn(NoSuchIndexException.class, false);
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
@ -556,41 +554,35 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link DeleteRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<String> doDelete(DeleteRequest request) {
@Nullable
private QueryBuilder mappedFilterQuery(Query query, ElasticsearchPersistentEntity<?> entity) {
return Mono.from(execute(client -> client.delete(request))) //
if (query instanceof NativeSearchQuery) {
return ((NativeSearchQuery) query).getFilter();
}
.flatMap(it -> {
if (HttpStatus.valueOf(it.status().getStatus()).equals(HttpStatus.NOT_FOUND)) {
return Mono.empty();
}
return Mono.just(it.getId());
}) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
return null;
}
/**
* Customization hook on the actual execution result {@link Publisher}. <br />
*
* @param request the already prepared {@link DeleteByQueryRequest} ready to be executed.
* @return a {@link Mono} emitting the result of the operation.
*/
protected Mono<BulkByScrollResponse> doDeleteBy(DeleteByQueryRequest request) {
private QueryBuilder mappedQuery(Query query, ElasticsearchPersistentEntity<?> entity) {
return Mono.from(execute(client -> client.deleteBy(request))) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
// TODO: we need to actually map the fields to the according field names!
QueryBuilder elasticsearchQuery = null;
if (query instanceof CriteriaQuery) {
elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(((CriteriaQuery) query).getCriteria());
} else if (query instanceof StringQuery) {
elasticsearchQuery = new WrapperQueryBuilder(((StringQuery) query).getSource());
} else if (query instanceof NativeSearchQuery) {
elasticsearchQuery = ((NativeSearchQuery) query).getQuery();
} else {
throw new IllegalArgumentException(String.format("Unknown query type '%s'.", query.getClass()));
}
return elasticsearchQuery != null ? elasticsearchQuery : QueryBuilders.matchAllQuery();
}
// private helpers
private static List<FieldSortBuilder> sort(Query query, ElasticsearchPersistentEntity<?> entity) {
if (query.getSort() == null || query.getSort().isUnsorted()) {
@ -618,33 +610,82 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
return mappedSort;
}
private QueryBuilder mappedQuery(Query query, ElasticsearchPersistentEntity<?> entity) {
/**
* Customization hook to modify a generated {@link SearchRequest} prior to its execution. Eg. by setting the
* {@link SearchRequest#indicesOptions(IndicesOptions) indices options} if applicable.
*
* @param request the generated {@link CountRequest}.
* @return never {@literal null}.
*/
protected CountRequest prepareCountRequest(CountRequest request) {
// TODO: we need to actually map the fields to the according field names!
QueryBuilder elasticsearchQuery = null;
if (query instanceof CriteriaQuery) {
elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(((CriteriaQuery) query).getCriteria());
} else if (query instanceof StringQuery) {
elasticsearchQuery = new WrapperQueryBuilder(((StringQuery) query).getSource());
} else if (query instanceof NativeSearchQuery) {
elasticsearchQuery = ((NativeSearchQuery) query).getQuery();
} else {
throw new IllegalArgumentException(String.format("Unknown query type '%s'.", query.getClass()));
if (indicesOptions == null) {
return request;
}
return elasticsearchQuery != null ? elasticsearchQuery : QueryBuilders.matchAllQuery();
return request.indicesOptions(indicesOptions);
}
@Nullable
private QueryBuilder mappedFilterQuery(Query query, ElasticsearchPersistentEntity<?> entity) {
/**
* Customization hook to modify a generated {@link SearchRequest} prior to its execution. Eg. by setting the
* {@link SearchRequest#indicesOptions(IndicesOptions) indices options} if applicable.
*
* @param request the generated {@link SearchRequest}.
* @return never {@literal null}.
*/
protected SearchRequest prepareSearchRequest(SearchRequest request) {
if (query instanceof NativeSearchQuery) {
return ((NativeSearchQuery) query).getFilter();
if (indicesOptions == null) {
return request;
}
return null;
return request.indicesOptions(indicesOptions);
}
// endregion
// region Helper methods
// Property Setters / Getters
/**
* Set the default {@link RefreshPolicy} to apply when writing to Elasticsearch.
*
* @param refreshPolicy can be {@literal null}.
*/
public void setRefreshPolicy(@Nullable RefreshPolicy refreshPolicy) {
this.refreshPolicy = refreshPolicy;
}
/**
* Set the default {@link IndicesOptions} for {@link SearchRequest search requests}.
*
* @param indicesOptions can be {@literal null}.
*/
public void setIndicesOptions(@Nullable IndicesOptions indicesOptions) {
this.indicesOptions = indicesOptions;
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#exctute(ClientCallback)
*/
@Override
public <T> Publisher<T> execute(ClientCallback<Publisher<T>> callback) {
return Flux.defer(() -> callback.doWithClient(getClient())).onErrorMap(this::translateException);
}
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations#getElasticsearchConverter()
*/
@Override
public ElasticsearchConverter getElasticsearchConverter() {
return converter;
}
@Override
public IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
return getPersistentEntityFor(clazz).getIndexCoordinates();
}
@Override
@ -661,4 +702,16 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
return potentiallyTranslatedException != null ? potentiallyTranslatedException : throwable;
}
/**
* Obtain the {@link ReactiveElasticsearchClient} to operate upon.
*
* @return never {@literal null}.
*/
protected ReactiveElasticsearchClient getClient() {
return this.client;
}
// endregion
}

View File

@ -0,0 +1,119 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.StringQuery;
/**
* The reactive operations for the
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search.html">Elasticsearch Document
* APIs</a>.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
public interface ReactiveSearchOperations {
/**
* Search the index for entities matching the given {@link Query query}. <br />
* {@link Pageable#isUnpaged() Unpaged} queries may overrule elasticsearch server defaults for page size by either
* delegating to the scroll API or using a max {@link org.elasticsearch.search.builder.SearchSourceBuilder#size(int)
* size}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param <T>
* @return a {@link Flux} emitting matching entities one by one.
*/
default <T> Flux<T> find(Query query, Class<T> entityType) {
return find(query, entityType, entityType);
}
/**
* Search the index for entities matching the given {@link Query query}. <br />
* {@link Pageable#isUnpaged() Unpaged} queries may overrule elasticsearch server defaults for page size by either *
* delegating to the scroll API or using a max {@link org.elasticsearch.search.builder.SearchSourceBuilder#size(int) *
* size}.
*
* @param query must not be {@literal null}.
* @param entityType The entity type for mapping the query. Must not be {@literal null}.
* @param returnType The mapping target type. Must not be {@literal null}. Th
* @param <T>
* @return a {@link Flux} emitting matching entities one by one.
*/
<T> Flux<T> find(Query query, Class<?> entityType, Class<T> returnType);
/**
* Search the index for entities matching the given {@link Query query}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @param <T>
* @returnm a {@link Flux} emitting matching entities one by one.
*/
default <T> Flux<T> find(Query query, Class<T> entityType, IndexCoordinates index) {
return find(query, entityType, entityType, index);
}
/**
* Search the index for entities matching the given {@link Query query}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param resultType the projection result type.
* @param index the target index, must not be {@literal null}
* @param <T>
* @return a {@link Flux} emitting matching entities one by one.
*/
<T> Flux<T> find(Query query, Class<?> entityType, Class<T> resultType, IndexCoordinates index);
/**
* Count the number of documents matching the given {@link Query}.
*
* @param entityType must not be {@literal null}.
* @return a {@link Mono} emitting the nr of matching documents.
*/
default Mono<Long> count(Class<?> entityType) {
return count(new StringQuery(QueryBuilders.matchAllQuery().toString()), entityType);
}
/**
* Count the number of documents matching the given {@link Query}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @return a {@link Mono} emitting the nr of matching documents.
*/
Mono<Long> count(Query query, Class<?> entityType);
/**
* Count the number of documents matching the given {@link Query}.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the nr of matching documents.
*/
Mono<Long> count(Query query, Class<?> entityType, IndexCoordinates index);
}

View File

@ -66,6 +66,7 @@ import org.springframework.data.elasticsearch.ElasticsearchException;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

View File

@ -0,0 +1,204 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
import java.util.List;
import java.util.stream.Collectors;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.util.CloseableIterator;
import org.springframework.lang.Nullable;
/**
* The operations for the
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search.html">Elasticsearch Document
* APIs</a>.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
public interface SearchOperations {
/**
* Return number of elements found by given query.
*
* @param query the query to execute
* @param index the index to run the query against
* @return count
*/
default long count(Query query, IndexCoordinates index) {
return count(query, null, index);
}
/**
* return number of elements found by given query
*
* @param query the query to execute
* @param clazz the entity clazz used for property mapping
* @param index the index to run the query against
* @return count
*/
long count(Query query, @Nullable Class<?> clazz, IndexCoordinates index);
<T> T query(Query query, ResultsExtractor<T> resultsExtractor, Class<T> clazz, IndexCoordinates index);
/**
* Execute the query against elasticsearch and return the first returned object.
*
* @param query the query to execute
* @param clazz the entity clazz used for property mapping
* @param index the index to run the query against
* @return the first matching object
*/
default <T> T queryForObject(Query query, Class<T> clazz, IndexCoordinates index) {
List<T> content = queryForPage(query, clazz, index).getContent();
return content.isEmpty() ? null : content.get(0);
}
/**
* Execute the query against elasticsearch and return result as {@link Page}.
*
* @param query the query to execute
* @param clazz the entity clazz used for property mapping
* @param index the index to run the query against
* @return a page with aggregations
*/
<T> AggregatedPage<T> queryForPage(Query query, Class<T> clazz, IndexCoordinates index);
/**
* Execute the multi-search against elasticsearch and return result as {@link List} of {@link Page}
*
* @param queries the queries
* @param clazz the entity clazz used for property mapping
* @param index the index to run the query against
* @return list of pages with the results
*/
<T> List<Page<T>> queryForPage(List<? extends Query> queries, Class<T> clazz, IndexCoordinates index);
/**
* Execute the multi-search against elasticsearch and return result as {@link List} of {@link Page}
*
* @param queries the queries
* @param classes the entity classes used for the queries
* @param index the index to run the query against
* @return list of pages with the results
*/
List<Page<?>> queryForPage(List<? extends Query> queries, List<Class<?>> classes, IndexCoordinates index);
/**
* Executes the given {@link Query} against elasticsearch and return result as {@link CloseableIterator}.
* <p>
*
* @param <T> element return type
* @param query the query to execute
* @param clazz the entity clazz used for property mapping
* @param index the index to run the query against
* @return a {@link CloseableIterator} that wraps an Elasticsearch scroll context that needs to be closed in case of *
* error.
*/
<T> CloseableIterator<T> stream(Query query, Class<T> clazz, IndexCoordinates index);
/**
* Execute the criteria query against elasticsearch and return result as {@link List}
*
* @param query the query to execute
* @param clazz the entity clazz used for property mapping
* @param index the index to run the query against
* @param <T> element return type
* @return list of found objects
*/
<T> List<T> queryForList(Query query, Class<T> clazz, IndexCoordinates index);
/**
* Execute the multi search query against elasticsearch and return result as {@link List}
*
* @param queries the queries to execute
* @param clazz the entity clazz used for property mapping
* @param index the index to run the query against
* @param <T> element return type
* @return list of found objects
*/
default <T> List<List<T>> queryForList(List<Query> queries, Class<T> clazz, IndexCoordinates index) {
return queryForPage(queries, clazz, index).stream().map(Page::getContent).collect(Collectors.toList());
}
/**
* Execute the multi search query against elasticsearch and return result as {@link List}
*
* @param queries the queries to execute
* @param classes the entity classes used for property mapping
* @param index the index to run the query against
* @return list of list of found objects
*/
default List<List<?>> queryForList(List<Query> queries, List<Class<?>> classes, IndexCoordinates index) {
return queryForPage(queries, classes, index).stream().map(Page::getContent).collect(Collectors.toList());
}
/**
* Execute the query against elasticsearch and return ids
*
* @param query the query to execute
* @param clazz the entity clazz used for property mapping
* @param index the index to run the query against
* @return list of found object ids
*/
List<String> queryForIds(Query query, Class<?> clazz, IndexCoordinates index);
/**
* Returns scrolled page for given query
*
* @param scrollTimeInMillis duration of the scroll time
* @param query The search query.
* @param clazz The class of entity to retrieve.
* @param index the index to run the query against
* @return scrolled page result
*/
<T> ScrolledPage<T> startScroll(long scrollTimeInMillis, Query query, Class<T> clazz, IndexCoordinates index);
<T> ScrolledPage<T> continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class<T> clazz);
/**
* Clears the search contexts associated with specified scroll ids.
*
* @param scrollId the scroll id
*/
void clearScroll(String scrollId);
/**
* more like this query to search for documents that are "like" a specific document.
*
* @param query the query to execute
* @param clazz the entity clazz used for property mapping
* @param index the index to run the query against
* @param <T> element return type
* @return page with the results
*/
<T> Page<T> moreLikeThis(MoreLikeThisQuery query, Class<T> clazz, IndexCoordinates index);
/**
* Does a suggest query
*
* @param suggestion the query
* @param index the index to run the query against
* @return the suggest response
*/
SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index);
}

View File

@ -31,16 +31,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.CompletionContext;
import org.springframework.data.elasticsearch.annotations.CompletionField;
import org.springframework.data.elasticsearch.annotations.DynamicMapping;
import org.springframework.data.elasticsearch.annotations.DynamicTemplates;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.GeoPointField;
import org.springframework.data.elasticsearch.annotations.InnerField;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.ResourceUtil;
import org.springframework.data.elasticsearch.core.completion.Completion;
@ -111,7 +102,7 @@ public class MappingBuilder {
ElasticsearchPersistentEntity<?> entity = elasticsearchConverter.getMappingContext()
.getRequiredPersistentEntity(clazz);
XContentBuilder builder = jsonBuilder().startObject().startObject(entity.getIndexType());
XContentBuilder builder = jsonBuilder().startObject().startObject(entity.getIndexCoordinates().getTypeName());
// Dynamic templates
addDynamicTemplatesMapping(builder, entity);

View File

@ -32,9 +32,7 @@ import org.springframework.lang.Nullable;
*/
public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, ElasticsearchPersistentProperty> {
String getIndexName();
String getIndexType();
IndexCoordinates getIndexCoordinates();
short getShards();

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.core.mapping;
import java.util.Arrays;

View File

@ -102,8 +102,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
context.setRootObject(applicationContext);
}
@Override
public String getIndexName() {
private String getIndexName() {
if (indexName != null) {
Expression expression = parser.parseExpression(indexName, ParserContext.TEMPLATE_EXPRESSION);
@ -113,8 +112,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
return getTypeInformation().getType().getSimpleName();
}
@Override
public String getIndexType() {
private String getIndexType() {
if (indexType != null) {
Expression expression = parser.parseExpression(indexType, ParserContext.TEMPLATE_EXPRESSION);
@ -124,6 +122,11 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
return "";
}
@Override
public IndexCoordinates getIndexCoordinates() {
return IndexCoordinates.of(getIndexName()).withTypes(getIndexType());
}
@Override
public String getIndexStoreType() {
return indexStoreType;

View File

@ -19,6 +19,7 @@ import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.repository.NoRepositoryBean;
/**
@ -46,7 +47,7 @@ public interface ElasticsearchRepository<T, ID> extends ElasticsearchCrudReposit
Page<T> search(QueryBuilder query, Pageable pageable);
Page<T> search(NativeSearchQuery searchQuery);
Page<T> search(Query searchQuery);
Page<T> searchSimilar(T entity, String[] fields, Pageable pageable);

View File

@ -20,7 +20,7 @@ import reactor.core.publisher.Mono;
import org.reactivestreams.Publisher;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;

View File

@ -17,7 +17,7 @@ package org.springframework.data.elasticsearch.repository.query;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.repository.query.parser.ElasticsearchQueryCreator;

View File

@ -20,7 +20,7 @@ import java.util.regex.Pattern;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.convert.DateTimeConverters;
import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.data.repository.query.ParametersParameterAccessor;

View File

@ -16,7 +16,7 @@
package org.springframework.data.elasticsearch.repository.query;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.repository.query.ResultProcessor;

View File

@ -38,12 +38,12 @@ public class SimpleElasticsearchEntityMetadata<T> implements ElasticsearchEntity
@Override
public String getIndexName() {
return entity.getIndexName();
return entity.getIndexCoordinates().getIndexName();
}
@Override
public String getIndexTypeName() {
return entity.getIndexType();
return entity.getIndexCoordinates().getTypeName();
}
@Override

View File

@ -35,14 +35,19 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.DocumentOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.SearchOperations;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.util.Streamable;
import org.springframework.util.Assert;
@ -66,21 +71,23 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
static final Logger LOGGER = LoggerFactory.getLogger(AbstractElasticsearchRepository.class);
protected ElasticsearchOperations elasticsearchOperations;
protected ElasticsearchOperations operations;
protected IndexOperations indexOperations;
protected Class<T> entityClass;
protected ElasticsearchEntityInformation<T, ID> entityInformation;
public AbstractElasticsearchRepository() {}
public AbstractElasticsearchRepository(ElasticsearchOperations elasticsearchOperations) {
Assert.notNull(elasticsearchOperations, "ElasticsearchOperations must not be null!");
this.setElasticsearchOperations(elasticsearchOperations);
public AbstractElasticsearchRepository(ElasticsearchOperations operations) {
Assert.notNull(operations, "ElasticsearchOperations must not be null.");
this.operations = operations;
this.indexOperations = operations.getIndexOperations();
}
public AbstractElasticsearchRepository(ElasticsearchEntityInformation<T, ID> metadata,
ElasticsearchOperations elasticsearchOperations) {
this(elasticsearchOperations);
ElasticsearchOperations operations) {
this(operations);
Assert.notNull(metadata, "ElasticsearchEntityInformation must not be null!");
@ -97,22 +104,25 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
}
private void createIndex() {
elasticsearchOperations.createIndex(getEntityClass());
indexOperations.createIndex(getEntityClass());
}
private void putMapping() {
elasticsearchOperations.putMapping(getEntityClass());
indexOperations.putMapping(getEntityClass());
}
private boolean createIndexAndMapping() {
return elasticsearchOperations.getPersistentEntityFor(getEntityClass()).isCreateIndexAndMapping();
final ElasticsearchPersistentEntity<?> entity = operations.getElasticsearchConverter()
.getMappingContext().getRequiredPersistentEntity(getEntityClass());
return entity.isCreateIndexAndMapping();
}
@Override
public Optional<T> findById(ID id) {
GetQuery query = new GetQuery();
query.setId(stringIdRepresentation(id));
return Optional.ofNullable(elasticsearchOperations.get(query, getEntityClass(), getIndexCoordinates()));
return Optional.ofNullable(operations.get(query, getEntityClass(), getIndexCoordinates()));
}
@Override
@ -128,7 +138,7 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
@Override
public Page<T> findAll(Pageable pageable) {
NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withPageable(pageable).build();
return elasticsearchOperations.queryForPage(query, getEntityClass(), getIndexCoordinates());
return operations.queryForPage(query, getEntityClass(), getIndexCoordinates());
}
@Override
@ -140,27 +150,27 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
}
NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withPageable(PageRequest.of(0, itemCount, sort)).build();
return elasticsearchOperations.queryForPage(query, getEntityClass(), getIndexCoordinates());
return operations.queryForPage(query, getEntityClass(), getIndexCoordinates());
}
@Override
public Iterable<T> findAllById(Iterable<ID> ids) {
Assert.notNull(ids, "ids can't be null.");
NativeSearchQuery query = new NativeSearchQueryBuilder().withIds(stringIdsRepresentation(ids)).build();
return elasticsearchOperations.multiGet(query, getEntityClass(), getIndexCoordinates());
return operations.multiGet(query, getEntityClass(), getIndexCoordinates());
}
@Override
public long count() {
NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
return elasticsearchOperations.count(query, getEntityClass(), getIndexCoordinates());
return operations.count(query, getEntityClass(), getIndexCoordinates());
}
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Cannot save 'null' entity.");
elasticsearchOperations.index(createIndexQuery(entity), getIndexCoordinates());
elasticsearchOperations.refresh(getIndexCoordinates());
operations.index(createIndexQuery(entity), getIndexCoordinates());
indexOperations.refresh(getIndexCoordinates());
return entity;
}
@ -177,7 +187,7 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
@Override
public <S extends T> S indexWithoutRefresh(S entity) {
Assert.notNull(entity, "Cannot save 'null' entity.");
elasticsearchOperations.index(createIndexQuery(entity), getIndexCoordinates());
operations.index(createIndexQuery(entity), getIndexCoordinates());
return entity;
}
@ -188,8 +198,8 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
.collect(Collectors.toList());
if (!queries.isEmpty()) {
elasticsearchOperations.bulkIndex(queries, getIndexCoordinates());
elasticsearchOperations.refresh(getIndexCoordinates());
operations.bulkIndex(queries, getIndexCoordinates());
indexOperations.refresh(getIndexCoordinates());
}
return entities;
@ -203,24 +213,24 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
@Override
public Iterable<T> search(QueryBuilder query) {
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(query).build();
int count = (int) elasticsearchOperations.count(searchQuery, getEntityClass(), getIndexCoordinates());
int count = (int) operations.count(searchQuery, getEntityClass(), getIndexCoordinates());
if (count == 0) {
return new PageImpl<>(Collections.<T> emptyList());
}
searchQuery.setPageable(PageRequest.of(0, count));
return elasticsearchOperations.queryForPage(searchQuery, getEntityClass(), getIndexCoordinates());
return operations.queryForPage(searchQuery, getEntityClass(), getIndexCoordinates());
}
@Override
public Page<T> search(QueryBuilder query, Pageable pageable) {
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(query).withPageable(pageable).build();
return elasticsearchOperations.queryForPage(searchQuery, getEntityClass(), getIndexCoordinates());
return operations.queryForPage(searchQuery, getEntityClass(), getIndexCoordinates());
}
@Override
public Page<T> search(NativeSearchQuery query) {
return elasticsearchOperations.queryForPage(query, getEntityClass(), getIndexCoordinates());
public Page<T> search(Query query) {
return operations.queryForPage(query, getEntityClass(), getIndexCoordinates());
}
@Override
@ -235,22 +245,22 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
query.addFields(fields);
}
return elasticsearchOperations.moreLikeThis(query, getEntityClass(), getIndexCoordinates());
return operations.moreLikeThis(query, getEntityClass(), getIndexCoordinates());
}
@Override
public void deleteById(ID id) {
Assert.notNull(id, "Cannot delete entity with id 'null'.");
IndexCoordinates indexCoordinates = getIndexCoordinates();
elasticsearchOperations.delete(stringIdRepresentation(id), indexCoordinates);
elasticsearchOperations.refresh(indexCoordinates);
operations.delete(stringIdRepresentation(id), indexCoordinates);
indexOperations.refresh(indexCoordinates);
}
@Override
public void delete(T entity) {
Assert.notNull(entity, "Cannot delete 'null' entity.");
deleteById(extractIdFromBean(entity));
elasticsearchOperations.refresh(getIndexCoordinates());
indexOperations.refresh(getIndexCoordinates());
}
@Override
@ -266,13 +276,13 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
DeleteQuery deleteQuery = new DeleteQuery();
deleteQuery.setQuery(matchAllQuery());
IndexCoordinates indexCoordinates = getIndexCoordinates();
elasticsearchOperations.delete(deleteQuery, indexCoordinates);
elasticsearchOperations.refresh(indexCoordinates);
operations.delete(deleteQuery, indexCoordinates);
indexOperations.refresh(indexCoordinates);
}
@Override
public void refresh() {
elasticsearchOperations.refresh(getEntityClass());
indexOperations.refresh(getEntityClass());
}
private IndexQuery createIndexQuery(T entity) {
@ -326,11 +336,6 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
this.entityClass = entityClass;
}
public final void setElasticsearchOperations(ElasticsearchOperations elasticsearchOperations) {
Assert.notNull(elasticsearchOperations, "ElasticsearchOperations must not be null.");
this.elasticsearchOperations = elasticsearchOperations;
}
protected ID extractIdFromBean(T entity) {
return entityInformation.getId(entity);
}
@ -356,6 +361,6 @@ public abstract class AbstractElasticsearchRepository<T, ID> implements Elastics
}
private IndexCoordinates getIndexCoordinates() {
return elasticsearchOperations.getIndexCoordinatesFor(getEntityClass());
return operations.getIndexCoordinatesFor(getEntityClass());
}
}

View File

@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.repository.support;
import org.elasticsearch.index.VersionType;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.repository.core.EntityInformation;
/**
@ -25,14 +26,13 @@ import org.springframework.data.repository.core.EntityInformation;
* @author Mohsin Husen
* @author Christoph Strobl
* @author Ivan Greene
* @author Peter-Josef Meisch
*/
public interface ElasticsearchEntityInformation<T, ID> extends EntityInformation<T, ID> {
String getIdAttribute();
String getIndexName();
String getType();
IndexCoordinates getIndexCoordinates();
Long getVersion(T entity);

View File

@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.repository.support;
import org.elasticsearch.index.VersionType;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.repository.core.support.PersistentEntityInformation;
@ -39,21 +40,19 @@ public class MappingElasticsearchEntityInformation<T, ID> extends PersistentEnti
implements ElasticsearchEntityInformation<T, ID> {
private final ElasticsearchPersistentEntity<T> entityMetadata;
private final String indexName;
private final String type;
private final IndexCoordinates indexCoordinates;
private final VersionType versionType;
public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity<T> entity) {
this(entity, entity.getIndexName(), entity.getIndexType(), entity.getVersionType());
this(entity, entity.getIndexCoordinates(), entity.getVersionType());
}
public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity<T> entity, String indexName, String type,
VersionType versionType) {
public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity<T> entity,
IndexCoordinates indexCoordinates, VersionType versionType) {
super(entity);
this.entityMetadata = entity;
this.indexName = indexName;
this.type = type;
this.indexCoordinates = indexCoordinates;
this.versionType = versionType;
}
@ -62,14 +61,10 @@ public class MappingElasticsearchEntityInformation<T, ID> extends PersistentEnti
return entityMetadata.getRequiredIdProperty().getFieldName();
}
@Override
public String getIndexName() {
return indexName != null ? indexName : entityMetadata.getIndexName();
}
@Override
public String getType() {
return type != null ? type : entityMetadata.getIndexType();
public IndexCoordinates getIndexCoordinates() {
return indexCoordinates;
}
@Override

View File

@ -115,9 +115,8 @@ public class ReactiveElasticsearchRepositoryFactory extends ReactiveRepositoryFa
@Nullable RepositoryInformation information) {
ElasticsearchPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(domainClass);
return new MappingElasticsearchEntityInformation<>((ElasticsearchPersistentEntity<T>) entity, entity.getIndexName(),
entity.getIndexType(), entity.getVersionType());
return new MappingElasticsearchEntityInformation<>((ElasticsearchPersistentEntity<T>) entity,
entity.getIndexCoordinates(), entity.getVersionType());
}
/**

View File

@ -20,7 +20,6 @@ import reactor.core.publisher.Mono;
import org.reactivestreams.Publisher;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
@ -35,7 +34,6 @@ public class SimpleReactiveElasticsearchRepository<T, ID> implements ReactiveEla
private final ElasticsearchEntityInformation<T, ID> entityInformation;
private final ReactiveElasticsearchOperations elasticsearchOperations;
private final IndexCoordinates index;
public SimpleReactiveElasticsearchRepository(ElasticsearchEntityInformation<T, ID> entityInformation,
ReactiveElasticsearchOperations elasticsearchOperations) {
@ -45,20 +43,19 @@ public class SimpleReactiveElasticsearchRepository<T, ID> implements ReactiveEla
this.entityInformation = entityInformation;
this.elasticsearchOperations = elasticsearchOperations;
this.index = IndexCoordinates.of(entityInformation.getIndexName()).withTypes(entityInformation.getType());
}
@Override
public Flux<T> findAll(Sort sort) {
return elasticsearchOperations.find(Query.findAll().addSort(sort), entityInformation.getJavaType(), index);
return elasticsearchOperations.find(Query.findAll().addSort(sort), entityInformation.getJavaType(), entityInformation.getIndexCoordinates());
}
@Override
public <S extends T> Mono<S> save(S entity) {
Assert.notNull(entity, "Entity must not be null!");
return elasticsearchOperations.save(entity, index);
return elasticsearchOperations.save(entity, entityInformation.getIndexCoordinates());
}
@Override
@ -79,7 +76,7 @@ public class SimpleReactiveElasticsearchRepository<T, ID> implements ReactiveEla
public Mono<T> findById(ID id) {
Assert.notNull(id, "Id must not be null!");
return elasticsearchOperations.findById(convertId(id), entityInformation.getJavaType(), index);
return elasticsearchOperations.findById(convertId(id), entityInformation.getJavaType(), entityInformation.getIndexCoordinates());
}
@Override
@ -93,7 +90,7 @@ public class SimpleReactiveElasticsearchRepository<T, ID> implements ReactiveEla
public Mono<Boolean> existsById(ID id) {
Assert.notNull(id, "Id must not be null!");
return elasticsearchOperations.exists(convertId(id), entityInformation.getJavaType(), index);
return elasticsearchOperations.exists(convertId(id), entityInformation.getJavaType(), entityInformation.getIndexCoordinates());
}
@Override
@ -106,7 +103,7 @@ public class SimpleReactiveElasticsearchRepository<T, ID> implements ReactiveEla
@Override
public Flux<T> findAll() {
return elasticsearchOperations.find(Query.findAll(), entityInformation.getJavaType(), index);
return elasticsearchOperations.find(Query.findAll(), entityInformation.getJavaType(), entityInformation.getIndexCoordinates());
}
@Override
@ -127,14 +124,14 @@ public class SimpleReactiveElasticsearchRepository<T, ID> implements ReactiveEla
@Override
public Mono<Long> count() {
return elasticsearchOperations.count(Query.findAll(), entityInformation.getJavaType(), index);
return elasticsearchOperations.count(Query.findAll(), entityInformation.getJavaType(), entityInformation.getIndexCoordinates());
}
@Override
public Mono<Void> deleteById(ID id) {
Assert.notNull(id, "Id must not be null!");
return elasticsearchOperations.deleteById(convertId(id), entityInformation.getJavaType(), index) //
return elasticsearchOperations.deleteById(convertId(id), entityInformation.getJavaType(), entityInformation.getIndexCoordinates()) //
.then();
}
@ -149,7 +146,7 @@ public class SimpleReactiveElasticsearchRepository<T, ID> implements ReactiveEla
public Mono<Void> delete(T entity) {
Assert.notNull(entity, "Entity must not be null!");
return elasticsearchOperations.delete(entity, index) //
return elasticsearchOperations.delete(entity, entityInformation.getIndexCoordinates()) //
.then();
}
@ -170,7 +167,7 @@ public class SimpleReactiveElasticsearchRepository<T, ID> implements ReactiveEla
@Override
public Mono<Void> deleteAll() {
return elasticsearchOperations.deleteBy(Query.findAll(), entityInformation.getJavaType(), index) //
return elasticsearchOperations.deleteBy(Query.findAll(), entityInformation.getJavaType(), entityInformation.getIndexCoordinates()) //
.then();
}

View File

@ -45,7 +45,7 @@ import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.InnerField;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.GetQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;

View File

@ -61,7 +61,7 @@ public class ElasticsearchRestTemplateTests extends ElasticsearchTemplateTests {
indexRequest.source("{}", XContentType.JSON);
UpdateQuery updateQuery = new UpdateQueryBuilder().withId(randomNumeric(5)).withIndexRequest(indexRequest).build();
assertThatThrownBy(() -> {
elasticsearchTemplate.update(updateQuery, index);
operations.update(updateQuery, index);
}).isInstanceOf(ElasticsearchStatusException.class);
}

View File

@ -34,6 +34,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
@ -59,7 +60,7 @@ public class ElasticsearchTransportTemplateTests extends ElasticsearchTemplateTe
indexRequest.source("{}", XContentType.JSON);
UpdateQuery updateQuery = new UpdateQueryBuilder().withId(randomNumeric(5)).withIndexRequest(indexRequest).build();
assertThatThrownBy(() -> {
elasticsearchTemplate.update(updateQuery, index);
operations.update(updateQuery, index);
}).isInstanceOf(DocumentMissingException.class);
}

View File

@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.core;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
/**
* @author Peter-Josef Meisch

View File

@ -36,6 +36,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;

View File

@ -24,6 +24,7 @@ import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

View File

@ -24,6 +24,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

View File

@ -42,7 +42,7 @@ import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.InnerField;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.ResultsExtractor;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;

View File

@ -26,6 +26,7 @@ import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.SuggestionBuilder;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
@ -35,7 +36,8 @@ import org.springframework.data.elasticsearch.annotations.CompletionField;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
@ -60,9 +62,17 @@ public class ElasticsearchTemplateCompletionTests {
@Autowired private ElasticsearchOperations operations;
private void loadCompletionObjectEntities() {
IndexOperations indexOperations;
IndexInitializer.init(operations, CompletionEntity.class);
@BeforeEach
private void setup() {
indexOperations = operations.getIndexOperations();
IndexInitializer.init(indexOperations, CompletionEntity.class);
IndexInitializer.init(indexOperations, AnnotatedCompletionEntity.class);
}
private void loadCompletionObjectEntities() {
List<IndexQuery> indexQueries = new ArrayList<>();
indexQueries.add(
@ -80,8 +90,6 @@ public class ElasticsearchTemplateCompletionTests {
private void loadAnnotatedCompletionObjectEntities() {
IndexInitializer.init(operations, AnnotatedCompletionEntity.class);
NonDocumentEntity nonDocumentEntity = new NonDocumentEntity();
nonDocumentEntity.setSomeField1("foo");
nonDocumentEntity.setSomeField2("bar");
@ -103,8 +111,6 @@ public class ElasticsearchTemplateCompletionTests {
private void loadAnnotatedCompletionObjectEntitiesWithWeights() {
IndexInitializer.init(operations, AnnotatedCompletionEntity.class);
List<IndexQuery> indexQueries = new ArrayList<>();
indexQueries.add(new AnnotatedCompletionEntityBuilder("1").name("Mewes Kochheim1")
.suggest(new String[] { "Mewes Kochheim1" }, 4).buildIndex());
@ -120,17 +126,6 @@ public class ElasticsearchTemplateCompletionTests {
operations.refresh(AnnotatedCompletionEntity.class);
}
@Test
public void shouldPutMappingForGivenEntity() throws Exception {
// given
Class entity = CompletionEntity.class;
operations.createIndex(entity);
// when
assertThat(operations.putMapping(entity)).isTrue();
}
@Test
public void shouldFindSuggestionsForGivenCriteriaQueryUsingCompletionEntity() {
@ -164,7 +159,7 @@ public class ElasticsearchTemplateCompletionTests {
// when
SearchResponse suggestResponse = ((AbstractElasticsearchTemplate) operations).suggest(
new SuggestBuilder().addSuggestion("test-suggest", completionSuggestionFuzzyBuilder),
IndexCoordinates.of("test-index-core-completion").withTypes("completion-type"));
IndexCoordinates.of("test-index-annotated-completion").withTypes("annotated-completion-type"));
CompletionSuggestion completionSuggestion = suggestResponse.getSuggest().getSuggestion("test-suggest");
List<CompletionSuggestion.Entry.Option> options = completionSuggestion.getEntries().get(0).getOptions();

View File

@ -33,6 +33,7 @@ import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.elasticsearch.search.suggest.completion.context.CategoryQueryContext;
import org.elasticsearch.search.suggest.completion.context.ContextMapping;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
@ -43,7 +44,8 @@ import org.springframework.data.elasticsearch.annotations.CompletionField;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
@ -64,9 +66,18 @@ public class ElasticsearchTemplateCompletionWithContextsTests {
@Autowired private ElasticsearchOperations operations;
private IndexOperations indexOperations;
@BeforeEach
void setup() {
indexOperations = operations.getIndexOperations();
indexOperations.deleteIndex(ContextCompletionEntity.class);
}
private void loadContextCompletionObjectEntities() {
IndexInitializer.init(operations, ContextCompletionEntity.class);
IndexInitializer.init(indexOperations, ContextCompletionEntity.class);
NonDocumentEntity nonDocumentEntity = new NonDocumentEntity();
nonDocumentEntity.setSomeField1("foo");
@ -99,17 +110,6 @@ public class ElasticsearchTemplateCompletionWithContextsTests {
operations.refresh(ContextCompletionEntity.class);
}
@Test
public void shouldPutMappingForGivenEntity() throws Exception {
// given
Class<?> entity = ContextCompletionEntity.class;
operations.createIndex(entity);
// when
assertThat(operations.putMapping(entity)).isTrue();
}
@Test // DATAES-536
public void shouldFindSuggestionsForGivenCriteriaQueryUsingContextCompletionEntityOfMongo() {

View File

@ -38,7 +38,8 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.GeoPointField;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
@ -75,11 +76,15 @@ public class ElasticsearchTemplateGeoTests {
@Autowired private ElasticsearchOperations operations;
private IndexOperations indexOperations;
@BeforeEach
public void before() {
IndexInitializer.init(operations, AuthorMarkerEntity.class);
IndexInitializer.init(operations, LocationMarkerEntity.class);
indexOperations = operations.getIndexOperations();
IndexInitializer.init(indexOperations, AuthorMarkerEntity.class);
IndexInitializer.init(indexOperations, LocationMarkerEntity.class);
}
private void loadClassBaseEntities() {
@ -125,17 +130,6 @@ public class ElasticsearchTemplateGeoTests {
operations.refresh(LocationMarkerEntity.class);
}
@Test
public void shouldPutMappingForGivenEntityWithGeoLocation() throws Exception {
// given
Class<?> entity = AuthorMarkerEntity.class;
operations.createIndex(entity);
// when
assertThat(operations.putMapping(entity)).isTrue();
}
@Test
public void shouldFindAuthorMarkersInRangeForGivenCriteriaQuery() {

View File

@ -51,7 +51,7 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.completion.Completion;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.query.IndexQuery;

View File

@ -42,7 +42,7 @@ import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.Score;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.test.context.ContextConfiguration;

View File

@ -27,6 +27,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
@ -57,15 +58,18 @@ public class DynamicIndexEntityTests {
@Autowired private DynamicIndexRepository repository;
@Autowired private ElasticsearchOperations operations;
private IndexOperations indexOperations;
@Autowired private IndexNameProvider indexNameProvider;
@BeforeEach
public void init() {
indexOperations = operations.getIndexOperations();
deleteIndexes();
operations.createIndex("index1");
operations.createIndex("index2");
indexOperations.createIndex("index1");
indexOperations.createIndex("index2");
}
@AfterEach
@ -75,8 +79,8 @@ public class DynamicIndexEntityTests {
private void deleteIndexes() {
operations.deleteIndex("index1");
operations.deleteIndex("index2");
indexOperations.deleteIndex("index1");
indexOperations.deleteIndex("index2");
}
@Test // DATAES-456

View File

@ -1,78 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.repositories.nondocument;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Peter-Josef Meisch
*/
public class NonDocumentEntityTests {
@Test
public void shouldNotInitialiseRepositoryWithNonDocument() {
// when
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/repository-non-document-entity.xml");
assertThatThrownBy(() -> {
ctx.getBean(NonDocumentEntityRepository.class);
}).isInstanceOf(BeanCreationException.class);
}
/**
* @author Rizwan Idrees
* @author Mohsin Husen
*/
static class NonDocumentEntity {
@Id private String someId;
private String someField1;
private String someField2;
public String getSomeField1() {
return someField1;
}
public void setSomeField1(String someField1) {
this.someField1 = someField1;
}
public String getSomeField2() {
return someField2;
}
public void setSomeField2(String someField2) {
this.someField2 = someField2;
}
}
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Oliver Gierke
*/
@Lazy
interface NonDocumentEntityRepository extends ElasticsearchRepository<NonDocumentEntity, String> {}
}

View File

@ -32,7 +32,7 @@ import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;

View File

@ -26,7 +26,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;

View File

@ -32,7 +32,7 @@ import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;

View File

@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.utils;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexOperations;
/**
* Utility to initialize indexes.
@ -31,9 +32,24 @@ public class IndexInitializer {
*
* @param operations
* @param clazz
* @deprecated since 4.0, use {@link IndexInitializer#init(IndexOperations, Class)}
*/
public static void init(ElasticsearchOperations operations, Class<?> clazz) {
operations.getIndexOperations().deleteIndex(clazz);
operations.getIndexOperations().createIndex(clazz);
operations.getIndexOperations().putMapping(clazz);
operations.getIndexOperations().refresh(clazz);
}
/**
* Initialize a fresh index with mappings for {@link Class}. Drops the index if it exists before creation.
*
* @param operations
* @param clazz
*/
public static void init(IndexOperations operations, Class<?> clazz) {
operations.deleteIndex(clazz);
operations.createIndex(clazz);
operations.putMapping(clazz);

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="http://www.springframework.org/schema/data/elasticsearch https://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<elasticsearch:node-client id="client" local="true" cluster-name="#{T(java.util.UUID).randomUUID().toString()}"
http-enabled="true" path-data="target/elasticsearchTestData" path-home="src/test/resources/test-home-dir"
path-configuration="node-client-configuration.yml"/>
<bean name="elasticsearchTemplate"
class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
<constructor-arg name="client" ref="client"/>
</bean>
<elasticsearch:repositories
base-package="org.springframework.data.elasticsearch.repositories.nondocument"
consider-nested-repositories="true"/>
</beans>