Spring Data Elasticsearch是否支持Multi Search API?

时间:2018-05-05 18:21:11

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

我正在使用Spring Data Elasticsearch,并且对使用Elasticsearch 6.2的Multi Search API感兴趣,以便在单个API请求中执行多个搜索。

https://www.elastic.co/guide/en/elasticsearch/reference/6.2/search-multi-search.html

目前使用Spring Data Elasticsearch的{​​{1}}来构建搜索查询,使用NativeSearchQueryBuilder来执行搜索请求。在查看Spring Data代码和浏览文档之后,我还没有设法找到任何公开或提交Multi Search请求的方法。

Spring Data Elasticsearch是否支持Multi Search API,通过他们的ElasticsearchTemplate或我可能不知道的其他客户端/机制?

Elasticsearch在他们的Java API中提供了这个功能,所以我所追求的是类似于以下内容的东西,但不幸的是我必然会使用Spring Data Elasticsearch。

https://www.elastic.co/guide/en/elasticsearch/client/java-api/6.2/java-search-msearch.html

2 个答案:

答案 0 :(得分:2)

否,Spring Data Elasticsearch暂时不支持多重搜索。我也遇到了同样的问题,因此我working a PR添加了此功能。现在,我提出以下解决方法:

@Override
public YYY findBy(XXX xxx) {
  Client client = template.getClient();
  MultiSearchRequest request = new MultiSearchRequest();
  // build searchQuery like normal
  for (NativeSearchQuery searchQuery : queries) {
    request.add(prepareSearch(client, searchQuery));
  }
  ActionFuture<MultiSearchResponse> future = client
      .multiSearch(request);
  MultiSearchResponse response = future.actionGet();
  Item[] items = response.getResponses();
  for (int i = 0; i < items.length; i++) {
    AggregatedPage<XXX> ts = resultMapper.mapResults(items[i].getResponse(), XXX.class, page);
    // do with page
  }
}

private SearchRequestBuilder prepareSearch(Client client, SearchQuery searchQuery) {
  Assert.notNull(searchQuery.getIndices(), "No index defined for Query");
  Assert.notNull(searchQuery.getTypes(), "No type defined for Query");

  int startRecord = 0;
  SearchRequestBuilder searchRequest = client.prepareSearch(toArray(searchQuery.getIndices()))
      .setSearchType(searchQuery.getSearchType()).setTypes(toArray(searchQuery.getTypes()));

  if (searchQuery.getSourceFilter() != null) {
    SourceFilter sourceFilter = searchQuery.getSourceFilter();
    searchRequest.setFetchSource(sourceFilter.getIncludes(), sourceFilter.getExcludes());
  }

  if (searchQuery.getPageable().isPaged()) {
    startRecord = searchQuery.getPageable().getPageNumber() * searchQuery.getPageable().getPageSize();
    searchRequest.setSize(searchQuery.getPageable().getPageSize());
  }
  searchRequest.setFrom(startRecord);

  if (!searchQuery.getFields().isEmpty()) {
    searchRequest.setFetchSource(toArray(searchQuery.getFields()), null);
  }

  if (searchQuery.getSort() != null) {
    for (Sort.Order order : searchQuery.getSort()) {
      searchRequest.addSort(order.getProperty(),
          order.getDirection() == Sort.Direction.DESC ? SortOrder.DESC : SortOrder.ASC);
    }
  }

  if (searchQuery.getMinScore() > 0) {
    searchRequest.setMinScore(searchQuery.getMinScore());
  }

  if (searchQuery.getFilter() != null) {
    searchRequest.setPostFilter(searchQuery.getFilter());
  }

  if (!isEmpty(searchQuery.getElasticsearchSorts())) {
    for (SortBuilder sort : searchQuery.getElasticsearchSorts()) {
      searchRequest.addSort(sort);
    }
  }

  if (!searchQuery.getScriptFields().isEmpty()) {
    //_source should be return all the time
    //searchRequest.addStoredField("_source");
    for (ScriptField scriptedField : searchQuery.getScriptFields()) {
      searchRequest.addScriptField(scriptedField.fieldName(), scriptedField.script());
    }
  }

  if (searchQuery.getHighlightFields() != null) {
    for (HighlightBuilder.Field highlightField : searchQuery.getHighlightFields()) {
      searchRequest.highlighter(new HighlightBuilder().field(highlightField));
    }
  }

  if (!isEmpty(searchQuery.getIndicesBoost())) {
    for (IndexBoost indexBoost : searchQuery.getIndicesBoost()) {
      searchRequest.addIndexBoost(indexBoost.getIndexName(), indexBoost.getBoost());
    }
  }

  if (!isEmpty(searchQuery.getAggregations())) {
    for (AbstractAggregationBuilder aggregationBuilder : searchQuery.getAggregations()) {
      searchRequest.addAggregation(aggregationBuilder);
    }
  }

  if (!isEmpty(searchQuery.getFacets())) {
    for (FacetRequest aggregatedFacet : searchQuery.getFacets()) {
      searchRequest.addAggregation(aggregatedFacet.getFacet());
    }
  }

  return searchRequest.setQuery(searchQuery.getQuery());
}

