basic mechanism for term facets

This commit is contained in:
akonczak 2013-05-20 09:47:26 +01:00
parent b421cb5044
commit b2572d4950
29 changed files with 1633 additions and 512 deletions

5
.gitignore vendored
View File

@ -3,8 +3,11 @@ atlassian-ide-plugin.xml
## Ignore svn files
.svn
## ignore any target dir
target
data
##ignore only top level data dir - local node data files for unit tests
/data
## Ignore project files created by Eclipse
.settings

View File

@ -20,20 +20,26 @@ import java.lang.annotation.*;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface Field {
String type() default "";
String type() default "";
String index() default "";
String index() default "";
boolean store() default false;
boolean store() default false;
String searchAnalyzer() default "";
String searchAnalyzer() default "";
String indexAnalyzer() default "";
String indexAnalyzer() default "";
boolean facetable() default false;
boolean sortable() default false;
}

View File

@ -84,7 +84,7 @@ public interface ElasticsearchOperations {
* @param clazz
* @return
*/
<T> Page<T> queryForPage(SearchQuery query, Class<T> clazz);
<T> FacetedPage<T> queryForPage(SearchQuery query, Class<T> clazz);
/**
* Execute the query against elasticsearch and return result as {@link Page}
@ -93,7 +93,7 @@ public interface ElasticsearchOperations {
* @param resultsMapper
* @return
*/
<T> Page<T> queryForPage(SearchQuery query, ResultsMapper<T> resultsMapper);
<T> FacetedPage<T> queryForPage(SearchQuery query, ResultsMapper<T> resultsMapper);
/**
* Execute the query against elasticsearch and return result as {@link Page}
@ -111,7 +111,7 @@ public interface ElasticsearchOperations {
* @param clazz
* @return
*/
<T> Page<T> queryForPage(StringQuery query, Class<T> clazz);
<T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz);
/**
* Execute the criteria query against elasticsearch and return result as {@link List}

View File

@ -15,6 +15,7 @@
*/
package org.springframework.data.elasticsearch.core;
import org.apache.commons.collections.CollectionUtils;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
@ -33,19 +34,20 @@ import org.elasticsearch.client.Requests;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.facet.Facet;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
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.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.facet.FacetMapper;
import org.springframework.data.elasticsearch.core.facet.FacetResult;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.query.*;
@ -69,443 +71,460 @@ import static org.springframework.data.elasticsearch.core.MappingBuilder.buildMa
/**
* ElasticsearchTemplate
*
*
* @author Rizwan Idrees
* @author Mohsin Husen
*/
public class ElasticsearchTemplate implements ElasticsearchOperations {
private Client client;
private ElasticsearchConverter elasticsearchConverter;
private ObjectMapper objectMapper = new ObjectMapper();
private Client client;
private ElasticsearchConverter elasticsearchConverter;
private ObjectMapper objectMapper = new ObjectMapper();
{
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
{
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
public ElasticsearchTemplate(Client client) {
this(client, null);
}
public ElasticsearchTemplate(Client client) {
this(client, null);
}
public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter) {
this.client = client;
this.elasticsearchConverter = (elasticsearchConverter == null) ? new MappingElasticsearchConverter(
new SimpleElasticsearchMappingContext()) : elasticsearchConverter;
}
public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter) {
this.client = client;
this.elasticsearchConverter = (elasticsearchConverter == null) ? new MappingElasticsearchConverter(
new SimpleElasticsearchMappingContext()) : elasticsearchConverter;
}
@Override
public <T> boolean createIndex(Class<T> clazz) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
return createIndexIfNotCreated(clazz);
}
@Override
public <T> boolean createIndex(Class<T> clazz) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
return createIndexIfNotCreated(clazz);
}
@Override
public <T> boolean putMapping(Class<T> clazz) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
PutMappingRequestBuilder requestBuilder = client.admin().indices()
.preparePutMapping(persistentEntity.getIndexName()).setType(persistentEntity.getIndexType());
@Override
public <T> boolean putMapping(Class<T> clazz) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
PutMappingRequestBuilder requestBuilder = client.admin().indices()
.preparePutMapping(persistentEntity.getIndexName()).setType(persistentEntity.getIndexType());
try {
XContentBuilder xContentBuilder = buildMapping(clazz, persistentEntity.getIndexType(), persistentEntity
.getIdProperty().getFieldName());
return requestBuilder.setSource(xContentBuilder).execute().actionGet().isAcknowledged();
} catch (Exception e) {
throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
}
}
try {
XContentBuilder xContentBuilder = buildMapping(clazz, persistentEntity.getIndexType(), persistentEntity
.getIdProperty().getFieldName());
return requestBuilder.setSource(xContentBuilder).execute().actionGet().isAcknowledged();
} catch (Exception e) {
throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
}
}
@Override
public ElasticsearchConverter getElasticsearchConverter() {
return elasticsearchConverter;
}
@Override
public ElasticsearchConverter getElasticsearchConverter() {
return elasticsearchConverter;
}
@Override
public <T> T queryForObject(GetQuery query, Class<T> clazz) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
GetResponse response = client
.prepareGet(persistentEntity.getIndexName(), persistentEntity.getIndexType(), query.getId()).execute()
.actionGet();
return mapResult(response.getSourceAsString(), clazz);
}
@Override
public <T> T queryForObject(GetQuery query, Class<T> clazz) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
GetResponse response = client
.prepareGet(persistentEntity.getIndexName(), persistentEntity.getIndexType(), query.getId()).execute()
.actionGet();
return mapResult(response.getSourceAsString(), clazz);
}
@Override
public <T> T queryForObject(CriteriaQuery query, Class<T> clazz) {
Page<T> 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> T queryForObject(CriteriaQuery query, Class<T> clazz) {
Page<T> 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> T queryForObject(StringQuery query, Class<T> clazz) {
Page<T> 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> T queryForObject(StringQuery query, Class<T> clazz) {
Page<T> 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> Page<T> queryForPage(SearchQuery query, Class<T> clazz) {
SearchResponse response = doSearch(prepareSearch(query, clazz), query.getQuery(), query.getFilter(),
query.getElasticsearchSort());
return mapResults(response, clazz, query.getPageable());
}
@Override
public <T> FacetedPage<T> queryForPage(SearchQuery query, Class<T> clazz) {
SearchResponse response = doSearch(prepareSearch(query, clazz), query);
return mapResults(response, clazz, query.getPageable());
}
@Override
public <T> Page<T> queryForPage(SearchQuery query, ResultsMapper<T> resultsMapper) {
SearchResponse response = doSearch(prepareSearch(query), query.getQuery(), query.getFilter(),
query.getElasticsearchSort());
return resultsMapper.mapResults(response);
}
@Override
public <T> FacetedPage<T> queryForPage(SearchQuery query, ResultsMapper<T> resultsMapper) {
SearchResponse response = doSearch(prepareSearch(query), query);
return resultsMapper.mapResults(response);
}
@Override
public <T> List<T> queryForList(CriteriaQuery query, Class<T> clazz) {
return queryForPage(query, clazz).getContent();
}
@Override
public <T> List<T> queryForList(CriteriaQuery query, Class<T> clazz) {
return queryForPage(query, clazz).getContent();
}
@Override
public <T> List<T> queryForList(StringQuery query, Class<T> clazz) {
return queryForPage(query, clazz).getContent();
}
@Override
public <T> List<T> queryForList(StringQuery query, Class<T> clazz) {
return queryForPage(query, clazz).getContent();
}
@Override
public <T> List<String> queryForIds(SearchQuery query) {
SearchRequestBuilder request = prepareSearch(query).setQuery(query.getQuery()).setNoFields();
if (query.getFilter() != null) {
request.setFilter(query.getFilter());
}
SearchResponse response = request.execute().actionGet();
return extractIds(response);
}
@Override
public <T> List<String> queryForIds(SearchQuery query) {
SearchRequestBuilder request = prepareSearch(query).setQuery(query.getQuery()).setNoFields();
if (query.getFilter() != null) {
request.setFilter(query.getFilter());
}
SearchResponse response = request.execute().actionGet();
return extractIds(response);
}
@Override
public <T> Page<T> queryForPage(CriteriaQuery criteriaQuery, Class<T> clazz) {
QueryBuilder query = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria());
SearchResponse response = prepareSearch(criteriaQuery, clazz).setQuery(query).execute().actionGet();
return mapResults(response, clazz, criteriaQuery.getPageable());
}
@Override
public <T> Page<T> queryForPage(CriteriaQuery criteriaQuery, Class<T> clazz) {
QueryBuilder query = new CriteriaQueryProcessor().createQueryFromCriteria(criteriaQuery.getCriteria());
SearchResponse response = prepareSearch(criteriaQuery, clazz).setQuery(query).execute().actionGet();
return mapResults(response, clazz, criteriaQuery.getPageable());
}
@Override
public <T> Page<T> queryForPage(StringQuery query, Class<T> clazz) {
SearchResponse response = prepareSearch(query, clazz).setQuery(query.getSource()).execute().actionGet();
return mapResults(response, clazz, query.getPageable());
}
@Override
public <T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz) {
SearchResponse response = prepareSearch(query, clazz).setQuery(query.getSource()).execute().actionGet();
return mapResults(response, clazz, query.getPageable());
}
@Override
public <T> long count(SearchQuery query, Class<T> clazz) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
CountRequestBuilder countRequestBuilder = client.prepareCount(persistentEntity.getIndexName()).setTypes(
persistentEntity.getIndexType());
if (query.getQuery() != null) {
countRequestBuilder.setQuery(query.getQuery());
}
return countRequestBuilder.execute().actionGet().getCount();
}
@Override
public <T> long count(SearchQuery query, Class<T> clazz) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
CountRequestBuilder countRequestBuilder = client.prepareCount(persistentEntity.getIndexName()).setTypes(
persistentEntity.getIndexType());
if (query.getQuery() != null) {
countRequestBuilder.setQuery(query.getQuery());
}
return countRequestBuilder.execute().actionGet().getCount();
}
@Override
public String index(IndexQuery query) {
return prepareIndex(query).execute().actionGet().getId();
}
@Override
public String index(IndexQuery query) {
return prepareIndex(query).execute().actionGet().getId();
}
@Override
public void bulkIndex(List<IndexQuery> queries) {
BulkRequestBuilder bulkRequest = client.prepareBulk();
for (IndexQuery query : queries) {
bulkRequest.add(prepareIndex(query));
}
BulkResponse bulkResponse = bulkRequest.execute().actionGet();
if (bulkResponse.hasFailures()) {
Map<String, String> failedDocuments = new HashMap<String, String>();
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 void bulkIndex(List<IndexQuery> queries) {
BulkRequestBuilder bulkRequest = client.prepareBulk();
for (IndexQuery query : queries) {
bulkRequest.add(prepareIndex(query));
}
BulkResponse bulkResponse = bulkRequest.execute().actionGet();
if (bulkResponse.hasFailures()) {
Map<String, String> failedDocuments = new HashMap<String, String>();
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 <T> boolean indexExists(Class<T> clazz) {
return indexExists(getPersistentEntityFor(clazz).getIndexName());
}
@Override
public <T> boolean indexExists(Class<T> clazz) {
return indexExists(getPersistentEntityFor(clazz).getIndexName());
}
@Override
public <T> boolean deleteIndex(Class<T> clazz) {
String indexName = getPersistentEntityFor(clazz).getIndexName();
if (indexExists(indexName)) {
return client.admin().indices().delete(new DeleteIndexRequest(indexName)).actionGet().isAcknowledged();
}
return false;
}
@Override
public <T> boolean deleteIndex(Class<T> clazz) {
String indexName = getPersistentEntityFor(clazz).getIndexName();
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(String indexName, String type, String id) {
return client.prepareDelete(indexName, type, id).execute().actionGet().getId();
}
@Override
public <T> String delete(Class<T> clazz, String id) {
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
return delete(persistentEntity.getIndexName(), persistentEntity.getIndexType(), id);
}
@Override
public <T> String delete(Class<T> clazz, String id) {
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
return delete(persistentEntity.getIndexName(), persistentEntity.getIndexType(), id);
}
@Override
public <T> void delete(DeleteQuery deleteQuery, Class<T> clazz) {
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
client.prepareDeleteByQuery(persistentEntity.getIndexName()).setTypes(persistentEntity.getIndexType())
.setQuery(deleteQuery.getQuery()).execute().actionGet();
}
@Override
public <T> void delete(DeleteQuery deleteQuery, Class<T> clazz) {
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
client.prepareDeleteByQuery(persistentEntity.getIndexName()).setTypes(persistentEntity.getIndexType())
.setQuery(deleteQuery.getQuery()).execute().actionGet();
}
@Override
public String scan(SearchQuery searchQuery, long scrollTimeInMillis, boolean noFields) {
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");
@Override
public String scan(SearchQuery searchQuery, long scrollTimeInMillis, boolean noFields) {
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");
SearchRequestBuilder requestBuilder = client.prepareSearch(toArray(searchQuery.getIndices())).setSearchType(SCAN)
.setQuery(searchQuery.getQuery()).setTypes(toArray(searchQuery.getTypes()))
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).setFrom(0)
.setSize(searchQuery.getPageable().getPageSize());
SearchRequestBuilder requestBuilder = client.prepareSearch(toArray(searchQuery.getIndices())).setSearchType(SCAN)
.setQuery(searchQuery.getQuery()).setTypes(toArray(searchQuery.getTypes()))
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).setFrom(0)
.setSize(searchQuery.getPageable().getPageSize());
if (searchQuery.getFilter() != null) {
requestBuilder.setFilter(searchQuery.getFilter());
}
if (searchQuery.getFilter() != null) {
requestBuilder.setFilter(searchQuery.getFilter());
}
if (noFields) {
requestBuilder.setNoFields();
}
return requestBuilder.execute().actionGet().getScrollId();
}
if (noFields) {
requestBuilder.setNoFields();
}
return requestBuilder.execute().actionGet().getScrollId();
}
@Override
public <T> Page<T> scroll(String scrollId, long scrollTimeInMillis, ResultsMapper<T> resultsMapper) {
SearchResponse response = client.prepareSearchScroll(scrollId)
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute().actionGet();
return resultsMapper.mapResults(response);
}
@Override
public <T> Page<T> scroll(String scrollId, long scrollTimeInMillis, ResultsMapper<T> resultsMapper) {
SearchResponse response = client.prepareSearchScroll(scrollId)
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute().actionGet();
return resultsMapper.mapResults(response);
}
@Override
public <T> Page<T> moreLikeThis(MoreLikeThisQuery query, Class<T> clazz) {
int startRecord = 0;
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName() : persistentEntity.getIndexName();
String type = isNotBlank(query.getType()) ? query.getType() : persistentEntity.getIndexType();
@Override
public <T> Page<T> moreLikeThis(MoreLikeThisQuery query, Class<T> clazz) {
int startRecord = 0;
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");
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");
MoreLikeThisRequestBuilder requestBuilder = client.prepareMoreLikeThis(indexName, type, query.getId());
MoreLikeThisRequestBuilder requestBuilder = client.prepareMoreLikeThis(indexName, type, query.getId());
if (query.getPageable() != null) {
startRecord = query.getPageable().getPageNumber() * query.getPageable().getPageSize();
requestBuilder.setSearchSize(query.getPageable().getPageSize());
}
requestBuilder.setSearchFrom(startRecord);
if (query.getPageable() != null) {
startRecord = query.getPageable().getPageNumber() * query.getPageable().getPageSize();
requestBuilder.setSearchSize(query.getPageable().getPageSize());
}
requestBuilder.setSearchFrom(startRecord);
if (isNotEmpty(query.getSearchIndices())) {
requestBuilder.setSearchIndices(toArray(query.getSearchIndices()));
}
if (isNotEmpty(query.getSearchTypes())) {
requestBuilder.setSearchTypes(toArray(query.getSearchTypes()));
}
if (isNotEmpty(query.getFields())) {
requestBuilder.setField(toArray(query.getFields()));
}
if (isNotBlank(query.getRouting())) {
requestBuilder.setRouting(query.getRouting());
}
if (query.getPercentTermsToMatch() != null) {
requestBuilder.setPercentTermsToMatch(query.getPercentTermsToMatch());
}
if (query.getMinTermFreq() != null) {
requestBuilder.setMinTermFreq(query.getMinTermFreq());
}
if (query.getMaxQueryTerms() != null) {
requestBuilder.maxQueryTerms(query.getMaxQueryTerms());
}
if (isNotEmpty(query.getStopWords())) {
requestBuilder.setStopWords(toArray(query.getStopWords()));
}
if (query.getMinDocFreq() != null) {
requestBuilder.setMinDocFreq(query.getMinDocFreq());
}
if (query.getMaxDocFreq() != null) {
requestBuilder.setMaxDocFreq(query.getMaxDocFreq());
}
if (query.getMinWordLen() != null) {
requestBuilder.setMinWordLen(query.getMinWordLen());
}
if (query.getMaxWordLen() != null) {
requestBuilder.setMaxWordLen(query.getMaxWordLen());
}
if (query.getBoostTerms() != null) {
requestBuilder.setBoostTerms(query.getBoostTerms());
}
if (isNotEmpty(query.getSearchIndices())) {
requestBuilder.setSearchIndices(toArray(query.getSearchIndices()));
}
if (isNotEmpty(query.getSearchTypes())) {
requestBuilder.setSearchTypes(toArray(query.getSearchTypes()));
}
if (isNotEmpty(query.getFields())) {
requestBuilder.setField(toArray(query.getFields()));
}
if (isNotBlank(query.getRouting())) {
requestBuilder.setRouting(query.getRouting());
}
if (query.getPercentTermsToMatch() != null) {
requestBuilder.setPercentTermsToMatch(query.getPercentTermsToMatch());
}
if (query.getMinTermFreq() != null) {
requestBuilder.setMinTermFreq(query.getMinTermFreq());
}
if (query.getMaxQueryTerms() != null) {
requestBuilder.maxQueryTerms(query.getMaxQueryTerms());
}
if (isNotEmpty(query.getStopWords())) {
requestBuilder.setStopWords(toArray(query.getStopWords()));
}
if (query.getMinDocFreq() != null) {
requestBuilder.setMinDocFreq(query.getMinDocFreq());
}
if (query.getMaxDocFreq() != null) {
requestBuilder.setMaxDocFreq(query.getMaxDocFreq());
}
if (query.getMinWordLen() != null) {
requestBuilder.setMinWordLen(query.getMinWordLen());
}
if (query.getMaxWordLen() != null) {
requestBuilder.setMaxWordLen(query.getMaxWordLen());
}
if (query.getBoostTerms() != null) {
requestBuilder.setBoostTerms(query.getBoostTerms());
}
SearchResponse response = requestBuilder.execute().actionGet();
return mapResults(response, clazz, query.getPageable());
}
SearchResponse response = requestBuilder.execute().actionGet();
return mapResults(response, clazz, query.getPageable());
}
private SearchResponse doSearch(SearchRequestBuilder searchRequest, QueryBuilder query, FilterBuilder filter,
SortBuilder sortBuilder) {
if (filter != null) {
searchRequest.setFilter(filter);
}
private SearchResponse doSearch(SearchRequestBuilder searchRequest, SearchQuery searchQuery) {
if (searchQuery.getFilter() != null) {
searchRequest.setFilter(searchQuery.getFilter());
}
if (sortBuilder != null) {
searchRequest.addSort(sortBuilder);
}
if (searchQuery.getElasticsearchSort() != null) {
searchRequest.addSort(searchQuery.getElasticsearchSort());
}
return searchRequest.setQuery(query).execute().actionGet();
}
if (CollectionUtils.isNotEmpty(searchQuery.getFacets())) {
for (FacetRequest facetRequest : searchQuery.getFacets()) {
FacetBuilder facet = facetRequest.getFacet();
if (facetRequest.applyQueryFilter() && searchQuery.getFilter() != null) {
facet.facetFilter(searchQuery.getFilter());
}
searchRequest.addFacet(facet);
}
}
private <T> boolean createIndexIfNotCreated(Class<T> clazz) {
return indexExists(getPersistentEntityFor(clazz).getIndexName()) || createIndexWithSettings(clazz);
}
return searchRequest.setQuery(searchQuery.getQuery()).execute().actionGet();
}
private boolean indexExists(String indexName) {
return client.admin().indices().exists(indicesExistsRequest(indexName)).actionGet().isExists();
}
private <T> boolean createIndexIfNotCreated(Class<T> clazz) {
return indexExists(getPersistentEntityFor(clazz).getIndexName()) || createIndexWithSettings(clazz);
}
private <T> boolean createIndexWithSettings(Class<T> clazz) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
return client.admin().indices()
.create(Requests.createIndexRequest(persistentEntity.getIndexName()).settings(getSettings(persistentEntity)))
.actionGet().isAcknowledged();
}
private boolean indexExists(String indexName) {
return client.admin().indices().exists(indicesExistsRequest(indexName)).actionGet().isExists();
}
private <T> Map getSettings(ElasticsearchPersistentEntity<T> persistentEntity) {
return new MapBuilder<String, String>().put("index.number_of_shards", String.valueOf(persistentEntity.getShards()))
.put("index.number_of_replicas", String.valueOf(persistentEntity.getReplicas()))
.put("index.refresh_interval", persistentEntity.getRefreshInterval())
.put("index.store.type", persistentEntity.getIndexStoreType()).map();
}
private <T> boolean createIndexWithSettings(Class<T> clazz) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
return client.admin().indices()
.create(Requests.createIndexRequest(persistentEntity.getIndexName()).settings(getSettings(persistentEntity)))
.actionGet().isAcknowledged();
}
private <T> SearchRequestBuilder prepareSearch(Query query, Class<T> clazz) {
if (query.getIndices().isEmpty()) {
query.addIndices(retrieveIndexNameFromPersistentEntity(clazz));
}
if (query.getTypes().isEmpty()) {
query.addTypes(retrieveTypeFromPersistentEntity(clazz));
}
return prepareSearch(query);
}
private <T> Map getSettings(ElasticsearchPersistentEntity<T> persistentEntity) {
return new MapBuilder<String, String>().put("index.number_of_shards", String.valueOf(persistentEntity.getShards()))
.put("index.number_of_replicas", String.valueOf(persistentEntity.getReplicas()))
.put("index.refresh_interval", persistentEntity.getRefreshInterval())
.put("index.store.type", persistentEntity.getIndexStoreType()).map();
}
private SearchRequestBuilder prepareSearch(Query query) {
Assert.notNull(query.getIndices(), "No index defined for Query");
Assert.notNull(query.getTypes(), "No type defined for Query");
private <T> SearchRequestBuilder prepareSearch(Query query, Class<T> clazz) {
if (query.getIndices().isEmpty()) {
query.addIndices(retrieveIndexNameFromPersistentEntity(clazz));
}
if (query.getTypes().isEmpty()) {
query.addTypes(retrieveTypeFromPersistentEntity(clazz));
}
return prepareSearch(query);
}
int startRecord = 0;
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(toArray(query.getIndices()))
.setSearchType(DFS_QUERY_THEN_FETCH).setTypes(toArray(query.getTypes()));
private SearchRequestBuilder prepareSearch(Query query) {
Assert.notNull(query.getIndices(), "No index defined for Query");
Assert.notNull(query.getTypes(), "No type defined for Query");
if (query.getPageable() != null) {
startRecord = query.getPageable().getPageNumber() * query.getPageable().getPageSize();
searchRequestBuilder.setSize(query.getPageable().getPageSize());
}
searchRequestBuilder.setFrom(startRecord);
int startRecord = 0;
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(toArray(query.getIndices()))
.setSearchType(DFS_QUERY_THEN_FETCH).setTypes(toArray(query.getTypes()));
if (!query.getFields().isEmpty()) {
searchRequestBuilder.addFields(toArray(query.getFields()));
}
if (query.getPageable() != null) {
startRecord = query.getPageable().getPageNumber() * query.getPageable().getPageSize();
searchRequestBuilder.setSize(query.getPageable().getPageSize());
}
searchRequestBuilder.setFrom(startRecord);
if (query.getSort() != null) {
for (Sort.Order order : query.getSort()) {
searchRequestBuilder.addSort(order.getProperty(), order.getDirection() == Sort.Direction.DESC ? SortOrder.DESC
: SortOrder.ASC);
}
}
return searchRequestBuilder;
}
if (!query.getFields().isEmpty()) {
searchRequestBuilder.addFields(toArray(query.getFields()));
}
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();
if (query.getSort() != null) {
for (Sort.Order order : query.getSort()) {
searchRequestBuilder.addSort(order.getProperty(), order.getDirection() == Sort.Direction.DESC ? SortOrder.DESC
: SortOrder.ASC);
}
}
return searchRequestBuilder;
}
IndexRequestBuilder indexRequestBuilder = client.prepareIndex(indexName, type, query.getId()).setSource(
objectMapper.writeValueAsString(query.getObject()));
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();
if (query.getVersion() != null) {
indexRequestBuilder.setVersion(query.getVersion());
indexRequestBuilder.setVersionType(EXTERNAL);
}
return indexRequestBuilder;
} catch (IOException e) {
throw new ElasticsearchException("failed to index the document [id: " + query.getId() + "]", e);
}
}
IndexRequestBuilder indexRequestBuilder = client.prepareIndex(indexName, type, query.getId()).setSource(
objectMapper.writeValueAsString(query.getObject()));
public void refresh(String indexName, boolean waitForOperation) {
client.admin().indices().refresh(refreshRequest(indexName).waitForOperations(waitForOperation)).actionGet();
}
if (query.getVersion() != null) {
indexRequestBuilder.setVersion(query.getVersion());
indexRequestBuilder.setVersionType(EXTERNAL);
}
return indexRequestBuilder;
} catch (IOException e) {
throw new ElasticsearchException("failed to index the document [id: " + query.getId() + "]", e);
}
}
public <T> void refresh(Class<T> clazz, boolean waitForOperation) {
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
client.admin().indices()
.refresh(refreshRequest(persistentEntity.getIndexName()).waitForOperations(waitForOperation)).actionGet();
}
public void refresh(String indexName, boolean waitForOperation) {
client.admin().indices().refresh(refreshRequest(indexName).waitForOperations(waitForOperation)).actionGet();
}
private 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().getPersistentEntity(clazz);
}
public <T> void refresh(Class<T> clazz, boolean waitForOperation) {
ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
client.admin().indices()
.refresh(refreshRequest(persistentEntity.getIndexName()).waitForOperations(waitForOperation)).actionGet();
}
private String[] retrieveIndexNameFromPersistentEntity(Class clazz) {
return new String[] { getPersistentEntityFor(clazz).getIndexName() };
}
private 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().getPersistentEntity(clazz);
}
private String[] retrieveTypeFromPersistentEntity(Class clazz) {
return new String[] { getPersistentEntityFor(clazz).getIndexType() };
}
private String[] retrieveIndexNameFromPersistentEntity(Class clazz) {
return new String[]{getPersistentEntityFor(clazz).getIndexName()};
}
private <T> Page<T> mapResults(SearchResponse response, final Class<T> elementType, final Pageable pageable) {
ResultsMapper<T> resultsMapper = new ResultsMapper<T>() {
@Override
public Page<T> mapResults(SearchResponse response) {
long totalHits = response.getHits().totalHits();
List<T> results = new ArrayList<T>();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
results.add(mapResult(hit.sourceAsString(), elementType));
}
}
return new PageImpl<T>(results, pageable, totalHits);
}
};
return resultsMapper.mapResults(response);
}
private String[] retrieveTypeFromPersistentEntity(Class clazz) {
return new String[]{getPersistentEntityFor(clazz).getIndexType()};
}
private List<String> extractIds(SearchResponse response) {
List<String> ids = new ArrayList<String>();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
ids.add(hit.getId());
}
}
return ids;
}
private <T> FacetedPage<T> mapResults(SearchResponse response, final Class<T> elementType, final Pageable pageable) {
ResultsMapper<T> resultsMapper = new ResultsMapper<T>() {
@Override
public FacetedPage<T> mapResults(SearchResponse response) {
long totalHits = response.getHits().totalHits();
List<T> results = new ArrayList<T>();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
results.add(mapResult(hit.sourceAsString(), elementType));
}
}
List<FacetResult> facets = new ArrayList<FacetResult>();
if (response.getFacets() != null) {
for (Facet facet : response.getFacets()) {
FacetResult facetResult = FacetMapper.parse(facet);
if (facetResult != null) {
facets.add(facetResult);
}
}
}
private <T> T mapResult(String source, Class<T> clazz) {
if (isBlank(source)) {
return null;
}
try {
return objectMapper.readValue(source, clazz);
} catch (IOException e) {
throw new ElasticsearchException("failed to map source [ " + source + "] to class " + clazz.getSimpleName(), e);
}
}
return new FacetedPageImpl<T>(results, pageable, totalHits, facets);
}
};
return resultsMapper.mapResults(response);
}
private static String[] toArray(List<String> values) {
String[] valuesAsArray = new String[values.size()];
return values.toArray(valuesAsArray);
private List<String> extractIds(SearchResponse response) {
List<String> ids = new ArrayList<String>();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
ids.add(hit.getId());
}
}
return ids;
}
}
private <T> T mapResult(String source, Class<T> clazz) {
if (isBlank(source)) {
return null;
}
try {
return objectMapper.readValue(source, clazz);
} catch (IOException e) {
throw new ElasticsearchException("failed to map source [ " + source + "] to class " + clazz.getSimpleName(), e);
}
}
private static String[] toArray(List<String> values) {
String[] valuesAsArray = new String[values.size()];
return values.toArray(valuesAsArray);
}
}

