Search code examples
elasticsearchnest

Elastic search recreating query in NEST not working


Im trying to recreate query in from Kibana dev into NEST but it's not giving me the same results.

The query I run in Kibana works perfectly returning 1 result

Here is my Kibana query:

GET /cats/_doc/_search
{   
"query":{
    "bool" : {     
        "minimum_should_match" :3,
        "should": [
           {"term" : { "name" : "cats" }},
           {"term" : { "name" : "are" }},
           {"term" : { "name" : "craze" }}

         ]
      }
   }
}

When I create the query in NEST it returns no results except when I change the minimum_should_match to 1 (it then returns 2 results )

Here is my NEST query:

        string[] tmp = "Cats are craze".ToLower().Split(new string[] { " " }, StringSplitOptions.None);

        var cats = ElasticMain.Search<dynamic>(s => s.From(from).Size(20).Query(
                  q => q.Bool(
                      b => b.MinimumShouldMatch(tmp.Length).Should(
                          l => l.Terms(
                              t => t.Name("name").Field("name").Terms(tmp)))

               )));

What am I doing wrong?


Solution

  • You are not building the same query in NEST as you have in Kibana; the former is using a terms query, whilst the latter is using three term queries in a bool query should clause. The semantics of these two queries in combination with minimum should match is different.

    The same query in NEST is

    var client = new ElasticClient();
    
    string[] tmp = "Cats are craze".ToLower().Split(new string[] { " " }, StringSplitOptions.None);
    var from = 0;
    
    var searchResponse = client.Search<dynamic>(s => s
        .From(from)
        .Size(20)
        .Query(q => q
            .Bool(b =>
            {
                b.MinimumShouldMatch(tmp.Length);
                var shouldQueries = 
                    new List<Func<QueryContainerDescriptor<dynamic>, QueryContainer>>(tmp.Length);
    
                for (var i = 0; i < tmp.Length; i++)
                {
                    var value = tmp[i];              
                    shouldQueries.Add(qc => qc.Term(t => t
                        .Field("name")
                        .Value(value)
                    ));
                }
    
                b.Should(shouldQueries);
    
                return b;
            })
        )
    );
    

    which builds the following query

    {
      "from": 0,
      "query": {
        "bool": {
          "minimum_should_match": 3,
          "should": [
            {
              "term": {
                "name": {
                  "value": "cats"
                }
              }
            },
            {
              "term": {
                "name": {
                  "value": "are"
                }
              }
            },
            {
              "term": {
                "name": {
                  "value": "craze"
                }
              }
            }
          ]
        }
      },
      "size": 20
    }
    

    When the number of should clauses that must match is equal to minimum_should_match as in this example, it's effectively the same as saying that they are all must clauses (without the minimum_should_match)

    var client = new ElasticClient();
    
    string[] tmp = "Cats are craze".ToLower().Split(new string[] { " " }, StringSplitOptions.None);
    var from = 0;
    
    var searchResponse = client.Search<dynamic>(s => s
        .From(from)
        .Size(20)
        .Query(q => 
            tmp.Aggregate((QueryContainer)null, (qc, v) => qc && q.Term("name", v))
        )
    );
    

    which takes advantage of operator overloading on NEST queries to && them together, to build the query

    {
      "from": 0,
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "name": {
                  "value": "cats"
                }
              }
            },
            {
              "term": {
                "name": {
                  "value": "are"
                }
              }
            },
            {
              "term": {
                "name": {
                  "value": "craze"
                }
              }
            }
          ]
        }
      },
      "size": 20
    }