JLumos
JLumos

Reputation: 127

Get all different values for a certain field in elasticsearch with spring data

I am trying to get all the different values for a certain field (e.g. "name") in ElasticSearch with SpringData.

As a first approach, I have this JSON which does what I want:

{
    "aggs" : {
        "nameAgg" : {
            "terms" : { "field" : "name.key", "size":10000 }
        }
    },
    "size":0
}

It works fine if I perform a GET over the index, retrieving data like this:

"aggregations": {
        "nameAgg": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "Paul",
                    "doc_count": 12
                },
                {
                    "key": "John",
                    "doc_count": 7
                }
]
}
}

Since I only need all the distinct values for the field, this suits my need. Now, I am trying to achieve this in Java with spring-data-elasticsearch.

The closest try I did was this:

AbstractAggregationBuilder<TermsAggregationBuilder> agBuilder = AggregationBuilders.terms("name.key").field("name.key").size(10000);
    
Query query = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery()).addAggregation(agBuilder).build();

But the output contains all the data for each indexed document and it does not match my expected output. How could I execute this type of request with spring-data-elasticsearch?

UPDATE: I have tried to specify a Pageable argument to the query, just like this:

AbstractAggregationBuilder<TermsAggregationBuilder> agBuilder = AggregationBuilders.terms("name.key").field("name.key");
    
    Query query = new NativeSearchQueryBuilder()   
        .withPageable(PageRequest.of(0, 0))
        .withQuery(QueryBuilders.matchAllQuery())
        .addAggregation(agBuilder).build();

But then I receive this exception:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: Page size must not be less than one!
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:497)
Caused by: java.lang.IllegalArgumentException: Page size must not be less than one!
    at org.springframework.data.domain.AbstractPageRequest.<init>(AbstractPageRequest.java:50)
    at org.springframework.data.domain.PageRequest.<init>(PageRequest.java:43)
    at org.springframework.data.domain.PageRequest.of(PageRequest.java:70)
    at org.springframework.data.domain.PageRequest.of(PageRequest.java:58)

Upvotes: 0

Views: 1562

Answers (3)

Abacus
Abacus

Reputation: 19431

NativeSearchQueryBuilder has a withMaxResults(Integer) method which should do what you need.

Upvotes: 0

JLumos
JLumos

Reputation: 127

I have found the solution.

Since spring data throws an exception if you try to use a Page with size 0, I did a workaround creating a class implementing the Pageable interface:

public class CustomBlankPage implements Pageable {
  
  public static final CustomBlankPage PAGE = new CustomBlankPage();

  @Override
  public int getPageNumber() {
    return 0;
  }

  @Override
  public int getPageSize() {
    return 0;
  }

  @Override
  public long getOffset() {
    return 0;
  }

  @Override
  public Sort getSort() {
    return null;
  }

  @Override
  public Pageable next() {
    return null;
  }

  @Override
  public Pageable previousOrFirst() {
    return null;
  }

  @Override
  public Pageable first() {
    return null;
  }

  @Override
  public boolean hasPrevious() {
    return false;
  }
}

The impl of the search is as follows:

AbstractAggregationBuilder<TermsAggregationBuilder> agBuilder = AggregationBuilders.terms("name.key").field("name.key").size(10000);
    
Query query = new NativeSearchQueryBuilder()   
        .withPageable(CustomBlankPage.PAGE)
        .withQuery(QueryBuilders.matchAllQuery())
        .addAggregation(agBuilder).build(); 

Then I explore the results retrieved with this code:

SearchHits<Person> hits = operations.search(query, Person.class);
Aggregations aggs = hits.getAggregations();
ParsedStringTerms namesTerm = (ParsedStringTerms) aggs.get("name.key");
List<String> keys = namesTerm.getBuckets()
      .stream()
      .map(b -> b.getKeyAsString())
      .collect(Collectors.toList());

Upvotes: 1

Joe - Check out my books
Joe - Check out my books

Reputation: 16925

You've set size: 0 in your JSON so you'll need to do the same in spring:

Query query = new NativeSearchQueryBuilder()   
                     .withPageable(PageRequest.of(0, 0)    //  <-- (page = 0, size = 0) <--
                     .withQuery(QueryBuilders.matchAllQuery())
                     .addAggregation(agBuilder).build();

Upvotes: 0

Related Questions