View File

@ -0,0 +1,23 @@
package org.springframework.data.elasticsearch.core;
import org.springframework.data.domain.Page;
import org.springframework.data.elasticsearch.core.facet.FacetResult;
import java.util.List;
/**
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public interface FacetedPage<T> extends Page<T> {
boolean hasFacets();
List<FacetResult> getFacets();
FacetResult getFacet(String name);
}

View File

@ -0,0 +1,55 @@
package org.springframework.data.elasticsearch.core;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.facet.FacetResult;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Container for query result and facet results
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class FacetedPageImpl<T> extends PageImpl<T> implements FacetedPage<T> {
private List<FacetResult> facets;
private Map<String, FacetResult> mapOfFacets = new HashMap<String, FacetResult>();
public FacetedPageImpl(List<T> content) {
super(content);
}
public FacetedPageImpl(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public FacetedPageImpl(List<T> content, Pageable pageable, long total, List<FacetResult> facets) {
super(content, pageable, total);
this.facets = facets;
for (FacetResult facet : facets) {
mapOfFacets.put(facet.getName(), facet);
}
}
@Override
public boolean hasFacets() {
return CollectionUtils.isNotEmpty(facets);
}
@Override
public List<FacetResult> getFacets() {
return facets;
}
@Override
public FacetResult getFacet(String name) {
return mapOfFacets.get(name);
}
}

View File

@ -0,0 +1,52 @@
package org.springframework.data.elasticsearch.core;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.facet.FacetResult;
import java.util.List;
import java.util.Map;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class FactedPageImpl<T> extends PageImpl<T> implements FacetedPage<T> {
private List<FacetResult> facets;
private Map<String, FacetResult> mapOfFacets;
public FactedPageImpl(List<T> content) {
super(content);
}
public FactedPageImpl(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public FactedPageImpl(List<T> content, Pageable pageable, long total, List<FacetResult> facets) {
super(content, pageable, total);
this.facets = facets;
for (FacetResult facet : facets) {
mapOfFacets.put(facet.getName(), facet);
}
}
@Override
public boolean hasFacets() {
return CollectionUtils.isNotEmpty(facets);
}
@Override
public List<FacetResult> getFacets() {
return facets;
}
@Override
public FacetResult getFacet(String name) {
return mapOfFacets.get(name);
}
}

View File

@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.core;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.core.query.FacetRequest;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
@ -31,90 +32,177 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
*/
class MappingBuilder {
private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = new SimpleTypeHolder();
public static final String FIELD_STORE = "store";
public static final String FIELD_TYPE = "type";
public static final String FIELD_INDEX = "index";
public static final String FIELD_SEARCH_ANALYZER = "search_analyzer";
public static final String FIELD_INDEX_ANALYZER = "index_analyzer";
public static final String FIELD_PROPERTIES = "properties";
static XContentBuilder buildMapping(Class clazz, String indexType, String idFieldName) throws IOException {
XContentBuilder xContentBuilder = jsonBuilder().startObject().startObject(indexType).startObject("properties");
public static final String INDEX_VALUE_NOT_ANALYZED = "not_analyzed";
public static final String TYPE_VALUE_STRING = "string";
public static final String TYPE_VALUE_OBJECT = "object";
mapEntity(xContentBuilder, clazz, true, idFieldName, EMPTY);
private static SimpleTypeHolder SIMPLE_TYPE_HOLDER = new SimpleTypeHolder();
return xContentBuilder.endObject().endObject().endObject();
}
static XContentBuilder buildMapping(Class clazz, String indexType, String idFieldName) throws IOException {
XContentBuilder xContentBuilder = jsonBuilder().startObject().startObject(indexType).startObject(FIELD_PROPERTIES);
private static void mapEntity(XContentBuilder xContentBuilder, Class clazz, boolean isRootObject, String idFieldName,
String nestedObjectFieldName) throws IOException {
mapEntity(xContentBuilder, clazz, true, idFieldName, EMPTY);
java.lang.reflect.Field[] fields = clazz.getDeclaredFields();
return xContentBuilder.endObject().endObject().endObject();
}
if (!isRootObject && isAnyPropertyAnnotatedAsField(fields)) {
xContentBuilder.startObject(nestedObjectFieldName).field("type", "object").startObject("properties");
}
private static void mapEntity(XContentBuilder xContentBuilder, Class clazz, boolean isRootObject, String idFieldName,
String nestedObjectFieldName) throws IOException {
for (java.lang.reflect.Field field : fields) {
if (isEntity(field)) {
mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName());
}
Field fieldAnnotation = field.getAnnotation(Field.class);
if (isRootObject && fieldAnnotation != null && isIdField(field, idFieldName)) {
applyDefaultIdFieldMapping(xContentBuilder, field);
} else if (fieldAnnotation != null) {
applyFieldAnnotationMapping(xContentBuilder, field, fieldAnnotation);
}
}
java.lang.reflect.Field[] fields = clazz.getDeclaredFields();
if (!isRootObject && isAnyPropertyAnnotatedAsField(fields)) {
xContentBuilder.endObject().endObject();
}
if (!isRootObject && isAnyPropertyAnnotatedAsField(fields)) {
xContentBuilder.startObject(nestedObjectFieldName).field(FIELD_TYPE, TYPE_VALUE_OBJECT).startObject(FIELD_PROPERTIES);
}
}
for (java.lang.reflect.Field field : fields) {
if (isEntity(field)) {
mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName());
}
Field fieldAnnotation = field.getAnnotation(Field.class);
if (isRootObject && fieldAnnotation != null && isIdField(field, idFieldName)) {
applyDefaultIdFieldMapping(xContentBuilder, field);
} else if (fieldAnnotation != null) {
if ((fieldAnnotation.sortable() || fieldAnnotation.facetable()) && TYPE_VALUE_STRING.equals(fieldAnnotation.type())) {
addMultiFieldMapping(xContentBuilder, field, fieldAnnotation);
} else {
addSimpleFieldMapping(xContentBuilder, field, fieldAnnotation);
}
}
}
private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field)
throws IOException {
xContentBuilder.startObject(field.getName()).field("type", "string").field("index", "not_analyzed").endObject();
}
if (!isRootObject && isAnyPropertyAnnotatedAsField(fields)) {
xContentBuilder.endObject().endObject();
}
private static void applyFieldAnnotationMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field,
Field fieldAnnotation) throws IOException {
xContentBuilder.startObject(field.getName());
xContentBuilder.field("store", fieldAnnotation.store());
if (isNotBlank(fieldAnnotation.type())) {
xContentBuilder.field("type", fieldAnnotation.type());
}
if (isNotBlank(fieldAnnotation.index())) {
xContentBuilder.field("index", fieldAnnotation.index());
}
if (isNotBlank(fieldAnnotation.searchAnalyzer())) {
xContentBuilder.field("search_analyzer", fieldAnnotation.searchAnalyzer());
}
if (isNotBlank(fieldAnnotation.indexAnalyzer())) {
xContentBuilder.field("index_analyzer", fieldAnnotation.indexAnalyzer());
}
xContentBuilder.endObject();
}
}
private static boolean isEntity(java.lang.reflect.Field field) {
TypeInformation typeInformation = ClassTypeInformation.from(field.getType());
TypeInformation<?> actualType = typeInformation.getActualType();
boolean isComplexType = actualType == null ? false : !SIMPLE_TYPE_HOLDER.isSimpleType(actualType.getType());
return isComplexType && !actualType.isCollectionLike() && !Map.class.isAssignableFrom(typeInformation.getType());
}
private static void applyDefaultIdFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field)
throws IOException {
xContentBuilder.startObject(field.getName())
.field(FIELD_TYPE, TYPE_VALUE_STRING)
.field(FIELD_INDEX, INDEX_VALUE_NOT_ANALYZED);
xContentBuilder.endObject();
}
private static boolean isAnyPropertyAnnotatedAsField(java.lang.reflect.Field[] fields) {
if (fields != null) {
for (java.lang.reflect.Field field : fields) {
if (field.isAnnotationPresent(Field.class)) {
return true;
}
}
}
return false;
}
/**
* Apply mapping for a single @Field annotation
*
* @param xContentBuilder
* @param field
* @param fieldAnnotation
* @throws IOException
*/
private static void addSimpleFieldMapping(XContentBuilder xContentBuilder, java.lang.reflect.Field field,
Field fieldAnnotation) throws IOException {
xContentBuilder.startObject(field.getName());
xContentBuilder.field(FIELD_STORE, fieldAnnotation.store());
if (isNotBlank(fieldAnnotation.type())) {
xContentBuilder.field(FIELD_TYPE, fieldAnnotation.type());
}
if (isNotBlank(fieldAnnotation.index())) {
xContentBuilder.field(FIELD_INDEX, fieldAnnotation.index());
}
if (isNotBlank(fieldAnnotation.searchAnalyzer())) {
xContentBuilder.field(FIELD_SEARCH_ANALYZER, fieldAnnotation.searchAnalyzer());
}
if (isNotBlank(fieldAnnotation.indexAnalyzer())) {
xContentBuilder.field(FIELD_INDEX_ANALYZER, fieldAnnotation.indexAnalyzer());
}
xContentBuilder.endObject();
}
private static boolean isIdField(java.lang.reflect.Field field, String idFieldName) {
return idFieldName.equals(field.getName());
}
/**
* Multi field mappings for string type fields, support for sorts and facets
*
* @param builder
* @param field
* @param annotation
* @throws IOException
*/
private static void addMultiFieldMapping(XContentBuilder builder, java.lang.reflect.Field field,
Field annotation) throws IOException {
builder.startObject(field.getName());
builder.field(FIELD_TYPE, "multi_field");
builder.startObject("fields");
//add standard field
addSimpleFieldMapping(builder, field, annotation);
//facet field - untouched, not analise, stored
if (annotation.facetable()) {
addFacetMapping(builder, field, annotation);
}
//sort field - lowercase, not analise, stored
if (annotation.sortable()) {
addSortMapping(builder, field, annotation);
}
builder.endObject();
builder.endObject();
}
/**
* Facet field for string type, for other types we don't need it(long, int, double, float)
*
* @param builder
* @param field
* @param annotation
* @throws IOException
*/
private static void addFacetMapping(XContentBuilder builder, java.lang.reflect.Field field, Field annotation) throws IOException {
builder.startObject(FacetRequest.FIELD_UNTOUCHED)
.field(FIELD_TYPE, TYPE_VALUE_STRING)
.field(FIELD_INDEX, INDEX_VALUE_NOT_ANALYZED)
.field(FIELD_STORE, true);
builder.endObject();
}
/**
* Sort field for string type, for other types we don't need it(long, int, double, float)
* value of the field should be converted to lowercase and not analise
*
* @param builder
* @param field
* @param annotation
* @throws IOException
*/
private static void addSortMapping(XContentBuilder builder, java.lang.reflect.Field field, Field annotation) throws IOException {
builder.startObject(FacetRequest.FIELD_SORT)
.field(FIELD_TYPE, TYPE_VALUE_STRING)
.field(FIELD_INDEX, "keyword")
.field(FIELD_STORE, true);
builder.endObject();
}
private static boolean isEntity(java.lang.reflect.Field field) {
TypeInformation typeInformation = ClassTypeInformation.from(field.getType());
TypeInformation<?> actualType = typeInformation.getActualType();
boolean isComplexType = actualType == null ? false : !SIMPLE_TYPE_HOLDER.isSimpleType(actualType.getType());
return isComplexType && !actualType.isCollectionLike() && !Map.class.isAssignableFrom(typeInformation.getType());
}
private static boolean isAnyPropertyAnnotatedAsField(java.lang.reflect.Field[] fields) {
if (fields != null) {
for (java.lang.reflect.Field field : fields) {
if (field.isAnnotationPresent(Field.class)) {
return true;
}
}
}
return false;
}
private static boolean isIdField(java.lang.reflect.Field field, String idFieldName) {
return idFieldName.equals(field.getName());
}
}

