Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 941
Jackson Murph Avatar asked Dec 16 '14 16:12

Jackson Murph


1 Answers

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))));
like image 88
octavioccl Avatar answered Nov 19 '22 17:11

octavioccl