DATAES-34 #17 - added option to inject custom implementation of ResultMapper

This commit is contained in:
Artur Konczak
2013-11-17 20:25:50 +00:00
parent 47c82a8f96
commit 56d4079d81
15 changed files with 1001 additions and 706 deletions

View File

@@ -0,0 +1,35 @@
package org.springframework.data.elasticsearch.core;
import org.springframework.data.elasticsearch.ElasticsearchException;
import java.io.IOException;
import static org.apache.commons.lang.StringUtils.isBlank;
/**
* @author Artur Konczak
*/
public abstract class AbstractResultMapper implements ResultsMapper {
private EntityMapper entityMapper;
public AbstractResultMapper(EntityMapper entityMapper) {
this.entityMapper = entityMapper;
}
public <T> T mapEntity(String source, Class<T> clazz) {
if (isBlank(source)) {
return null;
}
try {
return entityMapper.mapToObject(source, clazz);
} catch (IOException e) {
throw new ElasticsearchException("failed to map source [ " + source + "] to class " + clazz.getSimpleName(), e);
}
}
@Override
public EntityMapper getEntityMapper() {
return this.entityMapper;
}
}

View File

@@ -0,0 +1,53 @@
package org.springframework.data.elasticsearch.core;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.facet.Facet;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.facet.DefaultFacetMapper;
import org.springframework.data.elasticsearch.core.facet.FacetResult;
import java.util.ArrayList;
import java.util.List;
/**
* @author Artur Konczak
*/
public class DefaultResultMapper extends AbstractResultMapper {
public DefaultResultMapper(){
super(new DefaultEntityMapper());
}
public DefaultResultMapper(EntityMapper entityMapper) {
super(entityMapper);
}
@Override
public <T> FacetedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
long totalHits = response.getHits().totalHits();
List<T> results = new ArrayList<T>();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
results.add(mapEntity(hit.sourceAsString(), clazz));
}
}
List<FacetResult> facets = new ArrayList<FacetResult>();
if (response.getFacets() != null) {
for (Facet facet : response.getFacets()) {
FacetResult facetResult = DefaultFacetMapper.parse(facet);
if (facetResult != null) {
facets.add(facetResult);
}
}
}
return new FacetedPageImpl<T>(results, pageable, totalHits, facets);
}
@Override
public <T> T mapResult(GetResponse response, Class<T> clazz) {
return mapEntity(response.getSourceAsString(),clazz);
}
}

View File

