Spring Elastic Search: Howto search across multiple indexes with mappings to different objects

Viewed 586

Firstly sorry if this has been asked somewhere, I've searched high and low with no result.

Basically I have an ES instance which has 3 indices. The data / mapping of the indices are generated from my java POJOs. Now I can search each index individually and map it back to a given Pojo / class.

However I'm completely at a loss when I want to do a search across the 3 indices in one go and map the search hit to the correct Pojo.

For instance here is how I currently search on a single index which maps to a UserProfile object I have:

    public Iterable<UserProfile> fetchUserSearchSuggestions(String query, Integer page, Integer size) {

        PageRequest pageRequest = PageRequest.of(page, size);

        QueryBuilder queryBuilder = QueryBuilders.boolQuery().should(new WildcardQueryBuilder("displayName", query + "*"))
                .should(new WildcardQueryBuilder("name", query + "*"));

        Query searchQuery = new NativeSearchQueryBuilder()
                .withFilter(queryBuilder)
                .withPageable(pageRequest)
                .build();

        SearchHits<UserProfile> searchSuggestions =
                elasticsearchOperations.search(searchQuery,
                        UserProfile.class,
                        IndexCoordinates.of(elasticUserIndex));

        List<UserProfile> suggestions = new ArrayList<>();

        searchSuggestions.getSearchHits().forEach(searchHit -> {
            suggestions.add(searchHit.getContent());
        });
        long totalCount = searchSuggestions.getTotalHits();
        Page<UserProfile> resultPage = PageableExecutionUtils.getPage(
                suggestions,
                pageRequest,
                () -> totalCount);
        return resultPage;
    }

Again my problem is if I want to search across all indices in one go, like a universal search and map a user result to the UserProfile class and so on for each different objects I have, how do I do this.

Many thanks for reading

1 Answers

Do you know a MultiSearchRequest ?

Let's say we have a class Request with such a structure:

class Request {
    private String index;
    private String field;
    private String value;
}
  • index - ElasticSearch index
  • field - field by which we are looking
  • value - field value

a search across multiple indices would look like this

   public MultiSearchResponse find(List<Request> requests) throws IOException {
    
    MultiSearchRequest multiSearchRequest = new MultiSearchRequest();

    for (Request req : requests){
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.matchBoolPrefixQuery(req.field, req.value));

        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.size(1);
        builder.query(queryBuilder);

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
        searchRequest.indices(req.index);
        searchRequest.source(builder);

        multiSearchRequest.add(searchRequest);
    }

    return restHighLevelClient.msearch(multiSearchRequest, RequestOptions.DEFAULT);
}

I don't know if my solution will help, but still

--UPD--

In my pet project i'm converter like this

public static List<SensorValueElasticsearch> parse(MultiSearchResponse response) {

    List<SensorValueElasticsearch> values = new ArrayList<>();
    
    for (MultiSearchResponse.Item item : response.getResponses()){
        if (item.getResponse().getHits().getTotalHits().value > 0){
            values.addAll(convertToList(item.getResponse()));
        }
    }
    return values;
}

public static List<SensorValueElasticsearch> convertToList(SearchResponse response) {
    SearchHit[] res = response.getHits().getHits();

    List<SensorValueElasticsearch> list = new LinkedList<>();

    for (SearchHit sh : res) {

        SensorValueElasticsearch object = null;
        try {
            object = OBJECT_MAPPER.readValue(sh.getSourceAsString(), SensorValueElasticsearch.class);
        } catch (JsonProcessingException e) {
            LOGGER.warn("Error while convert SearchResponse to SensorValueElasticsearch", e);
        }

        list.add(object);
    }

    return list;
}

In you case before OBJECT_MAPPER.readValue you must be check what kind of object came to you or write return with try-catch block like this:

        ObjectA a = null;
        ObjectB b = null;

        try {
            return OBJECT_MAPPER.readValue(sh.getSourceAsString(), ObjectA.class);
        } catch (JsonProcessingException e) {
            // ignore
        }

        try {
            return OBJECT_MAPPER.readValue(sh.getSourceAsString(), ObjectB.class);
        } catch (JsonProcessingException e) {
            // ignore
        }

        throw new IllegalArgumentException("Unknown class");
Related