Boolean Queries in Net Client

Hello,

As pointed out by @flobernd in my Github Issue related to working with the latest net client that the documentation for the latest is very sparse. Therefore, I'm trying to manage the AND operator in the Bool query, and it's quite different from the ones before Net API 5.x where we can use the && and || operators, Plus I tried the straight forward approach as I understoon that "the new client (starting from version 8.13) closely maps to the structure of the REST JSON structure":

 SearchResponse<PostDocument> searchResponse3 = await elasticsearchClient.SearchAsync<PostDocument>(s => s
     .Index(indexName)
     .From(0)
     .Size(10)
     .Query(q =>
     {
         q.Nested(n =>
         {
             //n.IgnoreUnmapped();
             n.Path(p => p.Categories)
             .Query(nq =>
             {
                 nq.Bool(b =>
                 {
                     b.Must(m =>
                     {
                         m.Match(mt =>
                         {
                             mt.Field(f => f.Categories.First().CategoryId)
                             .Query(category_dystopian.CategoryId.ToString());
                         });

                         m.Match(mt =>
                         {
                             mt.Field(f => f.Categories.First().CategoryId)
                             .Query(category_tragedy.CategoryId.ToString());
                         });
                     });
                 });
             });
         });
     })
 );

Debugging it would be:

{
  "query": {
    "nested": {
      "query": {
        "bool": {
          "must": {
            "match": {
              "categories.categoryId": {
                "query": "1a36c19d-8210-4e65-9cda-ca8c9c0ec04e"
              }
            }
          }
        }
      },
      "path": "categories"
    }
  },
  "from": 0,
  "size": 10
}

NESTED is already mapped, it gets just one category but does not perform the AND, as it only generated 1 line of match which should have been 2.

Unless there is a different way to implement AND , OR ?

Appreciated!

Hi @alikleitcr7 ,

your query was nearly correct.

Using m.Match on the same query descriptor 2 times, causes it to change it's content and only keep the configuration from the last invocation.

Instead, the params/array overload must be used:

csharp

SearchResponse<PostDocument> searchResponse3 = await client.SearchAsync<PostDocument>(s => s
	.Index(indexName)
	.From(0)
	.Size(10)
	.Query(q => q
		.Nested(n => n
			.Path(p => p.Categories)
			.Query(nq => nq
				.Bool(b => b
					.Must(
						// <- use the "params" overload
						m => m
							.Match(mt => mt
								.Field(f => f.Categories.First().CategoryId)
								.Query(category_dystopian.CategoryId.ToString())
							),
						m => m
							.Match(mt => mt
								.Field(f => f.Categories.First().CategoryId)
								.Query(category_tragedy.CategoryId.ToString())
							)
					)
				)
			)
		)
	)
);
1 Like

Thanks @flobernd !

I tried, and it resulted to the expected request query. However, is it correct that this is an AND? as it seems to not return any result. only when removing either one of the items. I tried with term as well, and removing the query by setting the value directly to the field as follows:

GET /test-posts/_search
{
  "query": {
    "nested": {
      "query": {
        "bool": {
          "must":     [
            {
              "term": {
                "categories.categoryId": "e5109ad9-9a73-4606-b4f8-c0130fbadcd9"
              }
            },
            {
              "term": {
                "categories.categoryId": "1a36c19d-8210-4e65-9cda-ca8c9c0ec04e"
              }
            }
          ]
        }
      },
      "path": "categories"
    }
  },
  "from": 0,
  "size": 10
}

results to:

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 0,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  }
}

given the following document:

{
  ...
  title: "Animal Farm",
  ....,
  categories: [
    {
      "categoryId": "e5109ad9-9a73-4606-b4f8-c0130fbadcd9",
      "title": "Dystopian",
    },
    {
      "categoryId": "1a36c19d-8210-4e65-9cda-ca8c9c0ec04e",
      "title": "Tragedy",
    }
  ]
},
...

with mapping:

@alikleitcr7 Yes, this is what AND does :sweat_smile: It only returns the results for which „all“ query conditions are matching.

In your case, you specify 2 different ids for the same field, which means that your condition is never true.

If you want to return documents with either id1 or id2 you would have to use OR (as this will return all documents for which „at least one“ of the conditions are matching).

I’m only on mobile right now and sadly can’t help you with the syntax for your case where you want to check the existence of 2 specific items in the nested array.

Interesting, I am aiming for something like contains. where the list of ids do exist in the nested array.

Then the following example also is never True Here?

An OR would not be the case, as it will get other documents with either ID1 or ID2 in their list.

I also tried using terms + keyword:

GET /test-posts/_search
{
 "query": {
    "nested": {
      "path": "categories",
      "query": {
        "bool": {
          "must": [
            {
              "terms": {
                "categories.categoryId.keyword": [
                  "e5109ad9-9a73-4606-b4f8-c0130fbadcd9",
                  "1a36c19d-8210-4e65-9cda-ca8c9c0ec04e" ]
              }
            }
          ]
        }
      }
    }
  },
  "from": 0,
  "size": 10
}

and an OR inside the AND

GET /test-posts/_search
{
 "query": {
    "nested": {
      "path": "categories",
      "query": {
        "bool": {
          "must": [
            {
              "bool": {
                "should": [
                  {
                    "term": {
                      "categories.categoryId": {
                        "value": "e5109ad9-9a73-4606-b4f8-c0130fbadcd9"
                      }
                    }
                  },
                    {
                    "term": {
                      "categories.categoryId": {
                        "value": "1a36c19d-8210-4e65-9cda-ca8c9c0ec04e"
                      }
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    }
  },
  "from": 0,
  "size": 10
}

Is there a specific way to handle AND on the same field in an array?

Yes, that example is as well slightly misleading as the condition can never be true.

I'm sorry, I think I missed the nested part in your initial query.

From this SO comment, the correct solution would be to build the query chain like this (pseudo code):

.Must(
  .Nested(
    .Term("categories.categoryId", "id1")
  ),
  .Nested(
    .Term("categories.categoryId", "id2")
  ),
)

So basically use the Must/AND query on the first level and put multiple Nested->Term queries inside.

1 Like