View File

@ -25,11 +25,12 @@ import org.springframework.data.domain.Page;
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
*
*/
public interface ResultsMapper<T> {
Page<T> mapResults(SearchResponse response);
FacetedPage<T> mapResults(SearchResponse response);
}

View File

@ -0,0 +1,31 @@
package org.springframework.data.elasticsearch.core.facet;
import org.springframework.util.Assert;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class AbstactFacetResult implements FacetResult {
private final String name;
private final FacetType type;
protected AbstactFacetResult(String name, FacetType type) {
Assert.hasText(name, "Facet name can't be null and should have a value");
this.name = name;
this.type = type;
}
@Override
public String getName() {
return name;
}
@Override
public FacetType getType() {
return type;
}
}

View File

@ -0,0 +1,29 @@
package org.springframework.data.elasticsearch.core.facet;
import org.elasticsearch.search.facet.Facet;
import org.elasticsearch.search.facet.terms.TermsFacet;
import java.util.ArrayList;
import java.util.List;
/**
* @author Artur Konczak
*/
public class FacetMapper {
public static FacetResult parse(Facet facet){
if(facet instanceof TermsFacet){
return parseTerm((TermsFacet) facet);
}
return null;
}
private static FacetResult parseTerm(TermsFacet facet) {
List<Term> terms = new ArrayList<Term>();
for(TermsFacet.Entry entry:facet.getEntries()){
terms.add(new Term(entry.getTerm().toString(),entry.getCount()));
}
return new TermResult(facet.getName(),terms);
}
}

