Search code examples
c#linqelasticsearchnest

How to add conditional logic to a linq statement when calling elasticsearch using the NEST library in C#?


I am trying to use fluent LINQ in C# to query an Elasticsearch server using the .NET NEST library.

I want to build the LINQ statement based on the incoming search request. If the search request has price range, I want to add a range clause to the Elasticsearch request. Here is my code

var products = await client.SearchAsync<Product>(x =>
{
      x = x.Query(q =>
      {
          if (request.IsAvailable.HasValue)
          {
               // this gets called, but it is never added to the final elasticsearch call.
              q = q.Match(b => b.Field(bm => bm.IsAvailable).Query(request.IsAvailable.Value ? "true" : "false")) as QueryContainerDescriptor<Product>;
          }

          if (request.MinPrice.HasValue || request.MaxPrice.HasValue)
          {
              // this gets called, but it is never added to the final elasticsearch call.
              q = q.Range(r =>
              {
                  if (request.MinPrice.HasValue)
                  {
                      r = r.Field(x => x.Price).GreaterThanOrEquals(request.MinPrice.Value);
                  }

                  if (request.MaxPrice.HasValue)
                  {
                      r = r.Field(x => x.Price).LessThanOrEquals(request.MaxPrice.Value);
                  }
                  return r;
              }) as QueryContainerDescriptor<Product>;
          }

          if (request.Types != null && request.Types.Length > 0)
          {
              // this gets called, but it is never added to the final elasticsearch call.
              q = q.Terms(t => t.Field(f => f.Type).Terms(request.Types)) as QueryContainerDescriptor<Product>;
          }

          return q;
      });

      x = x.Source(s => s.Excludes(se =>
      {
          // This works! There fields are being excluded as expected
          if (request.ExcludeSummary)
          {
              se = se.Field(sef => sef.Summary);
          }

          if (request.ExcludeTimestamp)
          {
              se = se.Field(sef => sef.Timestamp);
          }

          if (request.ExcludeLabels)
          {
              se = se.Field(sef => sef.Labels);
          }

          if (request.ExcludeTags)
          {
              se = se.Field(sef => sef.Tags);
          }

          return se;
      }));

      return x;
});

All of my conditions with in the Query() does not get added to the elasticsearch request. Meaning the generated json request does not have clause for the price or IsAvaliable. I suspect casting is the culprit, but not sure how to fix it.

The Source() is working as expected. It adds the correct fields to the excludes list.

How can I correctly add query clause to the Query() section?


Solution

  • I figured out the issue. It seems that NEST takes the last clause and ignores all the previous clause.

    Here is what I have done to fix it

    var products = await client.SearchAsync<Product>(x =>
    {
          x = x.Query(q =>
          {
              QueryContainer qc = q;
    
              if (request.IsAvailable.HasValue)
              {
                  qc = qc && +q.Match(b => b.Field(bm => bm.IsAvailable).Query(request.IsAvailable.Value ? "true" : "false"));
              }
    
              if (request.MinPrice.HasValue || request.MaxPrice.HasValue)
              {
                  qc = qc && +q.Range(r =>
                  {
                      if (request.MinPrice.HasValue)
                      {
                          r = r.Field(x => x.Price).GreaterThanOrEquals(request.MinPrice.Value);
                      }
    
                      if (request.MaxPrice.HasValue)
                      {
                          r = r.Field(x => x.Price).LessThanOrEquals(request.MaxPrice.Value);
                      }
                      return r;
                  });
              }
    
              if (request.Types != null && request.Types.Length > 0)
              {
                  qc = qc && +q.Terms(t => t.Field(f => f.Type).Terms(request.Types));
              }
    
              return qc;
          });
    
          x = x.Source(s => s.Excludes(se =>
          {
              if (request.ExcludeSummary)
              {
                  se = se.Field(sef => sef.Summary);
              }
    
              if (request.ExcludeTimestamp)
              {
                  se = se.Field(sef => sef.Timestamp);
              }
    
              if (request.ExcludeLabels)
              {
                  se = se.Field(sef => sef.Labels);
              }
    
              if (request.ExcludeTags)
              {
                  se = se.Field(sef => sef.Tags);
              }
    
              return se;
          }));
    
          return x;
    });