Search code examples
c#elasticsearchnest

How to apply "in list" operator with Nest (C#) library using the elastic search?


I have an ElasticSearch server that I want to query. I want to replicate the behavior of in operator in a SQL server using the NEST library.

Here is a short version of the Product class which is what I am using to search where the Type field is in request.Types

public class Product
{
    [Text(Name = "types")]
    public string[] Types { get; set; }

    [Text(Name = "price")]
    public double Price { get; set; }

    // ...
}

Here is my query request using the Nest library

var properties = await client.SearchAsync<Product>(x =>
{
    x = x.Query(q =>
    {
        if (request.MinPrice.HasValue || request.MaxPrice.HasValue)
        {
            q.Range(r =>
           {
               if (request.MinPrice.HasValue)
               {
                   r = r.Field(x => x.Price).GreaterThanOrEquals((double)request.MinPrice.Value);
               }

               if (request.MaxPrice.HasValue)
               {
                   r = r.Field(x => x.Price).LessThanOrEquals((double)request.MaxPrice.Value);
               }
               return r;
           });
        }

        if (request.Types != null && request.Types.Length > 0)
        {
            
            // Here I need to query the Type field where field in request.Types
        }

        return q;
    });

    return x;
});

As you can see in the comment above, I want to add the "in clause" only when the request.Types has value and the server has at least one matching type.

How can I add in clause condition?


Solution

  • I can see two possible interpretations of the requirement:

    1. query documents that have Types that intersect with request.Types

      A terms query can achieve this. Typically, a terms query targets a field mapped as a keyword type, but Product.Types is mapped as a text type. You may want to consider changing the mapping to map as keyword, or map it as both text and keyword with a multi-field.

      With Product.Types mapped as a keyword type, the query would be

      q.Terms(t => t
          .Field(f => f.Types)
          .Terms(request.Types)
      );
      
    2. query documents that have all the exact Types specified in request.Types

      A terms set query can achieve this. As before, the Types field should be mapped as a keyword, then the query would be

      q.TermsSet(t => t
          .Field(f => f.Types)
          .Terms(request.Types)
          .MinimumShouldMatchScript(s => s
             .Source("params.request_types_len")
             .Params(d => d
                 .Add("request_types_len", request.Types.Length)
             )
          )
      );