View File

@ -0,0 +1,18 @@
package org.springframework.data.elasticsearch.core.facet;
/**
* Generic interface for all facets
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*
*/
public interface FacetResult {
String getName();
FacetType getType();
}

View File

@ -0,0 +1,7 @@
package org.springframework.data.elasticsearch.core.facet;
public enum FacetType {
term, renage, histogram
}

View File

@ -0,0 +1,28 @@
package org.springframework.data.elasticsearch.core.facet;
/**
* Single term
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class Term {
private String term;
private int count;
public Term(String term, int count) {
this.term = term;
this.count = count;
}
public String getTerm() {
return term;
}
public int getCount() {
return count;
}
}

View File

@ -0,0 +1,28 @@
package org.springframework.data.elasticsearch.core.facet;
import org.springframework.util.Assert;
import java.util.List;
/**
* Basic term facet result
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
* @author Jonathan Yan
*/
public class TermResult extends AbstactFacetResult {
private List<Term> terms;
public TermResult(String name, List<Term> terms) {
super(name, FacetType.term);
this.terms = terms;
}
public List<Term> getTerms() {
return terms;
}
}

View File

@ -0,0 +1,30 @@
package org.springframework.data.elasticsearch.core.query;
import org.springframework.util.Assert;
/**
* @author Artur Konczak
*/
public abstract class AbstractFacetRequest implements FacetRequest {
private String name;
private boolean applyQueryFilter;
public AbstractFacetRequest(String name) {
Assert.hasText(name, "Facet can't be null or empty !!!");
this.name = name;
}
protected String getName(){
return name;
}
public void setApplyQueryFilter(boolean applyQueryFilter) {
this.applyQueryFilter = applyQueryFilter;
}
@Override
public boolean applyQueryFilter() {
return applyQueryFilter;
}
}

