From 7dba0441bb96495fa58f6dd7a82ba9ad93d9aaf4 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 30 Jun 2017 11:42:38 +0200 Subject: [PATCH] DATAES-369 - Polishing. Fix linebreaks. --- .../core/ElasticsearchTemplate.java | 2426 ++++++++--------- .../ElasticsearchPersistentEntity.java | 104 +- .../SimpleElasticsearchPersistentEntity.java | 342 +-- ...MappingElasticsearchEntityInformation.java | 188 +- 4 files changed, 1530 insertions(+), 1530 deletions(-) diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java index aa07828c0..74702a998 100755 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -1,1213 +1,1213 @@ -/* - * Copyright 2013-2017 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.elasticsearch.core; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import org.elasticsearch.action.ListenableActionFuture; -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.BulkItemResponse; -import org.elasticsearch.action.bulk.BulkRequestBuilder; -import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.action.get.GetResponse; -import org.elasticsearch.action.get.MultiGetRequest; -import org.elasticsearch.action.get.MultiGetRequestBuilder; -import org.elasticsearch.action.get.MultiGetResponse; -import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.search.SearchRequestBuilder; -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.client.Requests; -import org.elasticsearch.cluster.metadata.AliasMetaData; -import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.collect.MapBuilder; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.index.query.MoreLikeThisQueryBuilder; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.search.SearchHit; -import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; -import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; -import org.elasticsearch.search.sort.SortBuilder; -import org.elasticsearch.search.sort.SortOrder; -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.core.io.ClassPathResource; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -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.aggregation.AggregatedPage; -import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl; -import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; -import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; -import org.springframework.data.elasticsearch.core.facet.FacetRequest; -import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; -import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; -import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; -import org.springframework.data.elasticsearch.core.query.*; -import org.springframework.data.util.CloseableIterator; -import org.springframework.util.Assert; -import static org.apache.commons.lang.StringUtils.*; -import static org.elasticsearch.client.Requests.*; -import static org.elasticsearch.index.VersionType.*; -import static org.elasticsearch.index.query.QueryBuilders.*; -import static org.springframework.data.elasticsearch.core.MappingBuilder.*; -import static org.springframework.util.CollectionUtils.isEmpty; - -/** - * ElasticsearchTemplate - * - * @author Rizwan Idrees - * @author Mohsin Husen - * @author Artur Konczak - * @author Kevin Leturc - * @author Mason Chan - * @author Young Gu - * @author Oliver Gierke - * @author Mark Janssen - * @author Mark Paluch - */ -public class ElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware { - - private static final Logger logger = LoggerFactory.getLogger(ElasticsearchTemplate.class); - private Client client; - private ElasticsearchConverter elasticsearchConverter; - private ResultsMapper resultsMapper; - private String searchTimeout; - - public ElasticsearchTemplate(Client client) { - this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext())); - } - - public ElasticsearchTemplate(Client client, EntityMapper entityMapper) { - this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext()), entityMapper); - } - - public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter, - EntityMapper entityMapper) { - this(client, elasticsearchConverter, - new DefaultResultMapper(elasticsearchConverter.getMappingContext(), entityMapper)); - } - - public ElasticsearchTemplate(Client client, ResultsMapper resultsMapper) { - this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext()), resultsMapper); - } - - public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter) { - this(client, elasticsearchConverter, new DefaultResultMapper(elasticsearchConverter.getMappingContext())); - } - - public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter, - ResultsMapper resultsMapper) { - - Assert.notNull(client, "Client must not be null!"); - Assert.notNull(elasticsearchConverter, "ElasticsearchConverter must not be null!"); - Assert.notNull(resultsMapper, "ResultsMapper must not be null!"); - - this.client = client; - this.elasticsearchConverter = elasticsearchConverter; - this.resultsMapper = resultsMapper; - } - - @Override - public Client getClient() { - return client; - } - - public void setSearchTimeout(String searchTimeout) { - this.searchTimeout = searchTimeout; - } - - @Override - public boolean createIndex(Class clazz) { - return createIndexIfNotCreated(clazz); - } - - @Override - public boolean createIndex(String indexName) { - Assert.notNull(indexName, "No index defined for Query"); - return client.admin().indices().create(Requests.createIndexRequest(indexName)).actionGet().isAcknowledged(); - } - - @Override - public boolean putMapping(Class clazz) { - if (clazz.isAnnotationPresent(Mapping.class)) { - String mappingPath = clazz.getAnnotation(Mapping.class).mappingPath(); - if (isNotBlank(mappingPath)) { - String mappings = readFileFromClasspath(mappingPath); - if (isNotBlank(mappings)) { - return putMapping(clazz, mappings); - } - } else { - logger.info("mappingPath in @Mapping has to be defined. Building mappings using @Field"); - } - } - ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); - XContentBuilder xContentBuilder = null; - try { - - ElasticsearchPersistentProperty property = persistentEntity.getRequiredIdProperty(); - - xContentBuilder = buildMapping(clazz, persistentEntity.getIndexType(), - property.getFieldName(), persistentEntity.getParentType()); - } catch (Exception e) { - throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e); - } - return putMapping(clazz, xContentBuilder); - } - - @Override - public boolean putMapping(Class clazz, Object mapping) { - return putMapping(getPersistentEntityFor(clazz).getIndexName(), getPersistentEntityFor(clazz).getIndexType(), - mapping); - } - - @Override - public boolean putMapping(String indexName, String type, Object mapping) { - Assert.notNull(indexName, "No index defined for putMapping()"); - Assert.notNull(type, "No type defined for putMapping()"); - PutMappingRequestBuilder requestBuilder = client.admin().indices().preparePutMapping(indexName).setType(type); - if (mapping instanceof String) { - requestBuilder.setSource(String.valueOf(mapping)); - } else if (mapping instanceof Map) { - requestBuilder.setSource((Map) mapping); - } else if (mapping instanceof XContentBuilder) { - requestBuilder.setSource((XContentBuilder) mapping); - } - return requestBuilder.execute().actionGet().isAcknowledged(); - } - - @Override - public Map getMapping(String indexName, String type) { - Assert.notNull(indexName, "No index defined for putMapping()"); - Assert.notNull(type, "No type defined for putMapping()"); - Map mappings = null; - try { - mappings = client.admin().indices().getMappings(new GetMappingsRequest().indices(indexName).types(type)) - .actionGet().getMappings().get(indexName).get(type).getSourceAsMap(); - } catch (Exception e) { - throw new ElasticsearchException( - "Error while getting mapping for indexName : " + indexName + " type : " + type + " " + e.getMessage()); - } - return mappings; - } - - @Override - public Map getMapping(Class clazz) { - return getMapping(getPersistentEntityFor(clazz).getIndexName(), getPersistentEntityFor(clazz).getIndexType()); - } - - @Override - public ElasticsearchConverter getElasticsearchConverter() { - return elasticsearchConverter; - } - - @Override - public T queryForObject(GetQuery query, Class clazz) { - return queryForObject(query, clazz, resultsMapper); - } - - @Override - public T queryForObject(GetQuery query, Class clazz, GetResultMapper mapper) { - ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); - GetResponse response = client - .prepareGet(persistentEntity.getIndexName(), persistentEntity.getIndexType(), query.getId()).execute() - .actionGet(); - - T entity = mapper.mapResult(response, clazz); - return entity; - } - - @Override - public T queryForObject(CriteriaQuery query, Class clazz) { - Page page = queryForPage(query, clazz); - Assert.isTrue(page.getTotalElements() < 2, "Expected 1 but found " + page.getTotalElements() + " results"); - return page.getTotalElements() > 0 ? page.getContent().get(0) : null; - } - - @Override - public T queryForObject(StringQuery query, Class clazz) { - Page page = queryForPage(query, clazz); - Assert.isTrue(page.getTotalElements() < 2, "Expected 1 but found " + page.getTotalElements() + " results"); - return page.getTotalElements() > 0 ? page.getContent().get(0) : null; - } - - @Override - public AggregatedPage queryForPage(SearchQuery query, Class clazz) { - return queryForPage(query, clazz, resultsMapper); - } - - @Override - public AggregatedPage queryForPage(SearchQuery query, Class clazz, SearchResultMapper mapper) { - SearchResponse response = doSearch(prepareSearch(query, clazz), query); - return mapper.mapResults(response, clazz, query.getPageable()); - } - - @Override - public T query(SearchQuery query, ResultsExtractor resultsExtractor) { - SearchResponse response = doSearch(prepareSearch(query), query); - return resultsExtractor.extract(response); - } - - @Override - public List queryForList(CriteriaQuery query, Class clazz) { - return queryForPage(query, clazz).getContent(); - } - - @Override - public List queryForList(StringQuery query, Class clazz) { - return queryForPage(query, clazz).getContent(); - } - - @Override - public List queryForList(SearchQuery query, Class clazz) { - return queryForPage(query, clazz).getContent(); - } - - @Override - public List queryForIds(SearchQuery query) { - SearchRequestBuilder request = prepareSearch(query).setQuery(query.getQuery()); - if (query.getFilter() != null) { - request.setPostFilter(query.getFilter()); - } - SearchResponse response = getSearchResponse(request.execute()); - return extractIds(response); - } - - @Override - public Page queryForPage(CriteriaQuery criteriaQuery, Class clazz) { - QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria()); - QueryBuilder elasticsearchFilter = new CriteriaFilterProcessor() - .createFilterFromCriteria(criteriaQuery.getCriteria()); - SearchRequestBuilder searchRequestBuilder = prepareSearch(criteriaQuery, clazz); - - if (elasticsearchQuery != null) { - searchRequestBuilder.setQuery(elasticsearchQuery); - } else { - searchRequestBuilder.setQuery(QueryBuilders.matchAllQuery()); - } - - if (criteriaQuery.getMinScore() > 0) { - searchRequestBuilder.setMinScore(criteriaQuery.getMinScore()); - } - - if (elasticsearchFilter != null) - searchRequestBuilder.setPostFilter(elasticsearchFilter); - if (logger.isDebugEnabled()) { - logger.debug("doSearch query:\n" + searchRequestBuilder.toString()); - } - - SearchResponse response = getSearchResponse(searchRequestBuilder.execute()); - return resultsMapper.mapResults(response, clazz, criteriaQuery.getPageable()); - } - - @Override - public Page queryForPage(StringQuery query, Class clazz) { - return queryForPage(query, clazz, resultsMapper); - } - - @Override - public Page queryForPage(StringQuery query, Class clazz, SearchResultMapper mapper) { - SearchResponse response = getSearchResponse(prepareSearch(query, clazz).setQuery(wrapperQuery(query.getSource())).execute()); - return mapper.mapResults(response, clazz, query.getPageable()); - } - - @Override - public CloseableIterator stream(CriteriaQuery query, Class clazz) { - final long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis(); - return doStream(scrollTimeInMillis, (ScrolledPage) startScroll(scrollTimeInMillis, query, clazz), clazz, resultsMapper); - } - - @Override - public CloseableIterator stream(SearchQuery query, Class clazz) { - return stream(query, clazz, resultsMapper); - } - - @Override - public CloseableIterator stream(SearchQuery query, final Class clazz, final SearchResultMapper mapper) { - final long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis(); - return doStream(scrollTimeInMillis, (ScrolledPage) startScroll(scrollTimeInMillis, query, clazz, mapper), clazz, mapper); - } - - private CloseableIterator doStream(final long scrollTimeInMillis, final ScrolledPage page, final Class clazz, final SearchResultMapper mapper) { - return new CloseableIterator() { - - /** As we couldn't retrieve single result with scroll, store current hits. */ - private volatile Iterator currentHits = page.iterator(); - - /** The scroll id. */ - private volatile String scrollId = page.getScrollId(); - - /** If stream is finished (ie: cluster returns no results. */ - private volatile boolean finished = !currentHits.hasNext(); - - @Override - public void close() { - try { - // Clear scroll on cluster only in case of error (cause elasticsearch auto clear scroll when it's done) - if (!finished && scrollId != null && currentHits != null && currentHits.hasNext()) { - clearScroll(scrollId); - } - } finally { - currentHits = null; - scrollId = null; - } - } - - @Override - public boolean hasNext() { - // Test if stream is finished - if (finished) { - return false; - } - // Test if it remains hits - if (currentHits == null || !currentHits.hasNext()) { - // Do a new request - final ScrolledPage scroll = (ScrolledPage) continueScroll(scrollId, scrollTimeInMillis, clazz, mapper); - // Save hits and scroll id - currentHits = scroll.iterator(); - finished = !currentHits.hasNext(); - scrollId = scroll.getScrollId(); - } - return currentHits.hasNext(); - } - - @Override - public T next() { - if (hasNext()) { - return currentHits.next(); - } - throw new NoSuchElementException(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("remove"); - } - }; - } - - @Override - public long count(CriteriaQuery criteriaQuery, Class clazz) { - QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria()); - QueryBuilder elasticsearchFilter = new CriteriaFilterProcessor() - .createFilterFromCriteria(criteriaQuery.getCriteria()); - - if (elasticsearchFilter == null) { - return doCount(prepareCount(criteriaQuery, clazz), elasticsearchQuery); - } else { - // filter could not be set into CountRequestBuilder, convert request into search request - return doCount(prepareSearch(criteriaQuery, clazz), elasticsearchQuery, elasticsearchFilter); - } - } - - @Override - public long count(SearchQuery searchQuery, Class clazz) { - QueryBuilder elasticsearchQuery = searchQuery.getQuery(); - QueryBuilder elasticsearchFilter = searchQuery.getFilter(); - - if (elasticsearchFilter == null) { - return doCount(prepareCount(searchQuery, clazz), elasticsearchQuery); - } else { - // filter could not be set into CountRequestBuilder, convert request into search request - return doCount(prepareSearch(searchQuery, clazz), elasticsearchQuery, elasticsearchFilter); - } - } - - @Override - public long count(CriteriaQuery query) { - return count(query, null); - } - - @Override - public long count(SearchQuery query) { - return count(query, null); - } - - private long doCount(SearchRequestBuilder countRequestBuilder, QueryBuilder elasticsearchQuery) { - - if (elasticsearchQuery != null) { - countRequestBuilder.setQuery(elasticsearchQuery); - } - return countRequestBuilder.execute().actionGet().getHits().getTotalHits(); - } - - private long doCount(SearchRequestBuilder searchRequestBuilder, QueryBuilder elasticsearchQuery, - QueryBuilder elasticsearchFilter) { - if (elasticsearchQuery != null) { - searchRequestBuilder.setQuery(elasticsearchQuery); - } else { - searchRequestBuilder.setQuery(QueryBuilders.matchAllQuery()); - } - if (elasticsearchFilter != null) { - searchRequestBuilder.setPostFilter(elasticsearchFilter); - } - return searchRequestBuilder.execute().actionGet().getHits().getTotalHits(); - } - - private SearchRequestBuilder prepareCount(Query query, Class clazz) { - String indexName[] = !isEmpty(query.getIndices()) - ? query.getIndices().toArray(new String[query.getIndices().size()]) - : retrieveIndexNameFromPersistentEntity(clazz); - String types[] = !isEmpty(query.getTypes()) ? query.getTypes().toArray(new String[query.getTypes().size()]) - : retrieveTypeFromPersistentEntity(clazz); - - Assert.notNull(indexName, "No index defined for Query"); - - SearchRequestBuilder countRequestBuilder = client.prepareSearch(indexName); - - if (types != null) { - countRequestBuilder.setTypes(types); - } - countRequestBuilder.setSize(0); - return countRequestBuilder; - } - - @Override - public LinkedList multiGet(SearchQuery searchQuery, Class clazz) { - return resultsMapper.mapResults(getMultiResponse(searchQuery, clazz), clazz); - } - - private MultiGetResponse getMultiResponse(Query searchQuery, Class clazz) { - - String indexName = !isEmpty(searchQuery.getIndices()) ? searchQuery.getIndices().get(0) - : getPersistentEntityFor(clazz).getIndexName(); - String type = !isEmpty(searchQuery.getTypes()) ? searchQuery.getTypes().get(0) - : getPersistentEntityFor(clazz).getIndexType(); - - Assert.notNull(indexName, "No index defined for Query"); - Assert.notNull(type, "No type define for Query"); - Assert.notEmpty(searchQuery.getIds(), "No Id define for Query"); - - MultiGetRequestBuilder builder = client.prepareMultiGet(); - - if (searchQuery.getFields() != null && !searchQuery.getFields().isEmpty()) { - searchQuery.addSourceFilter(new FetchSourceFilter(toArray(searchQuery.getFields()), null)); - } - - for (String id : searchQuery.getIds()) { - - MultiGetRequest.Item item = new MultiGetRequest.Item(indexName, type, id); - - if (searchQuery.getRoute() != null) { - item = item.routing(searchQuery.getRoute()); - } - - builder.add(item); - } - return builder.execute().actionGet(); - } - - @Override - public LinkedList multiGet(SearchQuery searchQuery, Class clazz, MultiGetResultMapper getResultMapper) { - return getResultMapper.mapResults(getMultiResponse(searchQuery, clazz), clazz); - } - - @Override - public String index(IndexQuery query) { - String documentId = prepareIndex(query).execute().actionGet().getId(); - // We should call this because we are not going through a mapper. - if (query.getObject() != null) { - setPersistentEntityId(query.getObject(), documentId); - } - return documentId; - } - - @Override - public UpdateResponse update(UpdateQuery query) { - return this.prepareUpdate(query).execute().actionGet(); - } - - private UpdateRequestBuilder prepareUpdate(UpdateQuery query) { - String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName() - : getPersistentEntityFor(query.getClazz()).getIndexName(); - String type = isNotBlank(query.getType()) ? query.getType() - : getPersistentEntityFor(query.getClazz()).getIndexType(); - Assert.notNull(indexName, "No index defined for Query"); - Assert.notNull(type, "No type define for Query"); - Assert.notNull(query.getId(), "No Id define for Query"); - Assert.notNull(query.getUpdateRequest(), "No IndexRequest define for Query"); - UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate(indexName, type, query.getId()); - updateRequestBuilder.setRouting(query.getUpdateRequest().routing()); - - if (query.getUpdateRequest().script() == null) { - // doc - if (query.DoUpsert()) { - updateRequestBuilder.setDocAsUpsert(true).setDoc(query.getUpdateRequest().doc()); - } else { - updateRequestBuilder.setDoc(query.getUpdateRequest().doc()); - } - } else { - // or script - updateRequestBuilder.setScript(query.getUpdateRequest().script()); - } - - return updateRequestBuilder; - } - - @Override - public void bulkIndex(List queries) { - BulkRequestBuilder bulkRequest = client.prepareBulk(); - for (IndexQuery query : queries) { - bulkRequest.add(prepareIndex(query)); - } - checkForBulkUpdateFailure(bulkRequest.execute().actionGet()); - } - - @Override - public void bulkUpdate(List queries) { - BulkRequestBuilder bulkRequest = client.prepareBulk(); - for (UpdateQuery query : queries) { - bulkRequest.add(prepareUpdate(query)); - } - checkForBulkUpdateFailure(bulkRequest.execute().actionGet()); - } - - private void checkForBulkUpdateFailure(BulkResponse bulkResponse) { - if (bulkResponse.hasFailures()) { - Map failedDocuments = new HashMap<>(); - for (BulkItemResponse item : bulkResponse.getItems()) { - if (item.isFailed()) - failedDocuments.put(item.getId(), item.getFailureMessage()); - } - throw new ElasticsearchException( - "Bulk indexing has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages [" - + failedDocuments + "]", - failedDocuments); - } - } - - @Override - public boolean indexExists(Class clazz) { - return indexExists(getPersistentEntityFor(clazz).getIndexName()); - } - - @Override - public boolean indexExists(String indexName) { - return client.admin().indices().exists(indicesExistsRequest(indexName)).actionGet().isExists(); - } - - @Override - public boolean typeExists(String index, String type) { - return client.admin().cluster().prepareState().execute().actionGet().getState().metaData().index(index) - .getMappings().containsKey(type); - } - - @Override - public boolean deleteIndex(Class clazz) { - return deleteIndex(getPersistentEntityFor(clazz).getIndexName()); - } - - @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 indexName, String type, String id) { - return client.prepareDelete(indexName, type, id).execute().actionGet().getId(); - } - - @Override - public String delete(Class clazz, String id) { - ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); - return delete(persistentEntity.getIndexName(), persistentEntity.getIndexType(), id); - } - - @Override - public void delete(DeleteQuery deleteQuery, Class clazz) { - - String indexName = isNotBlank(deleteQuery.getIndex()) ? deleteQuery.getIndex() - : getPersistentEntityFor(clazz).getIndexName(); - String typeName = isNotBlank(deleteQuery.getType()) ? deleteQuery.getType() - : getPersistentEntityFor(clazz).getIndexType(); - Integer pageSize = deleteQuery.getPageSize() != null ? deleteQuery.getPageSize() : 1000; - Long scrollTimeInMillis = deleteQuery.getScrollTimeInMillis() != null ? deleteQuery.getScrollTimeInMillis() - : 10000l; - - SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(deleteQuery.getQuery()).withIndices(indexName) - .withTypes(typeName).withPageable(PageRequest.of(0, pageSize)).build(); - - SearchResultMapper onlyIdResultMapper = new SearchResultMapper() { - @Override - public AggregatedPage mapResults(SearchResponse response, Class clazz, Pageable pageable) { - List result = new ArrayList(); - for (SearchHit searchHit : response.getHits().getHits()) { - String id = searchHit.getId(); - result.add(id); - } - if (result.size() > 0) { - return new AggregatedPageImpl((List) result, response.getScrollId()); - } - return new AggregatedPageImpl(Collections.EMPTY_LIST, response.getScrollId()); - } - }; - - Page scrolledResult = startScroll(scrollTimeInMillis, searchQuery, String.class, onlyIdResultMapper); - BulkRequestBuilder bulkRequestBuilder = client.prepareBulk(); - List ids = new ArrayList(); - - do { - ids.addAll(scrolledResult.getContent()); - scrolledResult = continueScroll(((ScrolledPage)scrolledResult).getScrollId(), scrollTimeInMillis, String.class, onlyIdResultMapper); - } while(scrolledResult.getContent().size() != 0); - - for (String id : ids) { - bulkRequestBuilder.add(client.prepareDelete(indexName, typeName, id)); - } - - if (bulkRequestBuilder.numberOfActions() > 0) { - bulkRequestBuilder.execute().actionGet(); - } - - clearScroll(((ScrolledPage) scrolledResult).getScrollId()); - } - - @Override - public void delete(DeleteQuery deleteQuery) { - Assert.notNull(deleteQuery.getIndex(), "No index defined for Query"); - Assert.notNull(deleteQuery.getType(), "No type define for Query"); - delete(deleteQuery, null); - } - - @Override - public void delete(CriteriaQuery criteriaQuery, Class clazz) { - QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria()); - Assert.notNull(elasticsearchQuery, "Query can not be null."); - DeleteQuery deleteQuery = new DeleteQuery(); - deleteQuery.setQuery(elasticsearchQuery); - delete(deleteQuery, clazz); - } - - private SearchRequestBuilder prepareScroll(Query query, long scrollTimeInMillis, Class clazz) { - setPersistentEntityIndexAndType(query, clazz); - return prepareScroll(query, scrollTimeInMillis); - } - - private SearchRequestBuilder prepareScroll(Query query, long scrollTimeInMillis) { - SearchRequestBuilder requestBuilder = client.prepareSearch(toArray(query.getIndices())) - .setTypes(toArray(query.getTypes())).setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).setFrom(0); - - if(query.getPageable().isPaged()){ - requestBuilder.setSize(query.getPageable().getPageSize()); - } - - if (!isEmpty(query.getFields())) { - requestBuilder.setFetchSource(toArray(query.getFields()), null); - } - return requestBuilder; - } - - private SearchResponse doScroll(SearchRequestBuilder requestBuilder, CriteriaQuery criteriaQuery) { - Assert.notNull(criteriaQuery.getIndices(), "No index defined for Query"); - Assert.notNull(criteriaQuery.getTypes(), "No type define for Query"); - Assert.notNull(criteriaQuery.getPageable(), "Query.pageable is required for scan & scroll"); - - QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria()); - QueryBuilder elasticsearchFilter = new CriteriaFilterProcessor() - .createFilterFromCriteria(criteriaQuery.getCriteria()); - - if (elasticsearchQuery != null) { - requestBuilder.setQuery(elasticsearchQuery); - } else { - requestBuilder.setQuery(QueryBuilders.matchAllQuery()); - } - - if (elasticsearchFilter != null) { - requestBuilder.setPostFilter(elasticsearchFilter); - } - - return getSearchResponse(requestBuilder.execute()); - } - - private SearchResponse doScroll(SearchRequestBuilder requestBuilder, SearchQuery searchQuery) { - Assert.notNull(searchQuery.getIndices(), "No index defined for Query"); - Assert.notNull(searchQuery.getTypes(), "No type define for Query"); - Assert.notNull(searchQuery.getPageable(), "Query.pageable is required for scan & scroll"); - - if (searchQuery.getFilter() != null) { - requestBuilder.setPostFilter(searchQuery.getFilter()); - } - - return getSearchResponse(requestBuilder.setQuery(searchQuery.getQuery()).execute()); - } - - public Page startScroll(long scrollTimeInMillis, SearchQuery searchQuery, Class clazz) { - SearchResponse response = doScroll(prepareScroll(searchQuery, scrollTimeInMillis, clazz), searchQuery); - return resultsMapper.mapResults(response, clazz, null); - } - - public Page startScroll(long scrollTimeInMillis, CriteriaQuery criteriaQuery, Class clazz) { - SearchResponse response = doScroll(prepareScroll(criteriaQuery, scrollTimeInMillis, clazz), criteriaQuery); - return resultsMapper.mapResults(response, clazz, null); - } - - public Page startScroll(long scrollTimeInMillis, SearchQuery searchQuery, Class clazz, SearchResultMapper mapper) { - SearchResponse response = doScroll(prepareScroll(searchQuery, scrollTimeInMillis, clazz), searchQuery); - return mapper.mapResults(response, clazz, null); - } - - public Page startScroll(long scrollTimeInMillis, CriteriaQuery criteriaQuery, Class clazz, SearchResultMapper mapper) { - SearchResponse response = doScroll(prepareScroll(criteriaQuery, scrollTimeInMillis, clazz), criteriaQuery); - return mapper.mapResults(response, clazz, null); - } - - public Page continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class clazz) { - SearchResponse response = getSearchResponse(client.prepareSearchScroll(scrollId) - .setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute()); - return resultsMapper.mapResults(response, clazz, Pageable.unpaged()); - } - - public Page continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class clazz, SearchResultMapper mapper) { - SearchResponse response = getSearchResponse(client.prepareSearchScroll(scrollId) - .setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute()); - return mapper.mapResults(response, clazz, Pageable.unpaged()); - } - - @Override - public void clearScroll(String scrollId) { - client.prepareClearScroll().addScrollId(scrollId).execute().actionGet(); - } - - @Override - public Page moreLikeThis(MoreLikeThisQuery query, Class clazz) { - - ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); - String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName() : persistentEntity.getIndexName(); - String type = isNotBlank(query.getType()) ? query.getType() : persistentEntity.getIndexType(); - - Assert.notNull(indexName, "No 'indexName' defined for MoreLikeThisQuery"); - Assert.notNull(type, "No 'type' defined for MoreLikeThisQuery"); - Assert.notNull(query.getId(), "No document id defined for MoreLikeThisQuery"); - - - MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = moreLikeThisQuery(toArray(new MoreLikeThisQueryBuilder.Item(indexName, type, query.getId()))); - - if (query.getMinTermFreq() != null) { - moreLikeThisQueryBuilder.minTermFreq(query.getMinTermFreq()); - } - if (query.getMaxQueryTerms() != null) { - moreLikeThisQueryBuilder.maxQueryTerms(query.getMaxQueryTerms()); - } - if (!isEmpty(query.getStopWords())) { - moreLikeThisQueryBuilder.stopWords(toArray(query.getStopWords())); - } - if (query.getMinDocFreq() != null) { - moreLikeThisQueryBuilder.minDocFreq(query.getMinDocFreq()); - } - if (query.getMaxDocFreq() != null) { - moreLikeThisQueryBuilder.maxDocFreq(query.getMaxDocFreq()); - } - if (query.getMinWordLen() != null) { - moreLikeThisQueryBuilder.minWordLength(query.getMinWordLen()); - } - if (query.getMaxWordLen() != null) { - moreLikeThisQueryBuilder.maxWordLength(query.getMaxWordLen()); - } - if (query.getBoostTerms() != null) { - moreLikeThisQueryBuilder.boostTerms(query.getBoostTerms()); - } - - return queryForPage(new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).build(), clazz); - } - - private SearchResponse doSearch(SearchRequestBuilder searchRequest, SearchQuery searchQuery) { - if (searchQuery.getFilter() != null) { - searchRequest.setPostFilter(searchQuery.getFilter()); - } - - if (!isEmpty(searchQuery.getElasticsearchSorts())) { - for (SortBuilder sort : searchQuery.getElasticsearchSorts()) { - searchRequest.addSort(sort); - } - } - - if (!searchQuery.getScriptFields().isEmpty()) { - //_source should be return all the time - //searchRequest.addStoredField("_source"); - for (ScriptField scriptedField : searchQuery.getScriptFields()) { - searchRequest.addScriptField(scriptedField.fieldName(), scriptedField.script()); - } - } - - if (searchQuery.getHighlightFields() != null) { - for (HighlightBuilder.Field highlightField : searchQuery.getHighlightFields()) { - searchRequest.highlighter(new HighlightBuilder().field(highlightField)); - } - } - - if (!isEmpty(searchQuery.getIndicesBoost())) { - for (IndexBoost indexBoost : searchQuery.getIndicesBoost()) { - searchRequest.addIndexBoost(indexBoost.getIndexName(), indexBoost.getBoost()); - } - } - - if (!isEmpty(searchQuery.getAggregations())) { - for (AbstractAggregationBuilder aggregationBuilder : searchQuery.getAggregations()) { - searchRequest.addAggregation(aggregationBuilder); - } - } - - if (!isEmpty(searchQuery.getFacets())) { - for (FacetRequest aggregatedFacet : searchQuery.getFacets()) { - searchRequest.addAggregation(aggregatedFacet.getFacet()); - } - } - return getSearchResponse(searchRequest.setQuery(searchQuery.getQuery()).execute()); - } - - private SearchResponse getSearchResponse(ListenableActionFuture response) { - return searchTimeout == null ? response.actionGet() : response.actionGet(searchTimeout); - } - - private boolean createIndexIfNotCreated(Class clazz) { - return indexExists(getPersistentEntityFor(clazz).getIndexName()) || createIndexWithSettings(clazz); - } - - private boolean createIndexWithSettings(Class clazz) { - if (clazz.isAnnotationPresent(Setting.class)) { - String settingPath = clazz.getAnnotation(Setting.class).settingPath(); - if (isNotBlank(settingPath)) { - String settings = readFileFromClasspath(settingPath); - if (isNotBlank(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 boolean createIndex(String indexName, Object settings) { - CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(indexName); - if (settings instanceof String) { - createIndexRequestBuilder.setSettings(String.valueOf(settings)); - } else if (settings instanceof Map) { - createIndexRequestBuilder.setSettings((Map) settings); - } else if (settings instanceof XContentBuilder) { - createIndexRequestBuilder.setSettings((XContentBuilder) settings); - } - return createIndexRequestBuilder.execute().actionGet().isAcknowledged(); - } - - @Override - public boolean createIndex(Class clazz, Object settings) { - return createIndex(getPersistentEntityFor(clazz).getIndexName(), settings); - } - - private Map getDefaultSettings(ElasticsearchPersistentEntity persistentEntity) { - - if (persistentEntity.isUseServerConfiguration()) - return new HashMap(); - - return new MapBuilder().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(); - } - - @Override - public Map getSetting(Class clazz) { - return getSetting(getPersistentEntityFor(clazz).getIndexName()); - } - - @Override - public Map getSetting(String indexName) { - Assert.notNull(indexName, "No index defined for getSettings"); - return client.admin().indices().getSettings(new GetSettingsRequest()).actionGet().getIndexToSettings() - .get(indexName).getAsMap(); - } - - private SearchRequestBuilder prepareSearch(Query query, Class clazz) { - setPersistentEntityIndexAndType(query, clazz); - return prepareSearch(query); - } - - private SearchRequestBuilder prepareSearch(Query query) { - Assert.notNull(query.getIndices(), "No index defined for Query"); - Assert.notNull(query.getTypes(), "No type defined for Query"); - - int startRecord = 0; - SearchRequestBuilder searchRequestBuilder = client.prepareSearch(toArray(query.getIndices())) - .setSearchType(query.getSearchType()).setTypes(toArray(query.getTypes())); - - if (query.getSourceFilter() != null) { - SourceFilter sourceFilter = query.getSourceFilter(); - searchRequestBuilder.setFetchSource(sourceFilter.getIncludes(), sourceFilter.getExcludes()); - } - - if (query.getPageable().isPaged()) { - startRecord = query.getPageable().getPageNumber() * query.getPageable().getPageSize(); - searchRequestBuilder.setSize(query.getPageable().getPageSize()); - } - searchRequestBuilder.setFrom(startRecord); - - if (!query.getFields().isEmpty()) { - searchRequestBuilder.setFetchSource(toArray(query.getFields()),null); - } - - if (query.getSort() != null) { - for (Sort.Order order : query.getSort()) { - searchRequestBuilder.addSort(order.getProperty(), - order.getDirection() == Sort.Direction.DESC ? SortOrder.DESC : SortOrder.ASC); - } - } - - if (query.getMinScore() > 0) { - searchRequestBuilder.setMinScore(query.getMinScore()); - } - return searchRequestBuilder; - } - - private IndexRequestBuilder prepareIndex(IndexQuery query) { - try { - String indexName = isBlank(query.getIndexName()) - ? retrieveIndexNameFromPersistentEntity(query.getObject().getClass())[0] : query.getIndexName(); - String type = isBlank(query.getType()) ? retrieveTypeFromPersistentEntity(query.getObject().getClass())[0] - : query.getType(); - - IndexRequestBuilder indexRequestBuilder = null; - - if (query.getObject() != null) { - String id = isBlank(query.getId()) ? getPersistentEntityId(query.getObject()) : query.getId(); - // If we have a query id and a document id, do not ask ES to generate one. - if (id != null) { - indexRequestBuilder = client.prepareIndex(indexName, type, id); - } else { - indexRequestBuilder = client.prepareIndex(indexName, type); - } - indexRequestBuilder.setSource(resultsMapper.getEntityMapper().mapToString(query.getObject())); - } else if (query.getSource() != null) { - indexRequestBuilder = client.prepareIndex(indexName, type, query.getId()).setSource(query.getSource()); - } else { - throw new ElasticsearchException( - "object or source is null, failed to index the document [id: " + query.getId() + "]"); - } - if (query.getVersion() != null) { - indexRequestBuilder.setVersion(query.getVersion()); - indexRequestBuilder.setVersionType(EXTERNAL); - } - - if (query.getParentId() != null) { - indexRequestBuilder.setParent(query.getParentId()); - } - - return indexRequestBuilder; - } catch (IOException e) { - throw new ElasticsearchException("failed to index the document [id: " + query.getId() + "]", e); - } - } - - @Override - public void refresh(String indexName) { - Assert.notNull(indexName, "No index defined for refresh()"); - client.admin().indices().refresh(refreshRequest(indexName)).actionGet(); - } - - @Override - public void refresh(Class clazz) { - refresh(getPersistentEntityFor(clazz).getIndexName()); - } - - @Override - public Boolean addAlias(AliasQuery query) { - Assert.notNull(query.getIndexName(), "No index defined for Alias"); - Assert.notNull(query.getAliasName(), "No alias defined"); - final IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add().alias(query.getAliasName()).index(query.getIndexName()); - - if (query.getFilterBuilder() != null) { - aliasAction.filter(query.getFilterBuilder()); - } else if (query.getFilter() != null) { - aliasAction.filter(query.getFilter()); - } else if (isNotBlank(query.getRouting())) { - aliasAction.routing(query.getRouting()); - } else if (isNotBlank(query.getSearchRouting())) { - aliasAction.searchRouting(query.getSearchRouting()); - } else if (isNotBlank(query.getIndexRouting())) { - aliasAction.indexRouting(query.getIndexRouting()); - } - return client.admin().indices().prepareAliases().addAliasAction(aliasAction).execute().actionGet().isAcknowledged(); - } - - @Override - public Boolean removeAlias(AliasQuery query) { - Assert.notNull(query.getIndexName(), "No index defined for Alias"); - Assert.notNull(query.getAliasName(), "No alias defined"); - return client.admin().indices().prepareAliases().removeAlias(query.getIndexName(), query.getAliasName()).execute() - .actionGet().isAcknowledged(); - } - - @Override - public List queryForAlias(String indexName) { - return client.admin().indices().getAliases(new GetAliasesRequest().indices(indexName)).actionGet().getAliases() - .get(indexName); - } - - @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 String getPersistentEntityId(Object entity) { - - ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(entity.getClass()); - Object identifier = persistentEntity.getIdentifierAccessor(entity).getIdentifier(); - - if (identifier != null){ - return identifier.toString(); - } - - return null; - } - - private void setPersistentEntityId(Object entity, String id) { - - ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(entity.getClass()); - ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty(); - - // Only deal with text because ES generated Ids are strings ! - - if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) { - persistentEntity.getPropertyAccessor(entity).setProperty(idProperty, id); - } - } - - private void setPersistentEntityIndexAndType(Query query, Class clazz) { - if (query.getIndices().isEmpty()) { - query.addIndices(retrieveIndexNameFromPersistentEntity(clazz)); - } - if (query.getTypes().isEmpty()) { - query.addTypes(retrieveTypeFromPersistentEntity(clazz)); - } - } - - private String[] retrieveIndexNameFromPersistentEntity(Class clazz) { - if (clazz != null) { - return new String[] { getPersistentEntityFor(clazz).getIndexName() }; - } - return null; - } - - private String[] retrieveTypeFromPersistentEntity(Class clazz) { - if (clazz != null) { - return new String[] { getPersistentEntityFor(clazz).getIndexType() }; - } - return null; - } - - private List extractIds(SearchResponse response) { - List ids = new ArrayList<>(); - for (SearchHit hit : response.getHits()) { - if (hit != null) { - ids.add(hit.getId()); - } - } - return ids; - } - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - if (elasticsearchConverter instanceof ApplicationContextAware) { - ((ApplicationContextAware) elasticsearchConverter).setApplicationContext(context); - } - } - - private static String[] toArray(List values) { - String[] valuesAsArray = new String[values.size()]; - return values.toArray(valuesAsArray); - } - - private static MoreLikeThisQueryBuilder.Item[] toArray(MoreLikeThisQueryBuilder.Item... values) { - return values; - } - - protected ResultsMapper getResultsMapper() { - return resultsMapper; - } - - public static String readFileFromClasspath(String url) { - StringBuilder stringBuilder = new StringBuilder(); - - BufferedReader bufferedReader = null; - - try { - ClassPathResource classPathResource = new ClassPathResource(url); - InputStreamReader inputStreamReader = new InputStreamReader(classPathResource.getInputStream()); - bufferedReader = new BufferedReader(inputStreamReader); - String line; - - String lineSeparator = System.getProperty("line.separator"); - while ((line = bufferedReader.readLine()) != null) { - stringBuilder.append(line).append(lineSeparator); - } - } catch (Exception e) { - logger.debug(String.format("Failed to load file from url: %s: %s", url, e.getMessage())); - return null; - } finally { - if (bufferedReader != null) - try { - bufferedReader.close(); - } catch (IOException e) { - logger.debug(String.format("Unable to close buffered reader.. %s", e.getMessage())); - } - } - - return stringBuilder.toString(); - } - - public SearchResponse suggest(SuggestBuilder suggestion, String... indices) { - return client.prepareSearch(indices).suggest(suggestion).get(); - } - - public SearchResponse suggest(SuggestBuilder suggestion, Class clazz) { - return suggest(suggestion, retrieveIndexNameFromPersistentEntity(clazz)); - } -} +/* + * Copyright 2013-2017 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.elasticsearch.core; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import org.elasticsearch.action.ListenableActionFuture; +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.BulkItemResponse; +import org.elasticsearch.action.bulk.BulkRequestBuilder; +import org.elasticsearch.action.bulk.BulkResponse; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.get.MultiGetRequest; +import org.elasticsearch.action.get.MultiGetRequestBuilder; +import org.elasticsearch.action.get.MultiGetResponse; +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchRequestBuilder; +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.client.Requests; +import org.elasticsearch.cluster.metadata.AliasMetaData; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.collect.MapBuilder; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.index.query.MoreLikeThisQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; +import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; +import org.elasticsearch.search.sort.SortBuilder; +import org.elasticsearch.search.sort.SortOrder; +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.core.io.ClassPathResource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +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.aggregation.AggregatedPage; +import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl; +import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; +import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; +import org.springframework.data.elasticsearch.core.facet.FacetRequest; +import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; +import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; +import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; +import org.springframework.data.elasticsearch.core.query.*; +import org.springframework.data.util.CloseableIterator; +import org.springframework.util.Assert; +import static org.apache.commons.lang.StringUtils.*; +import static org.elasticsearch.client.Requests.*; +import static org.elasticsearch.index.VersionType.*; +import static org.elasticsearch.index.query.QueryBuilders.*; +import static org.springframework.data.elasticsearch.core.MappingBuilder.*; +import static org.springframework.util.CollectionUtils.isEmpty; + +/** + * ElasticsearchTemplate + * + * @author Rizwan Idrees + * @author Mohsin Husen + * @author Artur Konczak + * @author Kevin Leturc + * @author Mason Chan + * @author Young Gu + * @author Oliver Gierke + * @author Mark Janssen + * @author Mark Paluch + */ +public class ElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware { + + private static final Logger logger = LoggerFactory.getLogger(ElasticsearchTemplate.class); + private Client client; + private ElasticsearchConverter elasticsearchConverter; + private ResultsMapper resultsMapper; + private String searchTimeout; + + public ElasticsearchTemplate(Client client) { + this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext())); + } + + public ElasticsearchTemplate(Client client, EntityMapper entityMapper) { + this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext()), entityMapper); + } + + public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter, + EntityMapper entityMapper) { + this(client, elasticsearchConverter, + new DefaultResultMapper(elasticsearchConverter.getMappingContext(), entityMapper)); + } + + public ElasticsearchTemplate(Client client, ResultsMapper resultsMapper) { + this(client, new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext()), resultsMapper); + } + + public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter) { + this(client, elasticsearchConverter, new DefaultResultMapper(elasticsearchConverter.getMappingContext())); + } + + public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter, + ResultsMapper resultsMapper) { + + Assert.notNull(client, "Client must not be null!"); + Assert.notNull(elasticsearchConverter, "ElasticsearchConverter must not be null!"); + Assert.notNull(resultsMapper, "ResultsMapper must not be null!"); + + this.client = client; + this.elasticsearchConverter = elasticsearchConverter; + this.resultsMapper = resultsMapper; + } + + @Override + public Client getClient() { + return client; + } + + public void setSearchTimeout(String searchTimeout) { + this.searchTimeout = searchTimeout; + } + + @Override + public boolean createIndex(Class clazz) { + return createIndexIfNotCreated(clazz); + } + + @Override + public boolean createIndex(String indexName) { + Assert.notNull(indexName, "No index defined for Query"); + return client.admin().indices().create(Requests.createIndexRequest(indexName)).actionGet().isAcknowledged(); + } + + @Override + public boolean putMapping(Class clazz) { + if (clazz.isAnnotationPresent(Mapping.class)) { + String mappingPath = clazz.getAnnotation(Mapping.class).mappingPath(); + if (isNotBlank(mappingPath)) { + String mappings = readFileFromClasspath(mappingPath); + if (isNotBlank(mappings)) { + return putMapping(clazz, mappings); + } + } else { + logger.info("mappingPath in @Mapping has to be defined. Building mappings using @Field"); + } + } + ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); + XContentBuilder xContentBuilder = null; + try { + + ElasticsearchPersistentProperty property = persistentEntity.getRequiredIdProperty(); + + xContentBuilder = buildMapping(clazz, persistentEntity.getIndexType(), + property.getFieldName(), persistentEntity.getParentType()); + } catch (Exception e) { + throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e); + } + return putMapping(clazz, xContentBuilder); + } + + @Override + public boolean putMapping(Class clazz, Object mapping) { + return putMapping(getPersistentEntityFor(clazz).getIndexName(), getPersistentEntityFor(clazz).getIndexType(), + mapping); + } + + @Override + public boolean putMapping(String indexName, String type, Object mapping) { + Assert.notNull(indexName, "No index defined for putMapping()"); + Assert.notNull(type, "No type defined for putMapping()"); + PutMappingRequestBuilder requestBuilder = client.admin().indices().preparePutMapping(indexName).setType(type); + if (mapping instanceof String) { + requestBuilder.setSource(String.valueOf(mapping)); + } else if (mapping instanceof Map) { + requestBuilder.setSource((Map) mapping); + } else if (mapping instanceof XContentBuilder) { + requestBuilder.setSource((XContentBuilder) mapping); + } + return requestBuilder.execute().actionGet().isAcknowledged(); + } + + @Override + public Map getMapping(String indexName, String type) { + Assert.notNull(indexName, "No index defined for putMapping()"); + Assert.notNull(type, "No type defined for putMapping()"); + Map mappings = null; + try { + mappings = client.admin().indices().getMappings(new GetMappingsRequest().indices(indexName).types(type)) + .actionGet().getMappings().get(indexName).get(type).getSourceAsMap(); + } catch (Exception e) { + throw new ElasticsearchException( + "Error while getting mapping for indexName : " + indexName + " type : " + type + " " + e.getMessage()); + } + return mappings; + } + + @Override + public Map getMapping(Class clazz) { + return getMapping(getPersistentEntityFor(clazz).getIndexName(), getPersistentEntityFor(clazz).getIndexType()); + } + + @Override + public ElasticsearchConverter getElasticsearchConverter() { + return elasticsearchConverter; + } + + @Override + public T queryForObject(GetQuery query, Class clazz) { + return queryForObject(query, clazz, resultsMapper); + } + + @Override + public T queryForObject(GetQuery query, Class clazz, GetResultMapper mapper) { + ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); + GetResponse response = client + .prepareGet(persistentEntity.getIndexName(), persistentEntity.getIndexType(), query.getId()).execute() + .actionGet(); + + T entity = mapper.mapResult(response, clazz); + return entity; + } + + @Override + public T queryForObject(CriteriaQuery query, Class clazz) { + Page page = queryForPage(query, clazz); + Assert.isTrue(page.getTotalElements() < 2, "Expected 1 but found " + page.getTotalElements() + " results"); + return page.getTotalElements() > 0 ? page.getContent().get(0) : null; + } + + @Override + public T queryForObject(StringQuery query, Class clazz) { + Page page = queryForPage(query, clazz); + Assert.isTrue(page.getTotalElements() < 2, "Expected 1 but found " + page.getTotalElements() + " results"); + return page.getTotalElements() > 0 ? page.getContent().get(0) : null; + } + + @Override + public AggregatedPage queryForPage(SearchQuery query, Class clazz) { + return queryForPage(query, clazz, resultsMapper); + } + + @Override + public AggregatedPage queryForPage(SearchQuery query, Class clazz, SearchResultMapper mapper) { + SearchResponse response = doSearch(prepareSearch(query, clazz), query); + return mapper.mapResults(response, clazz, query.getPageable()); + } + + @Override + public T query(SearchQuery query, ResultsExtractor resultsExtractor) { + SearchResponse response = doSearch(prepareSearch(query), query); + return resultsExtractor.extract(response); + } + + @Override + public List queryForList(CriteriaQuery query, Class clazz) { + return queryForPage(query, clazz).getContent(); + } + + @Override + public List queryForList(StringQuery query, Class clazz) { + return queryForPage(query, clazz).getContent(); + } + + @Override + public List queryForList(SearchQuery query, Class clazz) { + return queryForPage(query, clazz).getContent(); + } + + @Override + public List queryForIds(SearchQuery query) { + SearchRequestBuilder request = prepareSearch(query).setQuery(query.getQuery()); + if (query.getFilter() != null) { + request.setPostFilter(query.getFilter()); + } + SearchResponse response = getSearchResponse(request.execute()); + return extractIds(response); + } + + @Override + public Page queryForPage(CriteriaQuery criteriaQuery, Class clazz) { + QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria()); + QueryBuilder elasticsearchFilter = new CriteriaFilterProcessor() + .createFilterFromCriteria(criteriaQuery.getCriteria()); + SearchRequestBuilder searchRequestBuilder = prepareSearch(criteriaQuery, clazz); + + if (elasticsearchQuery != null) { + searchRequestBuilder.setQuery(elasticsearchQuery); + } else { + searchRequestBuilder.setQuery(QueryBuilders.matchAllQuery()); + } + + if (criteriaQuery.getMinScore() > 0) { + searchRequestBuilder.setMinScore(criteriaQuery.getMinScore()); + } + + if (elasticsearchFilter != null) + searchRequestBuilder.setPostFilter(elasticsearchFilter); + if (logger.isDebugEnabled()) { + logger.debug("doSearch query:\n" + searchRequestBuilder.toString()); + } + + SearchResponse response = getSearchResponse(searchRequestBuilder.execute()); + return resultsMapper.mapResults(response, clazz, criteriaQuery.getPageable()); + } + + @Override + public Page queryForPage(StringQuery query, Class clazz) { + return queryForPage(query, clazz, resultsMapper); + } + + @Override + public Page queryForPage(StringQuery query, Class clazz, SearchResultMapper mapper) { + SearchResponse response = getSearchResponse(prepareSearch(query, clazz).setQuery(wrapperQuery(query.getSource())).execute()); + return mapper.mapResults(response, clazz, query.getPageable()); + } + + @Override + public CloseableIterator stream(CriteriaQuery query, Class clazz) { + final long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis(); + return doStream(scrollTimeInMillis, (ScrolledPage) startScroll(scrollTimeInMillis, query, clazz), clazz, resultsMapper); + } + + @Override + public CloseableIterator stream(SearchQuery query, Class clazz) { + return stream(query, clazz, resultsMapper); + } + + @Override + public CloseableIterator stream(SearchQuery query, final Class clazz, final SearchResultMapper mapper) { + final long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis(); + return doStream(scrollTimeInMillis, (ScrolledPage) startScroll(scrollTimeInMillis, query, clazz, mapper), clazz, mapper); + } + + private CloseableIterator doStream(final long scrollTimeInMillis, final ScrolledPage page, final Class clazz, final SearchResultMapper mapper) { + return new CloseableIterator() { + + /** As we couldn't retrieve single result with scroll, store current hits. */ + private volatile Iterator currentHits = page.iterator(); + + /** The scroll id. */ + private volatile String scrollId = page.getScrollId(); + + /** If stream is finished (ie: cluster returns no results. */ + private volatile boolean finished = !currentHits.hasNext(); + + @Override + public void close() { + try { + // Clear scroll on cluster only in case of error (cause elasticsearch auto clear scroll when it's done) + if (!finished && scrollId != null && currentHits != null && currentHits.hasNext()) { + clearScroll(scrollId); + } + } finally { + currentHits = null; + scrollId = null; + } + } + + @Override + public boolean hasNext() { + // Test if stream is finished + if (finished) { + return false; + } + // Test if it remains hits + if (currentHits == null || !currentHits.hasNext()) { + // Do a new request + final ScrolledPage scroll = (ScrolledPage) continueScroll(scrollId, scrollTimeInMillis, clazz, mapper); + // Save hits and scroll id + currentHits = scroll.iterator(); + finished = !currentHits.hasNext(); + scrollId = scroll.getScrollId(); + } + return currentHits.hasNext(); + } + + @Override + public T next() { + if (hasNext()) { + return currentHits.next(); + } + throw new NoSuchElementException(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove"); + } + }; + } + + @Override + public long count(CriteriaQuery criteriaQuery, Class clazz) { + QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria()); + QueryBuilder elasticsearchFilter = new CriteriaFilterProcessor() + .createFilterFromCriteria(criteriaQuery.getCriteria()); + + if (elasticsearchFilter == null) { + return doCount(prepareCount(criteriaQuery, clazz), elasticsearchQuery); + } else { + // filter could not be set into CountRequestBuilder, convert request into search request + return doCount(prepareSearch(criteriaQuery, clazz), elasticsearchQuery, elasticsearchFilter); + } + } + + @Override + public long count(SearchQuery searchQuery, Class clazz) { + QueryBuilder elasticsearchQuery = searchQuery.getQuery(); + QueryBuilder elasticsearchFilter = searchQuery.getFilter(); + + if (elasticsearchFilter == null) { + return doCount(prepareCount(searchQuery, clazz), elasticsearchQuery); + } else { + // filter could not be set into CountRequestBuilder, convert request into search request + return doCount(prepareSearch(searchQuery, clazz), elasticsearchQuery, elasticsearchFilter); + } + } + + @Override + public long count(CriteriaQuery query) { + return count(query, null); + } + + @Override + public long count(SearchQuery query) { + return count(query, null); + } + + private long doCount(SearchRequestBuilder countRequestBuilder, QueryBuilder elasticsearchQuery) { + + if (elasticsearchQuery != null) { + countRequestBuilder.setQuery(elasticsearchQuery); + } + return countRequestBuilder.execute().actionGet().getHits().getTotalHits(); + } + + private long doCount(SearchRequestBuilder searchRequestBuilder, QueryBuilder elasticsearchQuery, + QueryBuilder elasticsearchFilter) { + if (elasticsearchQuery != null) { + searchRequestBuilder.setQuery(elasticsearchQuery); + } else { + searchRequestBuilder.setQuery(QueryBuilders.matchAllQuery()); + } + if (elasticsearchFilter != null) { + searchRequestBuilder.setPostFilter(elasticsearchFilter); + } + return searchRequestBuilder.execute().actionGet().getHits().getTotalHits(); + } + + private SearchRequestBuilder prepareCount(Query query, Class clazz) { + String indexName[] = !isEmpty(query.getIndices()) + ? query.getIndices().toArray(new String[query.getIndices().size()]) + : retrieveIndexNameFromPersistentEntity(clazz); + String types[] = !isEmpty(query.getTypes()) ? query.getTypes().toArray(new String[query.getTypes().size()]) + : retrieveTypeFromPersistentEntity(clazz); + + Assert.notNull(indexName, "No index defined for Query"); + + SearchRequestBuilder countRequestBuilder = client.prepareSearch(indexName); + + if (types != null) { + countRequestBuilder.setTypes(types); + } + countRequestBuilder.setSize(0); + return countRequestBuilder; + } + + @Override + public LinkedList multiGet(SearchQuery searchQuery, Class clazz) { + return resultsMapper.mapResults(getMultiResponse(searchQuery, clazz), clazz); + } + + private MultiGetResponse getMultiResponse(Query searchQuery, Class clazz) { + + String indexName = !isEmpty(searchQuery.getIndices()) ? searchQuery.getIndices().get(0) + : getPersistentEntityFor(clazz).getIndexName(); + String type = !isEmpty(searchQuery.getTypes()) ? searchQuery.getTypes().get(0) + : getPersistentEntityFor(clazz).getIndexType(); + + Assert.notNull(indexName, "No index defined for Query"); + Assert.notNull(type, "No type define for Query"); + Assert.notEmpty(searchQuery.getIds(), "No Id define for Query"); + + MultiGetRequestBuilder builder = client.prepareMultiGet(); + + if (searchQuery.getFields() != null && !searchQuery.getFields().isEmpty()) { + searchQuery.addSourceFilter(new FetchSourceFilter(toArray(searchQuery.getFields()), null)); + } + + for (String id : searchQuery.getIds()) { + + MultiGetRequest.Item item = new MultiGetRequest.Item(indexName, type, id); + + if (searchQuery.getRoute() != null) { + item = item.routing(searchQuery.getRoute()); + } + + builder.add(item); + } + return builder.execute().actionGet(); + } + + @Override + public LinkedList multiGet(SearchQuery searchQuery, Class clazz, MultiGetResultMapper getResultMapper) { + return getResultMapper.mapResults(getMultiResponse(searchQuery, clazz), clazz); + } + + @Override + public String index(IndexQuery query) { + String documentId = prepareIndex(query).execute().actionGet().getId(); + // We should call this because we are not going through a mapper. + if (query.getObject() != null) { + setPersistentEntityId(query.getObject(), documentId); + } + return documentId; + } + + @Override + public UpdateResponse update(UpdateQuery query) { + return this.prepareUpdate(query).execute().actionGet(); + } + + private UpdateRequestBuilder prepareUpdate(UpdateQuery query) { + String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName() + : getPersistentEntityFor(query.getClazz()).getIndexName(); + String type = isNotBlank(query.getType()) ? query.getType() + : getPersistentEntityFor(query.getClazz()).getIndexType(); + Assert.notNull(indexName, "No index defined for Query"); + Assert.notNull(type, "No type define for Query"); + Assert.notNull(query.getId(), "No Id define for Query"); + Assert.notNull(query.getUpdateRequest(), "No IndexRequest define for Query"); + UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate(indexName, type, query.getId()); + updateRequestBuilder.setRouting(query.getUpdateRequest().routing()); + + if (query.getUpdateRequest().script() == null) { + // doc + if (query.DoUpsert()) { + updateRequestBuilder.setDocAsUpsert(true).setDoc(query.getUpdateRequest().doc()); + } else { + updateRequestBuilder.setDoc(query.getUpdateRequest().doc()); + } + } else { + // or script + updateRequestBuilder.setScript(query.getUpdateRequest().script()); + } + + return updateRequestBuilder; + } + + @Override + public void bulkIndex(List queries) { + BulkRequestBuilder bulkRequest = client.prepareBulk(); + for (IndexQuery query : queries) { + bulkRequest.add(prepareIndex(query)); + } + checkForBulkUpdateFailure(bulkRequest.execute().actionGet()); + } + + @Override + public void bulkUpdate(List queries) { + BulkRequestBuilder bulkRequest = client.prepareBulk(); + for (UpdateQuery query : queries) { + bulkRequest.add(prepareUpdate(query)); + } + checkForBulkUpdateFailure(bulkRequest.execute().actionGet()); + } + + private void checkForBulkUpdateFailure(BulkResponse bulkResponse) { + if (bulkResponse.hasFailures()) { + Map failedDocuments = new HashMap<>(); + for (BulkItemResponse item : bulkResponse.getItems()) { + if (item.isFailed()) + failedDocuments.put(item.getId(), item.getFailureMessage()); + } + throw new ElasticsearchException( + "Bulk indexing has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages [" + + failedDocuments + "]", + failedDocuments); + } + } + + @Override + public boolean indexExists(Class clazz) { + return indexExists(getPersistentEntityFor(clazz).getIndexName()); + } + + @Override + public boolean indexExists(String indexName) { + return client.admin().indices().exists(indicesExistsRequest(indexName)).actionGet().isExists(); + } + + @Override + public boolean typeExists(String index, String type) { + return client.admin().cluster().prepareState().execute().actionGet().getState().metaData().index(index) + .getMappings().containsKey(type); + } + + @Override + public boolean deleteIndex(Class clazz) { + return deleteIndex(getPersistentEntityFor(clazz).getIndexName()); + } + + @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 indexName, String type, String id) { + return client.prepareDelete(indexName, type, id).execute().actionGet().getId(); + } + + @Override + public String delete(Class clazz, String id) { + ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); + return delete(persistentEntity.getIndexName(), persistentEntity.getIndexType(), id); + } + + @Override + public void delete(DeleteQuery deleteQuery, Class clazz) { + + String indexName = isNotBlank(deleteQuery.getIndex()) ? deleteQuery.getIndex() + : getPersistentEntityFor(clazz).getIndexName(); + String typeName = isNotBlank(deleteQuery.getType()) ? deleteQuery.getType() + : getPersistentEntityFor(clazz).getIndexType(); + Integer pageSize = deleteQuery.getPageSize() != null ? deleteQuery.getPageSize() : 1000; + Long scrollTimeInMillis = deleteQuery.getScrollTimeInMillis() != null ? deleteQuery.getScrollTimeInMillis() + : 10000l; + + SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(deleteQuery.getQuery()).withIndices(indexName) + .withTypes(typeName).withPageable(PageRequest.of(0, pageSize)).build(); + + SearchResultMapper onlyIdResultMapper = new SearchResultMapper() { + @Override + public AggregatedPage mapResults(SearchResponse response, Class clazz, Pageable pageable) { + List result = new ArrayList(); + for (SearchHit searchHit : response.getHits().getHits()) { + String id = searchHit.getId(); + result.add(id); + } + if (result.size() > 0) { + return new AggregatedPageImpl((List) result, response.getScrollId()); + } + return new AggregatedPageImpl(Collections.EMPTY_LIST, response.getScrollId()); + } + }; + + Page scrolledResult = startScroll(scrollTimeInMillis, searchQuery, String.class, onlyIdResultMapper); + BulkRequestBuilder bulkRequestBuilder = client.prepareBulk(); + List ids = new ArrayList(); + + do { + ids.addAll(scrolledResult.getContent()); + scrolledResult = continueScroll(((ScrolledPage)scrolledResult).getScrollId(), scrollTimeInMillis, String.class, onlyIdResultMapper); + } while(scrolledResult.getContent().size() != 0); + + for (String id : ids) { + bulkRequestBuilder.add(client.prepareDelete(indexName, typeName, id)); + } + + if (bulkRequestBuilder.numberOfActions() > 0) { + bulkRequestBuilder.execute().actionGet(); + } + + clearScroll(((ScrolledPage) scrolledResult).getScrollId()); + } + + @Override + public void delete(DeleteQuery deleteQuery) { + Assert.notNull(deleteQuery.getIndex(), "No index defined for Query"); + Assert.notNull(deleteQuery.getType(), "No type define for Query"); + delete(deleteQuery, null); + } + + @Override + public void delete(CriteriaQuery criteriaQuery, Class clazz) { + QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria()); + Assert.notNull(elasticsearchQuery, "Query can not be null."); + DeleteQuery deleteQuery = new DeleteQuery(); + deleteQuery.setQuery(elasticsearchQuery); + delete(deleteQuery, clazz); + } + + private SearchRequestBuilder prepareScroll(Query query, long scrollTimeInMillis, Class clazz) { + setPersistentEntityIndexAndType(query, clazz); + return prepareScroll(query, scrollTimeInMillis); + } + + private SearchRequestBuilder prepareScroll(Query query, long scrollTimeInMillis) { + SearchRequestBuilder requestBuilder = client.prepareSearch(toArray(query.getIndices())) + .setTypes(toArray(query.getTypes())).setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).setFrom(0); + + if(query.getPageable().isPaged()){ + requestBuilder.setSize(query.getPageable().getPageSize()); + } + + if (!isEmpty(query.getFields())) { + requestBuilder.setFetchSource(toArray(query.getFields()), null); + } + return requestBuilder; + } + + private SearchResponse doScroll(SearchRequestBuilder requestBuilder, CriteriaQuery criteriaQuery) { + Assert.notNull(criteriaQuery.getIndices(), "No index defined for Query"); + Assert.notNull(criteriaQuery.getTypes(), "No type define for Query"); + Assert.notNull(criteriaQuery.getPageable(), "Query.pageable is required for scan & scroll"); + + QueryBuilder elasticsearchQuery = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria()); + QueryBuilder elasticsearchFilter = new CriteriaFilterProcessor() + .createFilterFromCriteria(criteriaQuery.getCriteria()); + + if (elasticsearchQuery != null) { + requestBuilder.setQuery(elasticsearchQuery); + } else { + requestBuilder.setQuery(QueryBuilders.matchAllQuery()); + } + + if (elasticsearchFilter != null) { + requestBuilder.setPostFilter(elasticsearchFilter); + } + + return getSearchResponse(requestBuilder.execute()); + } + + private SearchResponse doScroll(SearchRequestBuilder requestBuilder, SearchQuery searchQuery) { + Assert.notNull(searchQuery.getIndices(), "No index defined for Query"); + Assert.notNull(searchQuery.getTypes(), "No type define for Query"); + Assert.notNull(searchQuery.getPageable(), "Query.pageable is required for scan & scroll"); + + if (searchQuery.getFilter() != null) { + requestBuilder.setPostFilter(searchQuery.getFilter()); + } + + return getSearchResponse(requestBuilder.setQuery(searchQuery.getQuery()).execute()); + } + + public Page startScroll(long scrollTimeInMillis, SearchQuery searchQuery, Class clazz) { + SearchResponse response = doScroll(prepareScroll(searchQuery, scrollTimeInMillis, clazz), searchQuery); + return resultsMapper.mapResults(response, clazz, null); + } + + public Page startScroll(long scrollTimeInMillis, CriteriaQuery criteriaQuery, Class clazz) { + SearchResponse response = doScroll(prepareScroll(criteriaQuery, scrollTimeInMillis, clazz), criteriaQuery); + return resultsMapper.mapResults(response, clazz, null); + } + + public Page startScroll(long scrollTimeInMillis, SearchQuery searchQuery, Class clazz, SearchResultMapper mapper) { + SearchResponse response = doScroll(prepareScroll(searchQuery, scrollTimeInMillis, clazz), searchQuery); + return mapper.mapResults(response, clazz, null); + } + + public Page startScroll(long scrollTimeInMillis, CriteriaQuery criteriaQuery, Class clazz, SearchResultMapper mapper) { + SearchResponse response = doScroll(prepareScroll(criteriaQuery, scrollTimeInMillis, clazz), criteriaQuery); + return mapper.mapResults(response, clazz, null); + } + + public Page continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class clazz) { + SearchResponse response = getSearchResponse(client.prepareSearchScroll(scrollId) + .setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute()); + return resultsMapper.mapResults(response, clazz, Pageable.unpaged()); + } + + public Page continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class clazz, SearchResultMapper mapper) { + SearchResponse response = getSearchResponse(client.prepareSearchScroll(scrollId) + .setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute()); + return mapper.mapResults(response, clazz, Pageable.unpaged()); + } + + @Override + public void clearScroll(String scrollId) { + client.prepareClearScroll().addScrollId(scrollId).execute().actionGet(); + } + + @Override + public Page moreLikeThis(MoreLikeThisQuery query, Class clazz) { + + ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz); + String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName() : persistentEntity.getIndexName(); + String type = isNotBlank(query.getType()) ? query.getType() : persistentEntity.getIndexType(); + + Assert.notNull(indexName, "No 'indexName' defined for MoreLikeThisQuery"); + Assert.notNull(type, "No 'type' defined for MoreLikeThisQuery"); + Assert.notNull(query.getId(), "No document id defined for MoreLikeThisQuery"); + + + MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = moreLikeThisQuery(toArray(new MoreLikeThisQueryBuilder.Item(indexName, type, query.getId()))); + + if (query.getMinTermFreq() != null) { + moreLikeThisQueryBuilder.minTermFreq(query.getMinTermFreq()); + } + if (query.getMaxQueryTerms() != null) { + moreLikeThisQueryBuilder.maxQueryTerms(query.getMaxQueryTerms()); + } + if (!isEmpty(query.getStopWords())) { + moreLikeThisQueryBuilder.stopWords(toArray(query.getStopWords())); + } + if (query.getMinDocFreq() != null) { + moreLikeThisQueryBuilder.minDocFreq(query.getMinDocFreq()); + } + if (query.getMaxDocFreq() != null) { + moreLikeThisQueryBuilder.maxDocFreq(query.getMaxDocFreq()); + } + if (query.getMinWordLen() != null) { + moreLikeThisQueryBuilder.minWordLength(query.getMinWordLen()); + } + if (query.getMaxWordLen() != null) { + moreLikeThisQueryBuilder.maxWordLength(query.getMaxWordLen()); + } + if (query.getBoostTerms() != null) { + moreLikeThisQueryBuilder.boostTerms(query.getBoostTerms()); + } + + return queryForPage(new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).build(), clazz); + } + + private SearchResponse doSearch(SearchRequestBuilder searchRequest, SearchQuery searchQuery) { + if (searchQuery.getFilter() != null) { + searchRequest.setPostFilter(searchQuery.getFilter()); + } + + if (!isEmpty(searchQuery.getElasticsearchSorts())) { + for (SortBuilder sort : searchQuery.getElasticsearchSorts()) { + searchRequest.addSort(sort); + } + } + + if (!searchQuery.getScriptFields().isEmpty()) { + //_source should be return all the time + //searchRequest.addStoredField("_source"); + for (ScriptField scriptedField : searchQuery.getScriptFields()) { + searchRequest.addScriptField(scriptedField.fieldName(), scriptedField.script()); + } + } + + if (searchQuery.getHighlightFields() != null) { + for (HighlightBuilder.Field highlightField : searchQuery.getHighlightFields()) { + searchRequest.highlighter(new HighlightBuilder().field(highlightField)); + } + } + + if (!isEmpty(searchQuery.getIndicesBoost())) { + for (IndexBoost indexBoost : searchQuery.getIndicesBoost()) { + searchRequest.addIndexBoost(indexBoost.getIndexName(), indexBoost.getBoost()); + } + } + + if (!isEmpty(searchQuery.getAggregations())) { + for (AbstractAggregationBuilder aggregationBuilder : searchQuery.getAggregations()) { + searchRequest.addAggregation(aggregationBuilder); + } + } + + if (!isEmpty(searchQuery.getFacets())) { + for (FacetRequest aggregatedFacet : searchQuery.getFacets()) { + searchRequest.addAggregation(aggregatedFacet.getFacet()); + } + } + return getSearchResponse(searchRequest.setQuery(searchQuery.getQuery()).execute()); + } + + private SearchResponse getSearchResponse(ListenableActionFuture response) { + return searchTimeout == null ? response.actionGet() : response.actionGet(searchTimeout); + } + + private boolean createIndexIfNotCreated(Class clazz) { + return indexExists(getPersistentEntityFor(clazz).getIndexName()) || createIndexWithSettings(clazz); + } + + private boolean createIndexWithSettings(Class clazz) { + if (clazz.isAnnotationPresent(Setting.class)) { + String settingPath = clazz.getAnnotation(Setting.class).settingPath(); + if (isNotBlank(settingPath)) { + String settings = readFileFromClasspath(settingPath); + if (isNotBlank(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 boolean createIndex(String indexName, Object settings) { + CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(indexName); + if (settings instanceof String) { + createIndexRequestBuilder.setSettings(String.valueOf(settings)); + } else if (settings instanceof Map) { + createIndexRequestBuilder.setSettings((Map) settings); + } else if (settings instanceof XContentBuilder) { + createIndexRequestBuilder.setSettings((XContentBuilder) settings); + } + return createIndexRequestBuilder.execute().actionGet().isAcknowledged(); + } + + @Override + public boolean createIndex(Class clazz, Object settings) { + return createIndex(getPersistentEntityFor(clazz).getIndexName(), settings); + } + + private Map getDefaultSettings(ElasticsearchPersistentEntity persistentEntity) { + + if (persistentEntity.isUseServerConfiguration()) + return new HashMap(); + + return new MapBuilder().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(); + } + + @Override + public Map getSetting(Class clazz) { + return getSetting(getPersistentEntityFor(clazz).getIndexName()); + } + + @Override + public Map getSetting(String indexName) { + Assert.notNull(indexName, "No index defined for getSettings"); + return client.admin().indices().getSettings(new GetSettingsRequest()).actionGet().getIndexToSettings() + .get(indexName).getAsMap(); + } + + private SearchRequestBuilder prepareSearch(Query query, Class clazz) { + setPersistentEntityIndexAndType(query, clazz); + return prepareSearch(query); + } + + private SearchRequestBuilder prepareSearch(Query query) { + Assert.notNull(query.getIndices(), "No index defined for Query"); + Assert.notNull(query.getTypes(), "No type defined for Query"); + + int startRecord = 0; + SearchRequestBuilder searchRequestBuilder = client.prepareSearch(toArray(query.getIndices())) + .setSearchType(query.getSearchType()).setTypes(toArray(query.getTypes())); + + if (query.getSourceFilter() != null) { + SourceFilter sourceFilter = query.getSourceFilter(); + searchRequestBuilder.setFetchSource(sourceFilter.getIncludes(), sourceFilter.getExcludes()); + } + + if (query.getPageable().isPaged()) { + startRecord = query.getPageable().getPageNumber() * query.getPageable().getPageSize(); + searchRequestBuilder.setSize(query.getPageable().getPageSize()); + } + searchRequestBuilder.setFrom(startRecord); + + if (!query.getFields().isEmpty()) { + searchRequestBuilder.setFetchSource(toArray(query.getFields()),null); + } + + if (query.getSort() != null) { + for (Sort.Order order : query.getSort()) { + searchRequestBuilder.addSort(order.getProperty(), + order.getDirection() == Sort.Direction.DESC ? SortOrder.DESC : SortOrder.ASC); + } + } + + if (query.getMinScore() > 0) { + searchRequestBuilder.setMinScore(query.getMinScore()); + } + return searchRequestBuilder; + } + + private IndexRequestBuilder prepareIndex(IndexQuery query) { + try { + String indexName = isBlank(query.getIndexName()) + ? retrieveIndexNameFromPersistentEntity(query.getObject().getClass())[0] : query.getIndexName(); + String type = isBlank(query.getType()) ? retrieveTypeFromPersistentEntity(query.getObject().getClass())[0] + : query.getType(); + + IndexRequestBuilder indexRequestBuilder = null; + + if (query.getObject() != null) { + String id = isBlank(query.getId()) ? getPersistentEntityId(query.getObject()) : query.getId(); + // If we have a query id and a document id, do not ask ES to generate one. + if (id != null) { + indexRequestBuilder = client.prepareIndex(indexName, type, id); + } else { + indexRequestBuilder = client.prepareIndex(indexName, type); + } + indexRequestBuilder.setSource(resultsMapper.getEntityMapper().mapToString(query.getObject())); + } else if (query.getSource() != null) { + indexRequestBuilder = client.prepareIndex(indexName, type, query.getId()).setSource(query.getSource()); + } else { + throw new ElasticsearchException( + "object or source is null, failed to index the document [id: " + query.getId() + "]"); + } + if (query.getVersion() != null) { + indexRequestBuilder.setVersion(query.getVersion()); + indexRequestBuilder.setVersionType(EXTERNAL); + } + + if (query.getParentId() != null) { + indexRequestBuilder.setParent(query.getParentId()); + } + + return indexRequestBuilder; + } catch (IOException e) { + throw new ElasticsearchException("failed to index the document [id: " + query.getId() + "]", e); + } + } + + @Override + public void refresh(String indexName) { + Assert.notNull(indexName, "No index defined for refresh()"); + client.admin().indices().refresh(refreshRequest(indexName)).actionGet(); + } + + @Override + public void refresh(Class clazz) { + refresh(getPersistentEntityFor(clazz).getIndexName()); + } + + @Override + public Boolean addAlias(AliasQuery query) { + Assert.notNull(query.getIndexName(), "No index defined for Alias"); + Assert.notNull(query.getAliasName(), "No alias defined"); + final IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add().alias(query.getAliasName()).index(query.getIndexName()); + + if (query.getFilterBuilder() != null) { + aliasAction.filter(query.getFilterBuilder()); + } else if (query.getFilter() != null) { + aliasAction.filter(query.getFilter()); + } else if (isNotBlank(query.getRouting())) { + aliasAction.routing(query.getRouting()); + } else if (isNotBlank(query.getSearchRouting())) { + aliasAction.searchRouting(query.getSearchRouting()); + } else if (isNotBlank(query.getIndexRouting())) { + aliasAction.indexRouting(query.getIndexRouting()); + } + return client.admin().indices().prepareAliases().addAliasAction(aliasAction).execute().actionGet().isAcknowledged(); + } + + @Override + public Boolean removeAlias(AliasQuery query) { + Assert.notNull(query.getIndexName(), "No index defined for Alias"); + Assert.notNull(query.getAliasName(), "No alias defined"); + return client.admin().indices().prepareAliases().removeAlias(query.getIndexName(), query.getAliasName()).execute() + .actionGet().isAcknowledged(); + } + + @Override + public List queryForAlias(String indexName) { + return client.admin().indices().getAliases(new GetAliasesRequest().indices(indexName)).actionGet().getAliases() + .get(indexName); + } + + @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 String getPersistentEntityId(Object entity) { + + ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(entity.getClass()); + Object identifier = persistentEntity.getIdentifierAccessor(entity).getIdentifier(); + + if (identifier != null){ + return identifier.toString(); + } + + return null; + } + + private void setPersistentEntityId(Object entity, String id) { + + ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(entity.getClass()); + ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty(); + + // Only deal with text because ES generated Ids are strings ! + + if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) { + persistentEntity.getPropertyAccessor(entity).setProperty(idProperty, id); + } + } + + private void setPersistentEntityIndexAndType(Query query, Class clazz) { + if (query.getIndices().isEmpty()) { + query.addIndices(retrieveIndexNameFromPersistentEntity(clazz)); + } + if (query.getTypes().isEmpty()) { + query.addTypes(retrieveTypeFromPersistentEntity(clazz)); + } + } + + private String[] retrieveIndexNameFromPersistentEntity(Class clazz) { + if (clazz != null) { + return new String[] { getPersistentEntityFor(clazz).getIndexName() }; + } + return null; + } + + private String[] retrieveTypeFromPersistentEntity(Class clazz) { + if (clazz != null) { + return new String[] { getPersistentEntityFor(clazz).getIndexType() }; + } + return null; + } + + private List extractIds(SearchResponse response) { + List ids = new ArrayList<>(); + for (SearchHit hit : response.getHits()) { + if (hit != null) { + ids.add(hit.getId()); + } + } + return ids; + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + if (elasticsearchConverter instanceof ApplicationContextAware) { + ((ApplicationContextAware) elasticsearchConverter).setApplicationContext(context); + } + } + + private static String[] toArray(List values) { + String[] valuesAsArray = new String[values.size()]; + return values.toArray(valuesAsArray); + } + + private static MoreLikeThisQueryBuilder.Item[] toArray(MoreLikeThisQueryBuilder.Item... values) { + return values; + } + + protected ResultsMapper getResultsMapper() { + return resultsMapper; + } + + public static String readFileFromClasspath(String url) { + StringBuilder stringBuilder = new StringBuilder(); + + BufferedReader bufferedReader = null; + + try { + ClassPathResource classPathResource = new ClassPathResource(url); + InputStreamReader inputStreamReader = new InputStreamReader(classPathResource.getInputStream()); + bufferedReader = new BufferedReader(inputStreamReader); + String line; + + String lineSeparator = System.getProperty("line.separator"); + while ((line = bufferedReader.readLine()) != null) { + stringBuilder.append(line).append(lineSeparator); + } + } catch (Exception e) { + logger.debug(String.format("Failed to load file from url: %s: %s", url, e.getMessage())); + return null; + } finally { + if (bufferedReader != null) + try { + bufferedReader.close(); + } catch (IOException e) { + logger.debug(String.format("Unable to close buffered reader.. %s", e.getMessage())); + } + } + + return stringBuilder.toString(); + } + + public SearchResponse suggest(SuggestBuilder suggestion, String... indices) { + return client.prepareSearch(indices).suggest(suggestion).get(); + } + + public SearchResponse suggest(SuggestBuilder suggestion, Class clazz) { + return suggest(suggestion, retrieveIndexNameFromPersistentEntity(clazz)); + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentEntity.java b/src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentEntity.java index 2769189f2..787d1ac79 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentEntity.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/mapping/ElasticsearchPersistentEntity.java @@ -1,52 +1,52 @@ -/* - * Copyright 2013-2017 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 - * - * http://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.mapping; - -import org.springframework.data.mapping.PersistentEntity; - -/** - * ElasticsearchPersistentEntity - * - * @author Rizwan Idrees - * @author Mohsin Husen - * @author Mark Paluch - */ -public interface ElasticsearchPersistentEntity extends PersistentEntity { - - String getIndexName(); - - String getIndexType(); - - short getShards(); - - short getReplicas(); - - boolean isUseServerConfiguration(); - - String getRefreshInterval(); - - String getIndexStoreType(); - - ElasticsearchPersistentProperty getVersionProperty(); - - String getParentType(); - - ElasticsearchPersistentProperty getParentIdProperty(); - - String settingPath(); - - boolean isCreateIndexAndMapping(); -} +/* + * Copyright 2013-2017 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 + * + * http://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.mapping; + +import org.springframework.data.mapping.PersistentEntity; + +/** + * ElasticsearchPersistentEntity + * + * @author Rizwan Idrees + * @author Mohsin Husen + * @author Mark Paluch + */ +public interface ElasticsearchPersistentEntity extends PersistentEntity { + + String getIndexName(); + + String getIndexType(); + + short getShards(); + + short getReplicas(); + + boolean isUseServerConfiguration(); + + String getRefreshInterval(); + + String getIndexStoreType(); + + ElasticsearchPersistentProperty getVersionProperty(); + + String getParentType(); + + ElasticsearchPersistentProperty getParentIdProperty(); + + String settingPath(); + + boolean isCreateIndexAndMapping(); +} diff --git a/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentEntity.java b/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentEntity.java index 9839748d3..18e685ddb 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentEntity.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentEntity.java @@ -1,171 +1,171 @@ -/* - * Copyright 2013-2017 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 - * - * http://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.mapping; - -import static org.springframework.util.StringUtils.*; - -import java.util.Locale; -import java.util.Optional; - -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.expression.BeanFactoryAccessor; -import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.data.elasticsearch.annotations.Document; -import org.springframework.data.elasticsearch.annotations.Parent; -import org.springframework.data.elasticsearch.annotations.Setting; -import org.springframework.data.mapping.model.BasicPersistentEntity; -import org.springframework.data.util.TypeInformation; -import org.springframework.expression.Expression; -import org.springframework.expression.ParserContext; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.util.Assert; - -/** - * Elasticsearch specific {@link org.springframework.data.mapping.PersistentEntity} implementation holding - * - * @param - * @author Rizwan Idrees - * @author Mohsin Husen - * @author Mark Paluch - */ -public class SimpleElasticsearchPersistentEntity extends BasicPersistentEntity - implements ElasticsearchPersistentEntity, ApplicationContextAware { - - private final StandardEvaluationContext context; - private final SpelExpressionParser parser; - - private String indexName; - private String indexType; - private boolean useServerConfiguration; - private short shards; - private short replicas; - private String refreshInterval; - private String indexStoreType; - private String parentType; - private ElasticsearchPersistentProperty parentIdProperty; - private String settingPath; - private boolean createIndexAndMapping; - - public SimpleElasticsearchPersistentEntity(TypeInformation typeInformation) { - super(typeInformation); - this.context = new StandardEvaluationContext(); - this.parser = new SpelExpressionParser(); - - Class clazz = typeInformation.getType(); - if (clazz.isAnnotationPresent(Document.class)) { - Document document = clazz.getAnnotation(Document.class); - Assert.hasText(document.indexName(), - " Unknown indexName. Make sure the indexName is defined. e.g @Document(indexName=\"foo\")"); - this.indexName = document.indexName(); - this.indexType = hasText(document.type()) ? document.type() : clazz.getSimpleName().toLowerCase(Locale.ENGLISH); - this.useServerConfiguration = document.useServerConfiguration(); - this.shards = document.shards(); - this.replicas = document.replicas(); - this.refreshInterval = document.refreshInterval(); - this.indexStoreType = document.indexStoreType(); - this.createIndexAndMapping = document.createIndex(); - } - if (clazz.isAnnotationPresent(Setting.class)) { - this.settingPath = typeInformation.getType().getAnnotation(Setting.class).settingPath(); - } - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - context.addPropertyAccessor(new BeanFactoryAccessor()); - context.setBeanResolver(new BeanFactoryResolver(applicationContext)); - context.setRootObject(applicationContext); - } - - @Override - public String getIndexName() { - Expression expression = parser.parseExpression(indexName, ParserContext.TEMPLATE_EXPRESSION); - return expression.getValue(context, String.class); - } - - @Override - public String getIndexType() { - Expression expression = parser.parseExpression(indexType, ParserContext.TEMPLATE_EXPRESSION); - return expression.getValue(context, String.class); - } - - @Override - public String getIndexStoreType() { - return indexStoreType; - } - - @Override - public short getShards() { - return shards; - } - - @Override - public short getReplicas() { - return replicas; - } - - @Override - public boolean isUseServerConfiguration() { - return useServerConfiguration; - } - - @Override - public String getRefreshInterval() { - return refreshInterval; - } - - @Override - public String getParentType() { - return parentType; - } - - @Override - public ElasticsearchPersistentProperty getParentIdProperty() { - return parentIdProperty; - } - - @Override - public String settingPath() { - return settingPath; - } - - @Override - public boolean isCreateIndexAndMapping() { - return createIndexAndMapping; - } - - @Override - public void addPersistentProperty(ElasticsearchPersistentProperty property) { - super.addPersistentProperty(property); - - Parent annotation = property.findAnnotation(Parent.class); - - if (annotation != null) { - Assert.isNull(this.parentIdProperty, "Only one field can hold a @Parent annotation"); - Assert.isNull(this.parentType, "Only one field can hold a @Parent annotation"); - Assert.isTrue(property.getType() == String.class, "Parent ID property should be String"); - this.parentIdProperty = property; - this.parentType = annotation.type(); - } - - if (property.isVersionProperty()) { - Assert.isTrue(property.getType() == Long.class, "Version property should be Long"); - } - } -} +/* + * Copyright 2013-2017 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 + * + * http://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.mapping; + +import static org.springframework.util.StringUtils.*; + +import java.util.Locale; +import java.util.Optional; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.expression.BeanFactoryAccessor; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.data.elasticsearch.annotations.Document; +import org.springframework.data.elasticsearch.annotations.Parent; +import org.springframework.data.elasticsearch.annotations.Setting; +import org.springframework.data.mapping.model.BasicPersistentEntity; +import org.springframework.data.util.TypeInformation; +import org.springframework.expression.Expression; +import org.springframework.expression.ParserContext; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.util.Assert; + +/** + * Elasticsearch specific {@link org.springframework.data.mapping.PersistentEntity} implementation holding + * + * @param + * @author Rizwan Idrees + * @author Mohsin Husen + * @author Mark Paluch + */ +public class SimpleElasticsearchPersistentEntity extends BasicPersistentEntity + implements ElasticsearchPersistentEntity, ApplicationContextAware { + + private final StandardEvaluationContext context; + private final SpelExpressionParser parser; + + private String indexName; + private String indexType; + private boolean useServerConfiguration; + private short shards; + private short replicas; + private String refreshInterval; + private String indexStoreType; + private String parentType; + private ElasticsearchPersistentProperty parentIdProperty; + private String settingPath; + private boolean createIndexAndMapping; + + public SimpleElasticsearchPersistentEntity(TypeInformation typeInformation) { + super(typeInformation); + this.context = new StandardEvaluationContext(); + this.parser = new SpelExpressionParser(); + + Class clazz = typeInformation.getType(); + if (clazz.isAnnotationPresent(Document.class)) { + Document document = clazz.getAnnotation(Document.class); + Assert.hasText(document.indexName(), + " Unknown indexName. Make sure the indexName is defined. e.g @Document(indexName=\"foo\")"); + this.indexName = document.indexName(); + this.indexType = hasText(document.type()) ? document.type() : clazz.getSimpleName().toLowerCase(Locale.ENGLISH); + this.useServerConfiguration = document.useServerConfiguration(); + this.shards = document.shards(); + this.replicas = document.replicas(); + this.refreshInterval = document.refreshInterval(); + this.indexStoreType = document.indexStoreType(); + this.createIndexAndMapping = document.createIndex(); + } + if (clazz.isAnnotationPresent(Setting.class)) { + this.settingPath = typeInformation.getType().getAnnotation(Setting.class).settingPath(); + } + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + context.addPropertyAccessor(new BeanFactoryAccessor()); + context.setBeanResolver(new BeanFactoryResolver(applicationContext)); + context.setRootObject(applicationContext); + } + + @Override + public String getIndexName() { + Expression expression = parser.parseExpression(indexName, ParserContext.TEMPLATE_EXPRESSION); + return expression.getValue(context, String.class); + } + + @Override + public String getIndexType() { + Expression expression = parser.parseExpression(indexType, ParserContext.TEMPLATE_EXPRESSION); + return expression.getValue(context, String.class); + } + + @Override + public String getIndexStoreType() { + return indexStoreType; + } + + @Override + public short getShards() { + return shards; + } + + @Override + public short getReplicas() { + return replicas; + } + + @Override + public boolean isUseServerConfiguration() { + return useServerConfiguration; + } + + @Override + public String getRefreshInterval() { + return refreshInterval; + } + + @Override + public String getParentType() { + return parentType; + } + + @Override + public ElasticsearchPersistentProperty getParentIdProperty() { + return parentIdProperty; + } + + @Override + public String settingPath() { + return settingPath; + } + + @Override + public boolean isCreateIndexAndMapping() { + return createIndexAndMapping; + } + + @Override + public void addPersistentProperty(ElasticsearchPersistentProperty property) { + super.addPersistentProperty(property); + + Parent annotation = property.findAnnotation(Parent.class); + + if (annotation != null) { + Assert.isNull(this.parentIdProperty, "Only one field can hold a @Parent annotation"); + Assert.isNull(this.parentType, "Only one field can hold a @Parent annotation"); + Assert.isTrue(property.getType() == String.class, "Parent ID property should be String"); + this.parentIdProperty = property; + this.parentType = annotation.type(); + } + + if (property.isVersionProperty()) { + Assert.isTrue(property.getType() == Long.class, "Version property should be Long"); + } + } +} diff --git a/src/main/java/org/springframework/data/elasticsearch/repository/support/MappingElasticsearchEntityInformation.java b/src/main/java/org/springframework/data/elasticsearch/repository/support/MappingElasticsearchEntityInformation.java index 34dbd6655..b1e82b08b 100644 --- a/src/main/java/org/springframework/data/elasticsearch/repository/support/MappingElasticsearchEntityInformation.java +++ b/src/main/java/org/springframework/data/elasticsearch/repository/support/MappingElasticsearchEntityInformation.java @@ -1,94 +1,94 @@ -/* - * Copyright 2013-2017 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 - * - * http://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.repository.support; - -import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; -import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; -import org.springframework.data.repository.core.support.PersistentEntityInformation; -import org.springframework.util.Assert; - -/** - * Elasticsearch specific implementation of - * {@link org.springframework.data.repository.core.support.AbstractEntityInformation} - * - * @param - * @param - * @author Rizwan Idrees - * @author Mohsin Husen - * @author Ryan Henszey - * @author Oliver Gierke - * @author Mark Paluch - * @author Christoph Strobl - */ -public class MappingElasticsearchEntityInformation extends PersistentEntityInformation - implements ElasticsearchEntityInformation { - - private final ElasticsearchPersistentEntity entityMetadata; - private final String indexName; - private final String type; - - public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity entity) { - this(entity, entity.getIndexName(), entity.getIndexType()); - } - - public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity entity, String indexName, String type) { - super(entity); - - Assert.notNull(indexName, "IndexName must not be null!"); - Assert.notNull(type, "IndexType must not be null!"); - - this.entityMetadata = entity; - this.indexName = indexName; - this.type = type; - } - - @Override - public String getIdAttribute() { - return entityMetadata.getRequiredIdProperty().getFieldName(); - } - - @Override - public String getIndexName() { - return indexName; - } - - @Override - public String getType() { - return type; - } - - @Override - public Long getVersion(T entity) { - - ElasticsearchPersistentProperty versionProperty = entityMetadata.getVersionProperty(); - try { - return versionProperty != null ? (Long) entityMetadata.getPropertyAccessor(entity).getProperty(versionProperty) : null; - } catch (Exception e) { - throw new IllegalStateException("failed to load version field", e); - } - } - - @Override - public String getParentId(T entity) { - - ElasticsearchPersistentProperty parentProperty = entityMetadata.getParentIdProperty(); - try { - return parentProperty != null ? (String) entityMetadata.getPropertyAccessor(entity).getProperty(parentProperty) : null; - } catch (Exception e) { - throw new IllegalStateException("failed to load parent ID: " + e, e); - } - } -} +/* + * Copyright 2013-2017 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 + * + * http://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.repository.support; + +import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; +import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; +import org.springframework.data.repository.core.support.PersistentEntityInformation; +import org.springframework.util.Assert; + +/** + * Elasticsearch specific implementation of + * {@link org.springframework.data.repository.core.support.AbstractEntityInformation} + * + * @param + * @param + * @author Rizwan Idrees + * @author Mohsin Husen + * @author Ryan Henszey + * @author Oliver Gierke + * @author Mark Paluch + * @author Christoph Strobl + */ +public class MappingElasticsearchEntityInformation extends PersistentEntityInformation + implements ElasticsearchEntityInformation { + + private final ElasticsearchPersistentEntity entityMetadata; + private final String indexName; + private final String type; + + public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity entity) { + this(entity, entity.getIndexName(), entity.getIndexType()); + } + + public MappingElasticsearchEntityInformation(ElasticsearchPersistentEntity entity, String indexName, String type) { + super(entity); + + Assert.notNull(indexName, "IndexName must not be null!"); + Assert.notNull(type, "IndexType must not be null!"); + + this.entityMetadata = entity; + this.indexName = indexName; + this.type = type; + } + + @Override + public String getIdAttribute() { + return entityMetadata.getRequiredIdProperty().getFieldName(); + } + + @Override + public String getIndexName() { + return indexName; + } + + @Override + public String getType() { + return type; + } + + @Override + public Long getVersion(T entity) { + + ElasticsearchPersistentProperty versionProperty = entityMetadata.getVersionProperty(); + try { + return versionProperty != null ? (Long) entityMetadata.getPropertyAccessor(entity).getProperty(versionProperty) : null; + } catch (Exception e) { + throw new IllegalStateException("failed to load version field", e); + } + } + + @Override + public String getParentId(T entity) { + + ElasticsearchPersistentProperty parentProperty = entityMetadata.getParentIdProperty(); + try { + return parentProperty != null ? (String) entityMetadata.getPropertyAccessor(entity).getProperty(parentProperty) : null; + } catch (Exception e) { + throw new IllegalStateException("failed to load parent ID: " + e, e); + } + } +}