Session: Querying: How to Perform a Faceted (Aggregated) Search

To execute facet (aggregation) query using the session query method, use the aggregateBy or aggregateUsing methods. This will scope you to the aggregation query builder where you will be allowed to define single or multiple facets for the query using a straightforward and fluent API.

Syntax

IAggregationDocumentQuery<T> aggregateBy(Consumer<IFacetBuilder<T>> builder);

IAggregationDocumentQuery<T> aggregateBy(FacetBase facet);

IAggregationDocumentQuery<T> aggregateBy(Facet... facet);

IAggregationDocumentQuery<T> aggregateUsing(String facetSetupDocumentId);
Parameters
facet FacetBase FacetBase implementation defining the scope of the facet and its options (either Facet or RangeFacet)
facets FacetBase... Items containing FacetBase implementations
builder Consumer<IFacetBuilder<T>> Builder with a fluent API that constructs a FacetBase instance
facetSetupDocumentId String ID of a document containing FacetSetup

Facet & RangeFacet

Facet vs RangeFacet

RangeFacet allows you to split the results of the calculations into several ranges, in contrast to Facet where whole spectrum of results will be used to generate a single outcome.

public class Facet {
    private String fieldName;
    private FacetOptions options;
    private Map<FacetAggregation, String> aggregations;
    private String displayFieldName;

    public String getFieldName() {
        return fieldName;
    }

    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }

    public FacetOptions getOptions() {
        return options;
    }

    public void setOptions(FacetOptions options) {
        this.options = options;
    }

    public Map<FacetAggregation, String> getAggregations() {
        return aggregations;
    }

    public void setAggregations(Map<FacetAggregation, String> aggregations) {
        this.aggregations = aggregations;
    }

    public String getDisplayFieldName() {
        return displayFieldName;
    }

    public void setDisplayFieldName(String displayFieldName) {
        this.displayFieldName = displayFieldName;
    }
}
public class RangeFacet {
    private List<String> ranges;
    private FacetOptions options;
    private Map<FacetAggregation, String> aggregations;
    private String displayFieldName;

    public List<String> getRanges() {
        return ranges;
    }

    public void setRanges(List<String> ranges) {
        this.ranges = ranges;
    }

    public FacetOptions getOptions() {
        return options;
    }

    public void setOptions(FacetOptions options) {
        this.options = options;
    }

    public Map<FacetAggregation, String> getAggregations() {
        return aggregations;
    }

    public void setAggregations(Map<FacetAggregation, String> aggregations) {
        this.aggregations = aggregations;
    }

    public String getDisplayFieldName() {
        return displayFieldName;
    }

    public void setDisplayFieldName(String displayFieldName) {
        this.displayFieldName = displayFieldName;
    }
}
public enum FacetAggregation {
    NONE,
    MAX,
    MIN,
    AVERAGE,
    SUM;
}

Builder

IFacetOperations<T> byRanges(RangeBuilder range, RangeBuilder... ranges);

IFacetOperations<T> byField(String fieldName);

IFacetOperations<T> withDisplayName(String displayName);

IFacetOperations<T> withOptions(FacetOptions options);

IFacetOperations<T> sumOn(String path);
IFacetOperations<T> minOn(String path);
IFacetOperations<T> maxOn(String path);
IFacetOperations<T> averageOn(String path);
Parameters
path String Points to the index field that should be used for operation (byRanges, byField) or to document field that should be used for aggregation (sumOn, minOn, maxOn, averageOn)
fieldName String Points to the index field that should be used for operation (byRanges, byField) or to document field that should be used for aggregation (sumOn, minOn, maxOn, averageOn)
displayName String If set, results of a facet will be returned under this name
options FacetOptions Non-default options that should be used for operation

Options

private FacetTermSortMode termSortMode = FacetTermSortMode.VALUE_ASC;
private boolean includeRemainingTerms;
private int start;
private int pageSize = Integer.MAX_VALUE;

//getters and setters
Options
termSortMode FacetTermSortMode Indicates how terms should be sorted (VALUE_ASC, VALUE_DESC, COUNT_ASC, COUNT_DESC)
includeRemainingTerms booelean Indicates if remaining terms should be included in results
start int Used to skip given number of facet results in the outcome
pageSize int Used to limit facet results to the given value

Example I

FacetOptions facetOptions = new FacetOptions();
facetOptions.setTermSortMode(FacetTermSortMode.COUNT_DESC);

