Jackson Murph
Jackson Murph

Reputation: 63

Aggregates in Nest (ElasticSearch) on child/nested objects

I have a catalog of products that I want to calculate aggregates on. This is simple enough for top level properties such as brand name, manufacturer, etc. The trouble comes with trying to calculate range counts on prices because we sell in multiple currencies, and when determining these counts I only want to query on one currency at a time. Here is a sample of my product object mapping:

public class Product
{
    public int ID { get; set;}
    public string Name { get; set; }
    public IList<Price> Prices { get; set; }
}

public class Price
{
    public int CurrencyID { get; set; }
    public decimal Cost { get; set; }
}

Here is an example of a query for all products with a price below 100:

var cheapProducts = client.Search<Product>(s => s
    .From(0)
    .Size(1000)
    .Query(q => q
        .Range(r => r
            .LowerOrEquals(100)
            .OnField(f => f.Prices.FirstOrDefault().Cost))));

The ElasticSearch request that this generates is:

{
    "from": 0,
    "size": 1000,
    "query": {
        "range" : {
            "prices.cost": {
                "lte": "100"
            }
        }
    }
}

This returns all products with at least one price below 100 in any currency, as you would expect. What I've been unable to do is to run this query against just prices in a given currency. For example, adding this filter to the query only removes products that don't have a price in currency 1:

var cheapProducts = client.Search<Product>(s => s
    .From(0)
    .Size(1000)
    .Filter(f => f
        .Term(t => t
            .Prices.FirstOrDefault().CurrencyID, 1))
    .Query(q => q
        .Range(r => r
            .LowerOrEquals(100)
            .OnField(f => f.Prices.FirstOrDefault().Cost))));

I've tried treating the Prices list as both a nested object and a child object, but ElasticSearch doesn't appear to be indexing the prices in that way because I get an error of "AggregationExecutionException[[nested] nested path [prices] is not nested]" and similar for HasChild queries. Is it possible to generate queries and aggregates in this way?

Upvotes: 6

Views: 2831

Answers (1)

ocuenca
ocuenca

Reputation: 39376

First you need to map the nested type:

 public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
   [ElasticProperty(Type = FieldType.Nested)]
    public IList<Price> Prices { get; set; }
}

After that, try execute this query:

 var cheapProducts = client.Search<Product>(s => s
            .From(0)
            .Size(1000)
            .Query(x => x.Term(p => p
                .Prices.First().CurrencyID, 1) && x.Range(r => r
                    .LowerOrEquals(100)
                    .OnField(f => f.Prices.FirstOrDefault().Cost))));

Upvotes: 3

Related Questions