Search code examples
c#elasticsearchnest

Search on nested array using NEST(ElasticSearch)


I want to filter data in nested array using Nest. Given I have following classes:

public class Package
{
    public IEnumerable<PackageItem> PackageItems { get; set; }
}

public class PackageItem
{
    public string Title { get; set; }
    public decimal Price { get; set; }
}

and I have index these data in ElasticSearch using NEST: Here is my data:

"hits" : [
  {
    "_index" : "packages",
    "_type" : "_doc",
    "_id" : "cf08b05c-c8b5-45cb-bca8-17267c3613fb",
    "_score" : 1.0,
    "_source" : {
      "id" : "cf08b05c-c8b5-45cb-bca8-17267c3613fb",
      "PackageItems" : [
        {
          "Title" : "some-title",
          "price" : 100000.0
        },
        {
          "Title" : "some-title",
          "price" : 200000.0
        }
      ]
    }
  },
  {
    "_index" : "packages",
    "_type" : "_doc",
    "_id" : "39436cb3-483e-4fb4-92e4-4e06ecad27a1",
    "_score" : 1.0,
    "_source" : {
      "id" : "39436cb3-483e-4fb4-92e4-4e06ecad27a1",
      "PackageItems" : [
        {
          "Title" : "some-title",
          "price" : 100000.0
        },
        {
          "Title" : "some-title",
          "price" : 400000.0
        }
      ]
    }
  },
  {
    "_index" : "packages",
    "_type" : "_doc",
    "_id" : "cd5d3587-838b-46ec-babc-d756c5587396",
    "_score" : 1.0,
    "_source" : {
      "id" : "cd5d3587-838b-46ec-babc-d756c5587396",
      "PackageItems" : [
        {
          "Title" : "some-title",
          "price" : 300000.0
        },
        {
          "Title" : "some-title",
          "price" : 500000.0
        }
      ]
    }
  }
]

I want to filter packages with minimumPackageItemPrice:100000 and maximumPackageItemPrice:400000. What query should I execute using NEST?

The result should be like this:

"hits" : [
  {
    "_index" : "packages",
    "_type" : "_doc",
    "_id" : "cf08b05c-c8b5-45cb-bca8-17267c3613fb",
    "_score" : 1.0,
    "_source" : {
      "id" : "cf08b05c-c8b5-45cb-bca8-17267c3613fb",
      "PackageItems" : [
        {
          "Title" : "some-title",
          "price" : 100000.0
        },
        {
          "Title" : "some-title",
          "price" : 200000.0
        }
      ]
    }
  },
  {
    "_index" : "inventories",
    "_type" : "_doc",
    "_id" : "39436cb3-483e-4fb4-92e4-4e06ecad27a1",
    "_score" : 1.0,
    "_source" : {
      "id" : "39436cb3-483e-4fb4-92e4-4e06ecad27a1",
      "PackageItems" : [
        {
          "Title" : "some-title",
          "price" : 100000.0
        },
        {
          "Title" : "some-title",
          "price" : 400000.0
        }
      ]
    }
  }
]

Solution

  • To trigger range queries on Elasticsearch through NEST, you can make use of the range query. The approach is that you can construct a range query for your scenario and trigger that query through the NEST client's search method.

    Generating the range query for your case

    // This method builds the range query where price should be between 
    // 100000.0 and 400000.0 (inclusive range in this case)
    private static QueryContainer BuildNestedRangeQuery()
    {
        return new QueryContainerDescriptor<Package>()
            .Nested(n =>
                n.Path(p => p.PackageItems)
                 .Query(q => q
                    .Range(r => r
                        .Field(f => f.PackageItems.FirstOrDefault().Price)
                        .GreaterThanOrEquals(100000.0)
                        .LessThanOrEquals(400000.0))
                    )
                 )
            )
    }
    

    Triggering the search

    Search can be triggered through the NEST client as shown below. This may vary according to your own implementation. However, the above range query remains the same.

    // Replace T with type of your choice and client is NEST client
    var result = client.Search<T>(    
        .From(0)
        .Size(20)
        .Query(q => BuildNestedRangeQuery())
        // other methods that you want to chain go here
    )