Search code examples
elasticsearchnest

Elastic search query with Nest, search for text, but also filtering by flags


I have the following query:

return Q
    .MultiMatch(Fu => Fu
        .Fields(F => F
            .Field(Ff => Ff.Tags)
            .Field(Ff => Ff.Title)
        )
        .Query(Terms)
        .Fuzziness(Fuzziness.EditDistance(2))
    );

How can I add a secondary part to the query that would perform:

"If the document has NO 'score' field OR if 'score' <= 1"

I don't see how to add this part with the API.


Solution

  • Assuming a POCO something like

    public class MyDocument 
    {
        public string[] Tags { get; set; }
    
        public string Title { get; set; }
    
        public int Score { get; set; }
    }
    

    You can use the overloaded operators on queries to construct compound bool queries, to satisfy the requirements

    var client = new ElasticClient(settings);
    
    var terms = "foo bar baz";
    
    var searchResponse = client.Search<MyDocument>(s => s
        .Query(q => q
            .MultiMatch(mm => mm
                .Fields(f => f
                    .Field(ff => ff.Tags)
                    .Field(ff => ff.Title)
                )
                .Query(terms)
                .Fuzziness(Fuzziness.EditDistance(2))
            ) && +(!q
            .Exists(e => e
                .Field(f => f.Score)
            ) || q
            .Range(r => r
                .Field(f => f.Score)
                .LessThan(1)
            ))
        )
    ); 
    

    which results in the query

    {
      "query": {
        "bool": {
          "must": [
            {
              "multi_match": {
                "query": "foo bar baz",
                "fuzziness": 2,
                "fields": [
                  "tags",
                  "title"
                ]
              }
            }
          ],
          "filter": [
            {
              "bool": {
                "should": [
                  {
                    "bool": {
                      "must_not": [
                        {
                          "exists": {
                            "field": "score"
                          }
                        }
                      ]
                    }
                  },
                  {
                    "range": {
                      "score": {
                        "lt": 1.0
                      }
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    }
    

    This is the more succinct form of the query

    var searchResponse = client.Search<MyDocument>(s => s
        .Query(q => q
            .Bool(b => b
                .Must(mu => mu
                    .MultiMatch(mm => mm
                        .Fields(f => f
                            .Field(ff => ff.Tags)
                            .Field(ff => ff.Title)
                        )
                        .Query(terms)
                        .Fuzziness(Fuzziness.EditDistance(2))
                    )
                )
                .Filter(fi => fi
                    .Bool(bb => bb
                        .Should(sh => sh
                            .Bool(bbb => bbb
                                .MustNot(mn => mn
                                    .Exists(e => e
                                        .Field(f => f.Score)
                                    )
                                )
                            ), sh => sh
                            .Range(r => r
                                .Field(f => f.Score)
                                .LessThan(1)
                            )
                        )
                    )
                )
            )
        )
    );