答案 1 :(得分:0)

可能为时已晚,但是当我遇到相同的问题时,我没有任何答案就提出了问题。希望我的解决方案对其他搜索者有所帮助。 Spring数据Elasticsearch有很多限制,但是您可以使用ElasticSearch的Java客户端执行任何操作。

以下是Maven依赖项:

<!--ELASTICSEARCH-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>transport</artifactId>
        <version>${elasticsearch.version}</version>
    </dependency>
    <dependency>
        <groupId>org.elasticsearch.plugin</groupId>
        <artifactId>transport-netty4-client</artifactId>
        <version>${elasticsearch.version}</version>
    </dependency>

这是我的解决方法:

@Service
public class UniversalSearchHelper {
    @Autowired
    protected Client client;

    public List<SearchResult> matchQuery(String term) {
        MultiSearchRequest request = new MultiSearchRequest();
        request.add(searchRequest(QueryBuilders.boolQuery()
                .should(QueryBuilders.matchQuery("name", term))
                .should(QueryBuilders.matchQuery("full_name", term)), "movie_index"));
        request.add(searchRequest(QueryBuilders.matchQuery("name", term), "taxonomy_index"));
        ActionFuture<MultiSearchResponse> r = client.multiSearch(request);
        return getTopTen(r.actionGet(2000));
    }

    public List<SearchResult> fuzzyQuery(String term) {
        MultiSearchRequest request = new MultiSearchRequest();
        request.add(searchRequest(QueryBuilders.boolQuery()
                .should(QueryBuilders.fuzzyQuery("name", term))
                .should(QueryBuilders.fuzzyQuery("full_name", term)), "movie_index"));
        request.add(searchRequest((QueryBuilders.fuzzyQuery("name", term), "taxonomy_index"));
        ActionFuture<MultiSearchResponse> r = client.multiSearch(request);
        return getTopTen(r.actionGet(2000));
    }

    private SearchRequest searchRequest(QueryBuilder queryBuilder, String... indices) {
        SearchSourceBuilder search = new SearchSourceBuilder();
        search.query(queryBuilder);
        SearchRequest request = new SearchRequest();
        request.indices(indices);
        request.source(search);
        return request;
    }

    private List<SearchResult> getTopTen(MultiSearchResponse response) {
        List<SearchResult> results = new ArrayList<>();
        response.iterator().forEachRemaining(item -> {
            if (!item.isFailure()) {
                item.getResponse().getHits().iterator().forEachRemaining(hit -> results.add(new SearchResult(hit)));
            }
        });

        results.sort((searchResult1, searchResult2) -> (int) (searchResult2.getScore() - searchResult1.getScore()));
        return results.stream().filter(sr -> sr.getTaxonomy() != null).limit(10).collect(Collectors.toList());
    }
}

和实体获取结果:

import org.elasticsearch.search.SearchHit;

public class SearchResult {
    private Object id;
    private float score;
    private String term;
    private String type;
    private String taxonomy;

    public Object getId() {
        return id;
    }

    public SearchResult setId(Object id) {
        this.id = id;
        return this;
    }

    public float getScore() {
        return score;
    }

    public SearchResult setScore(float score) {
        this.score = score;
        return this;
    }

    public String getTerm() {
        return term;
    }

    public SearchResult setTerm(String term) {
        this.term = term;
        return this;
    }

    public String getType() {
        return type;
    }

    public SearchResult setType(String type) {
        this.type = type;
        return this;
    }

    public String getTaxonomy() {
        return taxonomy;
    }

    public void setTaxonomy(String taxonomy) {
        this.taxonomy = taxonomy;
    }

    public SearchResult() {
    }

    public SearchResult(SearchHit hit) {
        this.id = hit.getSource().get("id");
        this.term = (String)hit.getSource().get("name");
        this.score = hit.getScore();
        this.type = hit.getType();
        this.taxonomy = (String) hit.getSource().get("taxonomy");
    }
}