elastic / elasticsearch-rs

Official Elasticsearch Rust Client
https://www.elastic.co/guide/en/elasticsearch/client/rust-api/current/index.html
Apache License 2.0
695 stars 70 forks source link

[ENHANCEMENT] High level query builder #211

Open t-smirnov opened 1 year ago

t-smirnov commented 1 year ago

Is your feature request related to a problem? Please describe. Not a problem, actually, but I would be way more cool, sometimes, to use builder pattern to build queries. Like it was implemented in high-level ES client for dotnet. Example: https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.17/writing-queries.html

Describe the solution you'd like Usage of builder pattern, like in dotnet client:

var searchResponse = _client.Search<Project>(s => s
    .Query(q => q
        .DateRange(r => r
            .Field(f => f.StartedOn)
            .GreaterThanOrEquals(new DateTime(2017, 01, 01))
            .LessThan(new DateTime(2018, 01, 01))
        )
    )
);

Rust

let req = builder::new().match("querytext").aggs(agg:Terms("field")).source(false)

Describe alternatives you've considered only using json! macro with the body.

Additional context some more complex query example.

var searchResponse = _client.Search<Project>(s => s
    .Query(q => q
        .Bool(b => b
            .Must(mu => mu
                .Match(m => m 
                    .Field(f => f.LeadDeveloper.FirstName)
                    .Query("Russ")
                ), mu => mu
                .Match(m => m 
                    .Field(f => f.LeadDeveloper.LastName)
                    .Query("Cam")
                )
            )
            .Filter(fi => fi
                 .DateRange(r => r
                    .Field(f => f.StartedOn)
                    .GreaterThanOrEquals(new DateTime(2017, 01, 01))
                    .LessThan(new DateTime(2018, 01, 01)) 
                )
            )
        )
    )
);
benfalk commented 1 year ago

Not sure if you have any interest; but I'm working on something to address this: https://github.com/benfalk/elastic_lens/blob/master/examples/simple_search.rs

benfalk commented 1 year ago

Here is an example of DSL so far:

use elastic_lens::{prelude::*, request::search::GeoPoint, response::SearchResults, Error};
use serde_json::Value;

// Getting the documents back as serde JSON
// The client will deserialize the `_source` to any type
// that implements for it.  See `examples/inventory_item.rs`
// for an example.
pub async fn complex_search() -> Result<SearchResults<Value>, Error> {
    let client = create_client()?;

    let mut search = Search::default();

    search.with(
        field("server.location")
            .within(500)
            .miles()
            .of(GeoPoint::new(12.2, 18.9)),
    );

    search.with(field("log.level").has_any_of(["error", "warning"]));
    search.with(field("log.trace").exists());

    search.with(if_any_match(|any| {
        any.with(field("service").contains("backend-core"));

        any.with(if_all_match(|all| {
            all.with(field("service").contains("frontend-core"));
            all.with(field("tags").has_any_of(["market-place", "registration"]));
        }));
    }));

    Ok(client.search(&search).await?)
}