View File

@ -0,0 +1,17 @@
package org.springframework.data.elasticsearch.core.query;
import org.elasticsearch.search.facet.FacetBuilder;
/**
* @author Artur Koczak
*/
public interface FacetRequest {
public static final String FIELD_UNTOUCHED = "untouched";
public static final String FIELD_SORT = "sort";
FacetBuilder getFacet();
boolean applyQueryFilter();
}

View File

@ -0,0 +1,31 @@
package org.springframework.data.elasticsearch.core.query;
import org.elasticsearch.search.facet.FacetBuilder;
/**
* @author Artur Konczak
*/
public class NativeFacetRequest implements FacetRequest {
private FacetBuilder facet;
private boolean applyQueryFilter;
public NativeFacetRequest(FacetBuilder facet) {
this(facet, false);
}
public NativeFacetRequest(FacetBuilder facet, boolean applyQueryFilter) {
this.facet = facet;
this.applyQueryFilter = applyQueryFilter;
}
@Override
public FacetBuilder getFacet() {
return facet;
}
@Override
public boolean applyQueryFilter() {
return applyQueryFilter;
}
}

View File

@ -17,8 +17,12 @@ package org.springframework.data.elasticsearch.core.query;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import java.util.ArrayList;
import java.util.List;
/**
* NativeSearchQuery
*
@ -31,6 +35,17 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
private QueryBuilder query;
private FilterBuilder filter;
private SortBuilder sort;
private List<FacetRequest> facets;
public NativeSearchQuery(QueryBuilder query) {
this.query = query;
}
public NativeSearchQuery(QueryBuilder query, FilterBuilder filter) {
this.query = query;
this.filter = filter;
}
public NativeSearchQuery(QueryBuilder query, FilterBuilder filter, SortBuilder sort) {
this.query = query;
@ -49,4 +64,20 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
public SortBuilder getElasticsearchSort() {
return sort;
}
public void addFacet(FacetRequest facetRequest){
if(facets==null){
facets = new ArrayList<FacetRequest>();
}
facets.add(facetRequest);
}
public void setFacets(List<FacetRequest> facets){
this.facets = facets;
}
@Override
public List<FacetRequest> getFacets() {
return facets;
}
}

View File

@ -15,14 +15,18 @@
*/
package org.springframework.data.elasticsearch.core.query;
import org.apache.commons.collections.CollectionUtils;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.springframework.data.domain.Pageable;
import java.util.ArrayList;
import java.util.List;
/**
* NativeSearchQuery
*
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
@ -30,63 +34,72 @@ import org.springframework.data.domain.Pageable;
public class NativeSearchQueryBuilder {
private QueryBuilder queryBuilder;
private FilterBuilder filterBuilder;
private SortBuilder sortBuilder;
private Pageable pageable;
private String[] indices;
private String[] types;
private String[] fields;
private QueryBuilder queryBuilder;
private FilterBuilder filterBuilder;
private SortBuilder sortBuilder;
private List<FacetRequest> facetRequests = new ArrayList<FacetRequest>();
private Pageable pageable;
private String[] indices;
private String[] types;
private String[] fields;
public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
return this;
}
public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
return this;
}
public NativeSearchQueryBuilder withFilter(FilterBuilder filterBuilder) {
this.filterBuilder = filterBuilder;
return this;
}
public NativeSearchQueryBuilder withFilter(FilterBuilder filterBuilder) {
this.filterBuilder = filterBuilder;
return this;
}
public NativeSearchQueryBuilder withSort(SortBuilder sortBuilder) {
this.sortBuilder = sortBuilder;
return this;
}
public NativeSearchQueryBuilder withSort(SortBuilder sortBuilder) {
this.sortBuilder = sortBuilder;
return this;
}
public NativeSearchQueryBuilder withPageable(Pageable pageable) {
this.pageable = pageable;
return this;
}
public NativeSearchQueryBuilder withFacet(FacetRequest facetRequest) {
facetRequests.add(facetRequest);
return this;
}
public NativeSearchQueryBuilder withIndices(String... indices) {
this.indices = indices;
return this;
}
public NativeSearchQueryBuilder withPageable(Pageable pageable) {
this.pageable = pageable;
return this;
}
public NativeSearchQueryBuilder withTypes(String... types) {
this.types = types;
return this;
}
public NativeSearchQueryBuilder withIndices(String... indices) {
this.indices = indices;
return this;
}
public NativeSearchQueryBuilder withFields(String... fields) {
this.fields = fields;
return this;
}
public NativeSearchQueryBuilder withTypes(String... types) {
this.types = types;
return this;
}
public NativeSearchQuery build() {
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryBuilder, filterBuilder, sortBuilder);
if (pageable != null) {
nativeSearchQuery.setPageable(pageable);
}
if (indices != null) {
nativeSearchQuery.addIndices(indices);
}
if (types != null) {
nativeSearchQuery.addTypes(types);
}
if (fields != null) {
nativeSearchQuery.addFields(fields);
}
return nativeSearchQuery;
}
public NativeSearchQueryBuilder withFields(String... fields) {
this.fields = fields;
return this;
}
public NativeSearchQuery build() {
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryBuilder, filterBuilder, sortBuilder);
if (pageable != null) {
nativeSearchQuery.setPageable(pageable);
}
if (indices != null) {
nativeSearchQuery.addIndices(indices);
}
if (types != null) {
nativeSearchQuery.addTypes(types);
}
if (fields != null) {
nativeSearchQuery.addFields(fields);
}
if (CollectionUtils.isNotEmpty(facetRequests)) {
nativeSearchQuery.setFacets(facetRequests);
}
return nativeSearchQuery;
}
}

View File

@ -17,8 +17,11 @@ package org.springframework.data.elasticsearch.core.query;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import java.util.List;
/**
* NativeSearchQuery
*
@ -32,4 +35,6 @@ public interface SearchQuery extends Query {
FilterBuilder getFilter();
SortBuilder getElasticsearchSort();
List<FacetRequest> getFacets();
}

View File

@ -0,0 +1,83 @@
package org.springframework.data.elasticsearch.core.query;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.facet.FacetBuilders;
import org.elasticsearch.search.facet.terms.TermsFacet;
import org.elasticsearch.search.facet.terms.TermsFacetBuilder;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic term facet
*
* @author Artur Konczak
*/
public class TermFacetRequest extends AbstractFacetRequest {
private String[] stringFields;
private String[] numberFields;
private int size = 10;
private TermsFacet.ComparatorType order;
public TermFacetRequest(String name) {
super(name);
}
public void setStringFields(String... fields) {
this.stringFields = fields;
}
public void setNumberFields(String... fields) {
this.numberFields = fields;
}
public void setSize(int size) {
Assert.isTrue(size <= 0, "Size should be bigger then zero !!!");
this.size = size;
}
public void ascTerm() {
order = TermsFacet.ComparatorType.TERM;
}
public void descTerm() {
order = TermsFacet.ComparatorType.REVERSE_TERM;
}
public void ascCount() {
order = TermsFacet.ComparatorType.REVERSE_COUNT;
}
public void descCount() {
order = TermsFacet.ComparatorType.COUNT;
}
private List<String> convertStringFieldsToFullNames() {
List<String> result = new ArrayList<String>();
if (ArrayUtils.isNotEmpty(stringFields)) {
for (String stringField : stringFields) {
result.add(stringField + "." + FIELD_UNTOUCHED);
}
}
if (ArrayUtils.isNotEmpty(numberFields)) {
Collections.addAll(result, numberFields);
}
return result;
}
@Override
public FacetBuilder getFacet() {
List<String> fields = convertStringFieldsToFullNames();
Assert.notEmpty(fields, "Please select at last one field !!!");
TermsFacetBuilder builder = FacetBuilders.termsFacet(getName()).fields(fields.toArray(new String[fields.size()])).size(size);
if (order != null) {
builder.order(order);
}
return builder;
}
}