@@ -60,6 +60,16 @@ public interface ElasticsearchOperations {
*/
<T> T queryForObject(GetQuery query, Class<T> clazz);
/**
* Execute the query against elasticsearch and return the first returned object using custom mapper
*
* @param query
* @param clazz
* @param mapper
* @return the first matching object
*/
<T> T queryForObject(GetQuery query, Class<T> clazz, GetResultMapper mapper);
/**
* Execute the query against elasticsearch and return the first returned object
*
@@ -87,14 +97,14 @@ public interface ElasticsearchOperations {
*/
<T> FacetedPage<T> queryForPage(SearchQuery query, Class<T> clazz);
/**
* Execute the query against elasticsearch and return result as {@link Page}
*
* @param query
* @param resultsMapper
* @return
*/
<T> FacetedPage<T> queryForPage(SearchQuery query, ResultsMapper<T> resultsMapper);
/**
* Execute the query against elasticsearch and return result as {@link Page} using custom mapper
*
* @param query
* @param clazz
* @return
*/
<T> FacetedPage<T> queryForPage(SearchQuery query, Class<T> clazz, SearchResultMapper mapper);
/**
* Execute the query against elasticsearch and return result as {@link Page}
@@ -114,6 +124,15 @@ public interface ElasticsearchOperations {
*/
<T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz);
/**
* Execute the query against elasticsearch and return result as {@link Page} using custom mapper
*
* @param query
* @param clazz
* @return
*/
<T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz, SearchResultMapper mapper);
/**
* Execute the criteria query against elasticsearch and return result as {@link List}
*
@@ -284,11 +303,11 @@ public interface ElasticsearchOperations {
*
* @param scrollId
* @param scrollTimeInMillis
* @param resultsMapper
* @param clazz
* @param <T>
* @return
*/
<T> Page<T> scroll(String scrollId, long scrollTimeInMillis, ResultsMapper<T> resultsMapper);
<T> Page<T> scroll(String scrollId, long scrollTimeInMillis, Class<T> clazz);
/**
* more like this query to search for documents that are "like" a specific document.

View File

@@ -39,20 +39,16 @@ import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.facet.Facet;
import org.elasticsearch.search.facet.FacetBuilder;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.Page;
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.FacetRequest;
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.*;
@@ -86,25 +82,29 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
private Client client;
private ElasticsearchConverter elasticsearchConverter;
private EntityMapper entityMapper;
private ResultsMapper resultsMapper;
public ElasticsearchTemplate(Client client) {
this(client, null, null);
}
public ElasticsearchTemplate(Client client, EntityMapper entityMapper) {
this(client, null, entityMapper);
this(client, null, new DefaultResultMapper(entityMapper));
}
public ElasticsearchTemplate(Client client, ResultsMapper resultsMapper) {
this(client, null, resultsMapper);
}
public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter) {
this(client, elasticsearchConverter, null);
}
public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter, EntityMapper entityMapper) {
public ElasticsearchTemplate(Client client, ElasticsearchConverter elasticsearchConverter, ResultsMapper resultsMapper) {
this.client = client;
this.entityMapper = (entityMapper == null) ? new DefaultEntityMapper() : entityMapper;
this.elasticsearchConverter = (elasticsearchConverter == null) ? new MappingElasticsearchConverter(
new SimpleElasticsearchMappingContext()) : elasticsearchConverter;
this.resultsMapper = (resultsMapper == null) ? new DefaultResultMapper() : resultsMapper;
}
@Override
@@ -134,11 +134,16 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
@Override
public <T> T queryForObject(GetQuery query, Class<T> clazz) {
return queryForObject(query, clazz, resultsMapper);
}
@Override
public <T> T queryForObject(GetQuery query, Class<T> clazz, GetResultMapper mapper) {
ElasticsearchPersistentEntity<T> persistentEntity = getPersistentEntityFor(clazz);
GetResponse response = client
.prepareGet(persistentEntity.getIndexName(), persistentEntity.getIndexType(), query.getId()).execute()
.actionGet();
return mapResult(response.getSourceAsString(), clazz);
return mapper.mapResult(response, clazz);
}
@Override
@@ -157,14 +162,13 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
@Override
public <T> FacetedPage<T> queryForPage(SearchQuery query, Class<T> clazz) {
SearchResponse response = doSearch(prepareSearch(query, clazz), query);
return mapResults(response, clazz, query.getPageable());
return queryForPage(query, clazz, resultsMapper);
}
@Override
public <T> FacetedPage<T> queryForPage(SearchQuery query, ResultsMapper<T> resultsMapper) {
SearchResponse response = doSearch(prepareSearch(query), query);
return resultsMapper.mapResults(response);
public <T> FacetedPage<T> queryForPage(SearchQuery query, Class<T> clazz, SearchResultMapper mapper) {
SearchResponse response = doSearch(prepareSearch(query, clazz), query);
return mapper.mapResults(response, clazz, query.getPageable());
}
@Override
@@ -209,13 +213,18 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
SearchResponse response = searchRequestBuilder
.execute().actionGet();
return mapResults(response, clazz, criteriaQuery.getPageable());
return resultsMapper.mapResults(response, clazz, criteriaQuery.getPageable());
}
@Override
public <T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz) {
return queryForPage(query, clazz, resultsMapper);
}
@Override
public <T> FacetedPage<T> queryForPage(StringQuery query, Class<T> clazz, SearchResultMapper mapper) {
SearchResponse response = prepareSearch(query, clazz).setQuery(query.getSource()).execute().actionGet();
return mapResults(response, clazz, query.getPageable());
return mapper.mapResults(response, clazz, query.getPageable());
}
@Override
@@ -349,10 +358,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
@Override
public <T> Page<T> scroll(String scrollId, long scrollTimeInMillis, ResultsMapper<T> resultsMapper) {
public <T> Page<T> scroll(String scrollId, long scrollTimeInMillis, Class<T> clazz) {
SearchResponse response = client.prepareSearchScroll(scrollId)
.setScroll(TimeValue.timeValueMillis(scrollTimeInMillis)).execute().actionGet();
return resultsMapper.mapResults(response);
return resultsMapper.mapResults(response, clazz, null);
}
@Override
@@ -415,7 +424,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
}
SearchResponse response = requestBuilder.execute().actionGet();
return mapResults(response, clazz, query.getPageable());
return resultsMapper.mapResults(response, clazz, query.getPageable());
}
private SearchResponse doSearch(SearchRequestBuilder searchRequest, SearchQuery searchQuery) {
@@ -513,7 +522,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
: query.getType();
IndexRequestBuilder indexRequestBuilder = client.prepareIndex(indexName, type, query.getId()).setSource(
entityMapper.mapToString(query.getObject()));
resultsMapper.getEntityMapper().mapToString(query.getObject()));
if (query.getVersion() != null) {
indexRequestBuilder.setVersion(query.getVersion());
@@ -549,33 +558,6 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
return new String[]{getPersistentEntityFor(clazz).getIndexType()};
}
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);
}
}
}
return new FacetedPageImpl<T>(results, pageable, totalHits, facets);
}
};
return resultsMapper.mapResults(response);
}
private List<String> extractIds(SearchResponse response) {
List<String> ids = new ArrayList<String>();
for (SearchHit hit : response.getHits()) {
@@ -586,24 +568,13 @@ public class ElasticsearchTemplate implements ElasticsearchOperations {
return ids;
}
private <T> T mapResult(String source, Class<T> clazz) {
if (isBlank(source)) {
return null;
}
try {
return entityMapper.mapToObject(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);
}
protected EntityMapper getEntityMapper() {
return entityMapper;
protected ResultsMapper getResultsMapper() {
return resultsMapper;
}
}

View File

@@ -0,0 +1,14 @@
package org.springframework.data.elasticsearch.core;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.springframework.data.domain.Pageable;
/**
* @author Artur Konczak
*/
public interface GetResultMapper {
<T> T mapResult(GetResponse response, Class<T> clazz);
}

View File

@@ -15,22 +15,16 @@
*/
package org.springframework.data.elasticsearch.core;
import org.elasticsearch.action.search.SearchResponse;
import org.springframework.data.domain.Page;
/**
* ResultsMapper
*
* @param <T>
*
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Artur Konczak
*
*/
public interface ResultsMapper<T> {
public interface ResultsMapper extends SearchResultMapper, GetResultMapper {
FacetedPage<T> mapResults(SearchResponse response);
EntityMapper getEntityMapper();
}

View File

@@ -0,0 +1,13 @@
package org.springframework.data.elasticsearch.core;
import org.elasticsearch.action.search.SearchResponse;
import org.springframework.data.domain.Pageable;
/**
* @author Artur Konczak
*/
public interface SearchResultMapper {
<T> FacetedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable);
}

View File

@@ -14,7 +14,7 @@ import java.util.List;
* @author Artur Konczak
* @author Petar Tahchiev
*/
public class FacetMapper {
public class DefaultFacetMapper {
public static FacetResult parse(Facet facet) {
if (facet instanceof TermsFacet) {