从Spring-Data查询Elasticsearch时我想获得 _score
如果我们采用以下简单的类:
@Document(indexName = "test", type = "el_test")
public static class ElTest{
private long id;
private String myField;
}
使用JUnit测试
@Test
public void testScore() throws Exception {
elasticsearchTemplate.index(new IndexQueryBuilder()
.withIndexName("test")
.withObject(new ElTest("first value"))
.build());
elasticsearchTemplate.index(new IndexQueryBuilder()
.withIndexName("test")
.withObject(new ElTest("second value"))
.build());
elasticsearchTemplate.index(new IndexQueryBuilder()
.withIndexName("test")
.withObject(new ElTest("third"))
.build());
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("myField","second value"))
.build();
List<ElTest> els = elasticsearchTemplate.queryForList(query, ElTest.class);
assertEquals(2, els.size());
}
这将在Elasticsearch中创建3个条目。该查询将检索具有不同分数的两个值。
如果我们将请求直接放在Elasticsearch中:
POST /test/_search
{
"query": {
"match" : {
"myField" : {
"query" : "second value",
"type" : "boolean"
}
}
}
}
Elasticsearch的回复:
{
"took": 15,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 0.8838835,
"hits": [
{
"_index": "test",
"_type": "el_test",
"_id": "AVKmmYCL3xnXT_BGRA3T",
"_score": 0.8838835,
"_source": {
"id": 0,
"myField": "second value"
}
},
{
"_index": "test",
"_type": "el_test",
"_id": "AVKmmYCI3xnXT_BGRA3S",
"_score": 0.028130025,
"_source": {
"id": 0,
"myField": "first value"
}
}
]
}
}
当我收到ElTest对象列表时,我无法检索 _score 值(0.8838835和0.028130025)。有没有办法在Spring-data中获取值?
我会想象像
这样的东西@Document(indexName = "test", type = "el_test")
public static class ElTest{
private long id;
private String myField;
@Score
private double score;
}
分数将是一个注释,表明该字段由Elasticsearch填充。
我想要分数的原因是在gui中对列表进行排序。
由于从Elasticsearch到gui的许多dto映射,列表在某个地方都没有排序。因此它不可靠。
我当然可以手动添加分数值,但需要迭代列表。这不是很好。
我使用Spring-data-elasticsearch版本1.2.0,因此使用Elasticsearch 1.4.4
答案 0 :(得分:13)
我一直在寻找同样的东西。
我发现在 ElasticsearchTemplate 中使用 DefaultResultMapper 的类实例:
@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) {
T result = null;
if (!Strings.isNullOrEmpty(hit.sourceAsString())) {
result = mapEntity(hit.sourceAsString(), clazz);
} else {
result = mapEntity(hit.getFields().values(), clazz);
}
setPersistentEntityId(result, hit.getId(), clazz);
results.add(result);
}
}
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);
}
分数存储在 SearchHit 实例中,但在hit.sourceAsString()中会被忽略...
作为一种解决方法,我创建了一个简单的界面:
/**
* Object to have score from elastic search
*/
public interface Scoreable {
float getScore();
void setScore(float score);
}
和扩展 DefaultResultMapper :
/**
* Results mapper with score support
*/
public class ScoreResultsMapper extends DefaultResultMapper {
public ScoreResultsMapper(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
super(mappingContext);
}
@Override
public <T> FacetedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
FacetedPage<T> resultPage = super.mapResults(response, clazz, pageable);
Iterator<T> it = resultPage.getContent().iterator();
for (SearchHit hit : response.getHits()) {
if (hit != null) {
T next = it.next();
if (next instanceof Scoreable) {
((Scoreable) next).setScore(hit.score());
}
}
}
return resultPage;
}
}
这里我只是检查返回的类型是否是Scoreable的一个实例,如果是的话我将得分加入其中
所以现在我可以使用新的mapper配置 ElasticsearchTemplate (我使用的是spring-boot):
@Bean
public ElasticsearchTemplate elasticsearchTemplate() {
MappingElasticsearchConverter converter = new MappingElasticsearchConverter(new SimpleElasticsearchMappingContext());
ScoreResultsMapper mapper = new ScoreResultsMapper(converter.getMappingContext());
return new ElasticsearchTemplate(client(), converter, mapper);
}
当然,我所有得分的文件都必须延伸 Scorable 。 我们可能必须覆盖DefaultResultMapper的其他一些方法,以便在其他类型的查询中获得分数支持。