View File

@ -0,0 +1,59 @@
package org.springframework.data.elasticsearch.core.query;
/**
* Basic term facet
*
* @author Artur Konczak
*/
public class TermFacetRequestBuilder {
private TermFacetRequest result;
public TermFacetRequestBuilder(String name) {
result = new TermFacetRequest(name);
}
public TermFacetRequestBuilder withStringFields(String... fields) {
result.setStringFields(fields);
return this;
}
public TermFacetRequestBuilder withNumberFields(String... fields) {
result.setNumberFields(fields);
return this;
}
public TermFacetRequestBuilder withSize(int size) {
result.setSize(size);
return this;
}
public TermFacetRequestBuilder ascTerm() {
result.ascTerm();
return this;
}
public TermFacetRequestBuilder descTerm() {
result.descTerm();
return this;
}
public TermFacetRequestBuilder ascCount() {
result.ascCount();
return this;
}
public TermFacetRequestBuilder descCount() {
result.descCount();
return this;
}
public TermFacetRequestBuilder applyQueryFilter() {
result.setApplyQueryFilter(true);
return this;
}
public TermFacetRequest build() {
return result;
}
}

View File

@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.repository;
import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.FacetedPage;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.repository.NoRepositoryBean;
@ -37,9 +38,9 @@ public interface ElasticsearchRepository<T, ID extends Serializable> extends Ela
Iterable<T> search(QueryBuilder query);
Page<T> search(QueryBuilder query, Pageable pageable);
FacetedPage<T> search(QueryBuilder query, Pageable pageable);
Page<T> search(SearchQuery searchQuery);
FacetedPage<T> search(SearchQuery searchQuery);
Page<T> searchSimilar(T entity, SearchQuery searchQuery);
Page<T> searchSimilar(T entity, SearchQuery searchQuery);
}

