Spring-data-elasticsearch元数据_score

时间:2016-02-03 11:06:10

标签: java spring-data spring-data-elasticsearch

从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

1 个答案:

答案 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的其他一些方法,以便在其他类型的查询中获得分数支持。