elastic / elasticsearch-net

This strongly-typed, client library enables working with Elasticsearch. It is the official client maintained and supported by Elastic.
https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/index.html
Apache License 2.0
13 stars 1.15k forks source link

Add a "multiple query descriptors" example to basic docs #8399

Open seanwm opened 2 weeks ago

seanwm commented 2 weeks ago

Is your feature request related to a problem? Please describe. I need to write queries like this:

{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "propOne": "my_value_1"
          }
        },
        {
          "term":
          {
            "propTwo": "my_value_2"
          }
        }
      ]
    }
  }
}

I couldn't find any examples of that in the current documentation. Flailing around in VS Code I can end up with compilable code like this:

Query(q =>
                q.Bool(b => b
                    .Filter(f => f.Term(t => t.Field(f => f.PropOne).Value("my_value_1")))
                    .Filter(f => f.Term(t => t.Field(f => f.PropTwo).Value("my_value_2))))
                ));

But that doesn't work; only the "PropTwo" filter ends up in the resulting query. One answer I found via another tangentially-related issue here was to do something like this:

var filters = new List<Action<QueryDescriptor<MyType>>>();
filters.Add(f => f.Term(t => t.Field(f => f.PropOne).Value("my_value_1")));
filters.Add(f => f.Term(t => t.Field(f => f.PropTwo).Value("my_value_2")));

...and then include them in my query with .Filter(filters.ToArray()). If there's a way to do what I want simply by chaining, I'm not sure what it might be.

I am no doubt bad at intuiting fluent interfaces, but it felt frustrating to know what output I wanted but be unable to create that in code.

Describe the solution you'd like For me, it would have helped a lot to have one or two examples on a page like "Query examples" demonstrate how to have more than one query descriptor under something like "Filter()".

Additional context I appreciate the library!

flobernd commented 2 weeks ago

Hi @seanwm ,

providing an example for BoolQuery in the documentation is an excellent suggestion 🙂 I'll take care about adding this!

In the meantime:

await client.SearchAsync<Person>(s => s
    .Query(q =>
        q.Bool(b => b
            .Filter([ // <- use the "params" overload
                f => f.Term(t => t.Field(f => f.FirstName).Value("my_value_1")),
                f => f.Term(t => t.Field(f => f.Email).Value("my_value_2"))
            ])
        )
    )
);

Your code was very close. The params overload is a little bit tricky to use with the descriptor actions. Calling .Filter() multiple times just overrides the previous value - like you already noticed. This is always the case unless the fluent API method starts with something like Add..., Remove.... In these cases you can expect the method to alter the state accordingly, in all other situations you should assume the previous value to get overwritten.