View File

@ -19,6 +19,7 @@ import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.*;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.FacetedPage;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.util.Assert;
@ -180,13 +181,13 @@ public abstract class AbstractElasticsearchRepository<T, ID extends Serializable
}
@Override
public Page<T> search(QueryBuilder query, Pageable pageable) {
public FacetedPage<T> search(QueryBuilder query, Pageable pageable) {
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(query).withPageable(pageable).build();
return elasticsearchOperations.queryForPage(searchQuery, getEntityClass());
}
@Override
public Page<T> search(SearchQuery query) {
public FacetedPage<T> search(SearchQuery query) {
return elasticsearchOperations.queryForPage(query, getEntityClass());
}

View File

@ -0,0 +1,66 @@
package org.springframework.data.elasticsearch;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import java.util.ArrayList;
import java.util.List;
/**
* Simple type to test facets
*/
@Document(indexName = "articles", type = "article", shards = 1, replicas = 0, refreshInterval = "-1")
public class Article {
@Id
private String id;
private String title;
@Field(type = "string", facetable = true)
private List<String> authors = new ArrayList<String>();
@Field(type = "integer", facetable = true)
private List<Integer> publishedYears = new ArrayList<Integer>();
public Article() {
}
public Article(String id) {
this.id = id;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<String> getAuthors() {
return authors;
}
public void setAuthors(List<String> authors) {
this.authors = authors;
}
public List<Integer> getPublishedYears() {
return publishedYears;
}
public void setPublishedYears(List<Integer> publishedYears) {
this.publishedYears = publishedYears;
}
}

View File

@ -0,0 +1,46 @@
package org.springframework.data.elasticsearch;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import java.util.ArrayList;
/**
* Simple type to test facets
*/
public class ArticleBuilder {
private Article resutl;
public ArticleBuilder(String id) {
resutl = new Article(id);
}
public ArticleBuilder title(String title) {
resutl.setTitle(title);
return this;
}
public ArticleBuilder addAuthor(String author) {
resutl.getAuthors().add(author);
return this;
}
public ArticleBuilder addPublishedYear(Integer year) {
resutl.getPublishedYears().add(year);
return this;
}
public Article build() {
return resutl;
}
public IndexQuery buildIndex(){
IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(resutl.getId());
indexQuery.setObject(resutl);
return indexQuery;
}
}

View File

@ -0,0 +1,350 @@
/*
* Copyright 2013 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 org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.search.facet.FacetBuilders;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.Article;
import org.springframework.data.elasticsearch.ArticleBuilder;
import org.springframework.data.elasticsearch.core.facet.Term;
import org.springframework.data.elasticsearch.core.facet.TermResult;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.elasticsearch.index.query.FilterBuilders.termFilter;
import static org.elasticsearch.index.query.QueryBuilders.fieldQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Jonathan Yan
* @author Artur Konczak
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:elasticsearch-template-test.xml")
public class ElasticsearchTemplateFacetTests {
public static final String RIZWAN_IDREES = "Rizwan Idrees";
public static final String MOHSIN_HUSEN = "Mohsin Husen";
public static final String JONATHAN_YAN = "Jonathan Yan";
public static final String ARTUR_KONCZAK = "Artur Konczak";
public static final int YEAR_2002 = 2002;
public static final int YEAR_2001 = 2001;
public static final int YEAR_2000 = 2000;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Before
public void before() {
elasticsearchTemplate.deleteIndex(Article.class);
elasticsearchTemplate.createIndex(Article.class);
elasticsearchTemplate.putMapping(Article.class);
elasticsearchTemplate.refresh(Article.class, true);
IndexQuery article1 = new ArticleBuilder("1").title("article four").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addAuthor(JONATHAN_YAN).buildIndex();
IndexQuery article2 = new ArticleBuilder("2").title("article three").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addAuthor(MOHSIN_HUSEN).addPublishedYear(YEAR_2000).buildIndex();
IndexQuery article3 = new ArticleBuilder("3").title("article two").addAuthor(RIZWAN_IDREES).addAuthor(ARTUR_KONCZAK).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).buildIndex();
IndexQuery article4 = new ArticleBuilder("4").title("article one").addAuthor(RIZWAN_IDREES).addPublishedYear(YEAR_2002).addPublishedYear(YEAR_2001).addPublishedYear(YEAR_2000).buildIndex();
elasticsearchTemplate.index(article1);
elasticsearchTemplate.index(article2);
elasticsearchTemplate.index(article3);
elasticsearchTemplate.index(article4);
elasticsearchTemplate.refresh(Article.class, true);
}
@Test
public void shouldReturnFacetedAuthorsForGivenQueryWithDefaultOrder() {
// given
String facetName = "fauthors";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withFacet(new TermFacetRequestBuilder(facetName).withStringFields("authors").build()).build();
// when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then
assertThat(result.getNumberOfElements(), is(equalTo(4)));
TermResult facet = (TermResult) result.getFacet(facetName);
Term term = facet.getTerms().get(0);
assertThat(term.getTerm(), is(RIZWAN_IDREES));
assertThat(term.getCount(), is(4));
term = facet.getTerms().get(1);
assertThat(term.getTerm(), is(ARTUR_KONCZAK));
assertThat(term.getCount(), is(3));
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(MOHSIN_HUSEN));
assertThat(term.getCount(), is(2));
term = facet.getTerms().get(3);
assertThat(term.getTerm(), is(JONATHAN_YAN));
assertThat(term.getCount(), is(1));
}
@Test
public void shouldReturnFacetedAuthorsForGivenFilteredQuery() {
// given
String facetName = "fauthors";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFilter(FilterBuilders.notFilter(FilterBuilders.termFilter("title","four")))
.withFacet(new TermFacetRequestBuilder(facetName).applyQueryFilter().withStringFields("authors").build()).build();
// when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then
assertThat(result.getNumberOfElements(), is(equalTo(3)));
TermResult facet = (TermResult) result.getFacet(facetName);
Term term = facet.getTerms().get(0);
assertThat(term.getTerm(), is(RIZWAN_IDREES));
assertThat(term.getCount(), is(3));
term = facet.getTerms().get(1);
assertThat(term.getTerm(), is(ARTUR_KONCZAK));
assertThat(term.getCount(), is(2));
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(MOHSIN_HUSEN));
assertThat(term.getCount(), is(1));
}
@Test
public void shouldReturnFacetedAuthorsForGivenQueryOrderedByTerm() {
// given
String facetName = "fauthors";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new TermFacetRequestBuilder(facetName).withStringFields("authors").ascTerm().build()).build();
// when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then
assertThat(result.getNumberOfElements(), is(equalTo(4)));
TermResult facet = (TermResult) result.getFacet(facetName);
Term term = facet.getTerms().get(0);
assertThat(term.getTerm(), is(ARTUR_KONCZAK));
assertThat(term.getCount(), is(3));
term = facet.getTerms().get(1);
assertThat(term.getTerm(), is(JONATHAN_YAN));
assertThat(term.getCount(), is(1));
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(MOHSIN_HUSEN));
assertThat(term.getCount(), is(2));
term = facet.getTerms().get(3);
assertThat(term.getTerm(), is(RIZWAN_IDREES));
assertThat(term.getCount(), is(4));
}
@Test
public void shouldReturnFacetedAuthorsForGivenQueryOrderedByCountAsc() {
// given
String facetName = "fauthors";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new TermFacetRequestBuilder(facetName).withStringFields("authors").ascCount().build()).build();
// when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then
assertThat(result.getNumberOfElements(), is(equalTo(4)));
TermResult facet = (TermResult) result.getFacet(facetName);
Term term = facet.getTerms().get(0);
assertThat(term.getTerm(), is(JONATHAN_YAN));
assertThat(term.getCount(), is(1));
term = facet.getTerms().get(1);
assertThat(term.getTerm(), is(MOHSIN_HUSEN));
assertThat(term.getCount(), is(2));
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(ARTUR_KONCZAK));
assertThat(term.getCount(), is(3));
term = facet.getTerms().get(3);
assertThat(term.getTerm(), is(RIZWAN_IDREES));
assertThat(term.getCount(), is(4));
}
@Test
public void shouldReturnFacetedYearsForGivenQuery() {
// given
String facetName = "fyears";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new TermFacetRequestBuilder(facetName).withNumberFields("publishedYears").descCount().build()).build();
// when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then
assertThat(result.getNumberOfElements(), is(equalTo(4)));
TermResult facet = (TermResult) result.getFacet(facetName);
assertThat(facet.getTerms().size(), is(equalTo(3)));
Term term = facet.getTerms().get(0);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2000)));
assertThat(term.getCount(), is(3));
term = facet.getTerms().get(1);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2001)));
assertThat(term.getCount(), is(2));
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2002)));
assertThat(term.getCount(), is(1));
}
@Test
public void shouldReturnSingleFacetOverYearsAndAuthorsForGivenQuery() {
// given
String facetName = "fyears";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new TermFacetRequestBuilder(facetName).withNumberFields("publishedYears").withStringFields("authors").ascTerm().build()).build();
// when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then
assertThat(result.getNumberOfElements(), is(equalTo(4)));
TermResult facet = (TermResult) result.getFacet(facetName);
assertThat(facet.getTerms().size(), is(equalTo(7)));
Term term = facet.getTerms().get(0);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2000)));
assertThat(term.getCount(), is(3));
term = facet.getTerms().get(1);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2001)));
assertThat(term.getCount(), is(2));
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2002)));
assertThat(term.getCount(), is(1));
term = facet.getTerms().get(3);
assertThat(term.getTerm(), is(ARTUR_KONCZAK));
assertThat(term.getCount(), is(3));
term = facet.getTerms().get(4);
assertThat(term.getTerm(), is(JONATHAN_YAN));
assertThat(term.getCount(), is(1));
term = facet.getTerms().get(5);
assertThat(term.getTerm(), is(MOHSIN_HUSEN));
assertThat(term.getCount(), is(2));
term = facet.getTerms().get(6);
assertThat(term.getTerm(), is(RIZWAN_IDREES));
assertThat(term.getCount(), is(4));
}
@Test
public void shouldReturnFacetedYearsAndFacetedAuthorsForGivenQuery() {
// given
String numberFacetName = "fAuthors";
String stringFacetName = "fyears";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new TermFacetRequestBuilder(numberFacetName).withNumberFields("publishedYears").ascTerm().build())
.withFacet(new TermFacetRequestBuilder(stringFacetName).withStringFields("authors").ascTerm().build())
.build();
// when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then
assertThat(result.getNumberOfElements(), is(equalTo(4)));
TermResult numberFacet = (TermResult) result.getFacet(numberFacetName);
assertThat(numberFacet.getTerms().size(), is(equalTo(3)));
Term numberTerm = numberFacet.getTerms().get(0);
assertThat(numberTerm.getTerm(), is(Integer.toString(YEAR_2000)));
assertThat(numberTerm.getCount(), is(3));
numberTerm = numberFacet.getTerms().get(1);
assertThat(numberTerm.getTerm(), is(Integer.toString(YEAR_2001)));
assertThat(numberTerm.getCount(), is(2));
numberTerm = numberFacet.getTerms().get(2);
assertThat(numberTerm.getTerm(), is(Integer.toString(YEAR_2002)));
assertThat(numberTerm.getCount(), is(1));
TermResult stringFacet = (TermResult) result.getFacet(stringFacetName);
Term stringTerm = stringFacet.getTerms().get(0);
assertThat(stringTerm.getTerm(), is(ARTUR_KONCZAK));
assertThat(stringTerm.getCount(), is(3));
stringTerm = stringFacet.getTerms().get(1);
assertThat(stringTerm.getTerm(), is(JONATHAN_YAN));
assertThat(stringTerm.getCount(), is(1));
stringTerm = stringFacet.getTerms().get(2);
assertThat(stringTerm.getTerm(), is(MOHSIN_HUSEN));
assertThat(stringTerm.getCount(), is(2));
stringTerm = stringFacet.getTerms().get(3);
assertThat(stringTerm.getTerm(), is(RIZWAN_IDREES));
assertThat(stringTerm.getCount(), is(4));
}
@Test
public void shouldReturnFacetedYearsForNativeFacet() {
// given
String facetName = "fyears";
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withFacet(new NativeFacetRequest(FacetBuilders.termsFacet(facetName).field("publishedYears"))).build();
// when
FacetedPage<Article> result = elasticsearchTemplate.queryForPage(searchQuery, Article.class);
// then
assertThat(result.getNumberOfElements(), is(equalTo(4)));
TermResult facet = (TermResult) result.getFacet(facetName);
assertThat(facet.getTerms().size(), is(equalTo(3)));
Term term = facet.getTerms().get(0);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2000)));
assertThat(term.getCount(), is(3));
term = facet.getTerms().get(1);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2001)));
assertThat(term.getCount(), is(2));
term = facet.getTerms().get(2);
assertThat(term.getTerm(), is(Integer.toString(YEAR_2002)));
assertThat(term.getCount(), is(1));
}
}

View File

@ -465,12 +465,12 @@ public class ElasticsearchTemplateTests {
// when
Page<String> page = elasticsearchTemplate.queryForPage(searchQuery, new ResultsMapper<String>() {
@Override
public Page<String> mapResults(SearchResponse response) {
public FacetedPage<String> mapResults(SearchResponse response) {
List<String> values = new ArrayList<String>();
for (SearchHit searchHit : response.getHits()) {
values.add((String) searchHit.field("message").value());
}
return new PageImpl<String>(values);
return new FacetedPageImpl<String>(values);
}
});
// then
@ -543,7 +543,7 @@ public class ElasticsearchTemplateTests {
while (hasRecords) {
Page<SampleEntity> page = elasticsearchTemplate.scroll(scrollId, 5000L, new ResultsMapper<SampleEntity>() {
@Override
public Page<SampleEntity> mapResults(SearchResponse response) {
public FacetedPage<SampleEntity> mapResults(SearchResponse response) {
List<SampleEntity> chunk = new ArrayList<SampleEntity>();
for (SearchHit searchHit : response.getHits()) {
if (response.getHits().getHits().length <= 0) {
@ -556,7 +556,7 @@ public class ElasticsearchTemplateTests {
}
if(chunk.size() > 0){
return new PageImpl<SampleEntity>(chunk);
return new FacetedPageImpl<SampleEntity>(chunk);
}
return null;
}