codigube
codigube

Reputation: 1256

How to dynamically create query using NEST for Elasticsearch

I am trying to use NEST to create search query dynamically based on user's input.

I have a Filter class with user inputs:

Public class ProductFilter
{
    public string Name { get; set; }
    public DateTime? PublishDateFrom { get; set; }
}

But for the properties like Name and PublishDateFrom , they could be empty or null if a user doesn't specify.

So when doing search using NEST like the following code:

var response1 = await client.SearchAsync<ProjectDocument>(s => s
      .Index(Indices.Parse("products"))
      .From(0)
      .Size(10000)
      .Type("product")
      .Query(q => q
           .....
      )
);

I want to cover the case when Name or PublishDateFrom is empty or null in one search all.

Right now if I use:

 ...
  .Query(q => q
         .Term(p => p.Name, filter.Name)

When filter.Name is empty or null, then the search result is empty. I want to something like: if the filter.Name is empty or null, the Term Query related to Name is not executed or included in the SearchAsync call. When both Name and PublishDateFrom are empty or null, then the query should be using .MatchAll().

I am trying to use Bool Query, but can't handle this case either.

Is there any good way to solve this problem?

Upvotes: 3

Views: 4741

Answers (1)

Russ Cam
Russ Cam

Reputation: 125498

NEST already supports this with a concept of Conditionless queries. Examine the output in each of the following

var filter = new ProductFilter();

client.Search<ProjectDocument>(s => s
    .Index("products")
    .From(0)
    .Size(10000)
    .Query(q => +q
        .Term(p => p.Name, filter.Name) && +q 
        .DateRange(d => d
            .Field(f => f.PublishDateFrom)
            .GreaterThan(filter.PublishDateFrom)
        )
    )
);

This query takes advantage of query operator overloading to more easily build a bool query.

With both null properties on the filter instance, the search request is

POST http://localhost:9200/products/projectdocument/_search?pretty=true 
{
  "from": 0,
  "size": 10000
}

No query specified is the same as a match_all query.


If we change filter to

var filter = new ProductFilter
{
    Name = "foo"
};

then we get the following request

POST http://localhost:9200/products/projectdocument/_search?pretty=true 
{
  "from": 0,
  "size": 10000,
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "name": {
              "value": "foo"
            }
          }
        }
      ]
    }
  }
}

And if we change filter to

var filter = new ProductFilter
{
    Name = "foo",
    PublishDateFrom = DateTime.UtcNow.Date
};

We get

POST http://localhost:9200/products/projectdocument/_search?pretty=true 
{
  "from": 0,
  "size": 10000,
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "name": {
              "value": "foo"
            }
          }
        },
        {
          "range": {
            "publishDateFrom": {
              "gt": "2017-06-25T00:00:00Z"
            }
          }
        }
      ]
    }
  }
}

Upvotes: 6

Related Questions