0

I'm using elasticsearch nest within an asp.net mvc application.

Following elasticsearch query is throwing an exception because fields like categories and brands could be null or empty. How do add if statements and build the filters conditionally. Thank you!

I have to use bool & must to combine (AND) filters for search criteria. As an example a user want products in "shoes" category and retailer "macys".

 s.Query(q => q
                .Bool(bq => bq
                    .Must(
                        mq => mq.Filtered(fq => fq
                            .Filter(f => f.Terms("Categories", request.Categories))
                            ),
                        mq => mq.Filtered(fq => fq
                            .Filter(f => f.Terms("BrandName", request.Brands))
                            ),
                        mq => mq.Filtered(fq => fq
                            .Filter(f => f.Terms("RetailerName", request.Retailers))
                            ),
                        mq => mq.Filtered(fq => fq
                            .Filter(f => f.Terms("RetailerName", request.Retailers))
                            ),
                        mq => mq.Range(r => r
                            .OnField("SellingPrice")
                            .GreaterOrEquals((double)request.PriceRanges[0].Start)
                            .LowerOrEquals((double)request.PriceRanges[0].End)
                            )
                    )
                )
             );    
bittusarkar
  • 6,247
  • 3
  • 30
  • 50

1 Answers1

0

You don't have to care about null or empty values passed to queries, because NEST has feature called Conditionless Queries. Documentation says

If any of the queries would result in an empty query they won't be sent to Elasticsearch.

The cause of the exception are these lines of code:

mq => mq.Range(r => r
    .OnField("SellingPrice")
    .GreaterOrEquals((double)request.PriceRanges[0].Start)
    .LowerOrEquals((double)request.PriceRanges[0].End)
)

Propably PriceRanges is null or empty, and you are trying to access Start and End properties from a first element. Would be great if you are able to change a request class to something like below, in case you are using only first item from PriceRanges:

class Request
{
    public List<string> Categories { get; set; }
    public List<string> Brands { get; set; }
    public double? PriceRangeStart { get; set; }
    public double? PriceRangeEnd { get; set; }
}

Then your NEST query will look like:

s.Query(q => q
    .Bool(bq => bq
        .Must(
            mq => mq.Filtered(fq => fq
                .Filter(f => f.Terms("Categories", request.Categories))
                ),
            mq => mq.Filtered(fq => fq
                .Filter(f => f.Terms("BrandName", request.Brands))
                ),
            mq => mq.Filtered(fq => fq
                .Filter(f => f.Terms("RetailerName", request.Retailers))
                ),
            mq => mq.Range(r => r
                .OnField("SellingPrice")
                .GreaterOrEquals(request.PriceRangeStart)
                .LowerOrEquals(request.PriceRangeEnd)
                )
        )
    ));

For this request object

var request = new Request
{
    Brands = new List<string>{"brand"},
    PriceRangesEnd = 100
};

NEST produces following elasticsearch query

"query": {
  "bool": {
    "must": [
      {
        "filtered": {
          "filter": {
            "terms": {
              "BrandName": [
                "brand"
              ]
            }
          }
        }
      },
      {
        "range": {
          "SellingPrice": {
            "lte": "100"
          }
        }
      }
    ]
  }
}
Rob
  • 9,664
  • 3
  • 41
  • 43
  • Thank you! `f.Terms("Categories", request.Categories)` compares any of the categories. How do I compare all? For example "shoes", "gifts". I want return results where categories match "shoes" and "gifts" (not "shoes" or "gifts"). I do see an execution option for the terms filter, I don't know if that will help or not. – Matt Townsend Mar 29 '15 at 19:34
  • May you share your index mapping? – Rob Mar 29 '15 at 19:56
  • `mq => mq.Filtered(fq => fq .Filter(f => f.Terms("Categories", request.Categories,TermsExecution.And))` compares all but it's case sensitive. I have this field not_analyzed in the mapping. How do I make the comparison case insensitive? here's the mapping for the field `"Categories" : { "type" : "string", "index_name" : "Category", "index" : "not_analyzed" }` – Matt Townsend Mar 29 '15 at 20:06
  • Here's the full mapping. I'm using categories and few other fields for aggregation. `-d '{"settings" : {"number_of_shards" : 1 }, "mappings" : {"summary" : { "properties" : {"RetailerName" : { "type" : "string", "index" : "not_analyzed" }, "BrandName" : { "type" : "string", "index" : "not_analyzed" }, "SellingPrice" : { "type" : "float" }, "AffiliateMarketingVerbs" : { "type" : "string", "index_name" : "AffiliateMarketingVerb", "index" : "not_analyzed" }, "Categories" : { "type" : "string", "index_name" : "Category", "index" : "not_analyzed" } } } } }'` – Matt Townsend Mar 29 '15 at 20:23