6

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?

Jackson Murph
  • 63
  • 1
  • 5
  • check this post [How can I use nested types with NEST client for Elastic Search](http://stackoverflow.com/questions/17834767/how-can-i-use-nested-types-with-nest-client-for-elastic-search). It might help – ocuenca Dec 16 '14 at 16:12
  • Unfortunately that answer appears to be out of date. As noted in the most recent response the NestedObject fluent method doesn't seem to work any longer, and I've tried decorating the Prices property with the [ElasticProperty(Type = FieldType.Nested)] attribute but Prices is still not being indexed as a nested object. If I am able to get the mapping down, would treating the prices as a nested object allow me to calculate aggregates the way I described above? – Jackson Murph Dec 16 '14 at 18:13

1 Answers1

3

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))));
ocuenca
  • 38,548
  • 11
  • 89
  • 102
  • That gets all of the criteria I'm looking for into the query, but it is still retrieving results with a price < 100 in any currency. I think I'm running into the problem described here [link](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-nested-type.html) I think your first response was on the right path, I need to be mapping the prices as a nested type. Create a new answer saying that and I'll checkmark it. – Jackson Murph Dec 17 '14 at 14:58