mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-29 07:12:26 +00:00
parent
ff381c63b6
commit
11a6430a90
@ -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
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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() {
|
||||
|
||||
|
@ -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() {
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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> {}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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>
|
Loading…
x
Reference in New Issue
Block a user