3
votes

I have a requirement to provide functionality which will allow user to search through many different domain elements and see results as combined list. So in UI he will have to fill only one text-field and than retrive results.

To visualize lets assume i have 3 entities in domain:

@Document(indexName="car") 
public class Car { 
  private int id;
  private String type;
} 
@Document(indexName="garage")
public class Garage{ 
  private int id;
  private String address;
} 
@Document(indexName="shop")
public class Shop{ 
  private int id;
  private String name;
}

Now i thought i could achieve requirement like this:

...
@Inject
    private ElasticsearchTemplate elasticsearchTemplate;
...    
@RequestMapping(value = "/_search/all/{query}",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
public List<?> search(@PathVariable String query) {
     SearchQuery searchQuery = new NativeSearchQueryBuilder()
       .withQuery(queryString(query))
       .withIndices("car", "garage", "shop")
       .build();

    //THIS WORKS
    elasticsearchTemplate.queryForIds(searchQuery);

    //THIS THROWS ERROR ABOUT WRONG INDEXES
    return elasticsearchTemplate.queryForPage(searchQuery, GlobalSearchDTO.class, new GlobalSearchResultMapper()).getContent();
    }
...
    class GlobalSearchDTO {
        public Long id;
        public String type;
        public Object obj;
    }
 ...  

but when calling 2nd function - the one which is responsible for returning actual documents, the following exception is thrown:

Unable to identify index name. GlobalSearchDTO is not a Document. Make sure the document class is annotated with @Document(indexName="foo")

I've tried with passing any domain entity as a class argument, but than i am retriving only elements from the corresponding index, not all of them. For instance calling:

return elasticsearchTemplate.queryForPage(searchQuery, Shop.class, new GlobalSearchResultMapper()).getContent();

Results in retrivng elements only from 'shop' index. It seems like for some reason dynamically provided indicies are not used.

So the question is: Is it possible to retrive data like that? Why specifying '.withIndices("car", "garage", "shop")' is not enough?

Maybe i should consider other solutions like:

  1. search through indexes in loop(one bye one), join results and order them by score

  2. create separate GlobalSearch entity with 'globalsearch' index
    and duplicate data there

Thanks in advance!

Krzysztof

1
This, admittedly old, thread states that was not implemented back in '14, it may still be the case. 1 and 2 would certainly work! Wish I could be of more help, but perhaps that thread will help at least some.Daniel Hoffmann-Mitscherling
Thanks for feedback Daniel, I have acutally read the thread u mentioned befor posting this one(maybe I should have mentioned that in question) and I was hoping that it had been implemented till now. Well I guess for now i will go for 1st alternative. I know that it will be slower then 2nd one, but in future it would be easily changable with the desired approach - searching through multiple indicies.DonCziken

1 Answers

2
votes

I have managed to find suitable workaround for my problem. It turned out that when using 'scroll' and 'scan' functionality dynamically provided indicies are used which means that query works as expected. Code for solution:

 @RequestMapping(value = "/_search/all/{query}",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
    @Timed
    public List<?> search(@PathVariable String query) {
       SearchQuery searchQuery = new NativeSearchQueryBuilder()
         .withQuery(queryString(query))
         .withIndices("car", "garage", "shop")
         .withPageable(new PageRequest(0,1))
         .build();

         String scrollId = elasticsearchTemplate.scan(searchQuery, 1000, false);
         List<GlobalSearchDTO> sampleEntities = new ArrayList<GlobalSearchDTO>();
         boolean hasRecords = true;
         while (hasRecords){
             Page<GlobalSearchDTO> page = elasticsearchTemplate.scroll(scrollId, 5000L , new ResultMapper());
             if(page != null) {
                 sampleEntities.addAll(page.getContent());
                 hasRecords = page.hasNext();
             }
             else{
                 hasRecords = false;
             }
         }
         return sampleEntities;
    }

}

and in the ResultMapper class:

...
for (SearchHit hit : response.getHits()) { 
    switch(hit.getIndex()) {
                case "car": //map to DTO
                case "shop": //map to DTO
                case "garage": //map to DTO
                }
}   
...