Facet facet1 = new Facet();
facet1.setFieldName("manufacturer");
facet1.setOptions(facetOptions);

RangeFacet facet2 = new RangeFacet();
facet2.setRanges(Arrays.asList(
    "cost < 200",
    "cost between 200 and 400",
    "cost between 400 and 600",
    "cost between 600 and 800",
    "cost >= 800"
));
facet2.setAggregations(Collections.singletonMap(FacetAggregation.AVERAGE, "cost"));

RangeFacet facet3 = new RangeFacet();
facet3.setRanges(Arrays.asList(
    "megapixels < 3",
    "megapixels between 3 and 7",
    "megapixels between 7 and 10",
    "megapixels >= 10"
));

Map<String, FacetResult> facets = session
    .query(Camera.class, Query.index("Camera/Costs"))
    .aggregateBy(facet1)
    .andAggregateBy(facet2)
    .andAggregateBy(facet3)
    .execute();
from index 'Camera/Costs' 
select 
facet(manufacturer), 
facet(cost < 200, cost >= 200 AND cost < 400, cost >= 400 AND cost < 600, cost >= 600 AND cost < 800, cost >= 800),
facet(megapixels < 3, megapixels >= 3 AND megapixels < 7, megapixels >= 7 AND megapixels < 10, megapixels >= 10)

Example II

FacetOptions options = new FacetOptions();
options.setTermSortMode(FacetTermSortMode.COUNT_DESC);

RangeBuilder<Integer> costBuilder = RangeBuilder.forPath("cost");
RangeBuilder<Integer> megapixelsBuilder = RangeBuilder.forPath("megapixels");

Map<String, FacetResult> facetResult = session
    .query(Camera.class, Query.index("Camera/Costs"))
    .aggregateBy(builder -> builder
        .byField("manufacturer")
        .withOptions(options))
    .andAggregateBy(builder -> builder
        .byRanges(
            costBuilder.isLessThan(200),
            costBuilder.isGreaterThanOrEqualTo(200).isLessThan(400),
            costBuilder.isGreaterThanOrEqualTo(400).isLessThan(600),
            costBuilder.isGreaterThanOrEqualTo(600).isLessThan(800),
            costBuilder.isGreaterThanOrEqualTo(800))
        .averageOn("cost"))
    .andAggregateBy(builder -> builder
        .byRanges(
            megapixelsBuilder.isLessThan(3),
            megapixelsBuilder.isGreaterThanOrEqualTo(3).isLessThan(7),
            megapixelsBuilder.isGreaterThanOrEqualTo(7).isLessThan(10),
            megapixelsBuilder.isGreaterThanOrEqualTo(10)
        ))
    .execute();
from index 'Camera/Costs' 
select 
facet(manufacturer), 
facet(cost < 200, cost >= 200 AND cost < 400, cost >= 400 AND cost < 600, cost >= 600 AND cost < 800, cost >= 800),
facet(megapixels < 3, megapixels >= 3 AND megapixels < 7, megapixels >= 7 AND megapixels < 10, megapixels >= 10)

Example III

FacetSetup facetSetup = new FacetSetup();

Facet facetManufacturer = new Facet();
facetManufacturer.setFieldName("manufacturer");
facetSetup.setFacets(Arrays.asList(facetManufacturer));

RangeFacet cameraFacet = new RangeFacet();
cameraFacet.setRanges(Arrays.asList(
    "cost < 200",
    "cost between 200 and 400",
    "cost between 400 and 600",
    "cost between 600 and 800",
    "cost >= 800"
));

RangeFacet megapixelsFacet = new RangeFacet();
megapixelsFacet.setRanges(Arrays.asList(
    "megapixels < 3",
    "megapixels between 3 and 7",
    "megapixels between 7 and 10",
    "megapixels >= 10"
));

facetSetup.setRangeFacets(Arrays.asList(cameraFacet, megapixelsFacet));

session.store(facetSetup, "facets/CameraFacets");
session.saveChanges();

Map<String, FacetResult> facets = session
    .query(Camera.class, Query.index("Camera/Costs"))
    .aggregateUsing("facets/CameraFacets")
    .execute();
from index 'Camera/Costs' 
select facet(id('facets/CameraFacets'))

Remarks

Warning

aggregateBy only supports aggregation by a single field. If you want to aggregate by multiple fields, you need to emit a single field that contains all values.