Skip to main content

flusso_query/
lib.rs

1//! `flusso-query` — a typed query client for a flusso-maintained search index.
2//!
3//! Targets OpenSearch and Elasticsearch 7.x, which share the `_search` query DSL
4//! this crate emits; any future backend divergence is handled on the [`Client`],
5//! not by separate crates.
6//!
7//! This is the **runtime** layer described in [`CLIENT.md`](../../../CLIENT.md):
8//! the transport, the field-handle / [`Query`] / [`Search`] builder, and the
9//! typed [`SearchResponse`]. It is generic over the caller's document type `T`.
10//!
11//! # What this first cut covers
12//!
13//! - [`Client`] transport over OpenSearch (`connect`, `basic_auth`, search,
14//!   msearch, get).
15//! - Field handles ([`Keyword`], [`Text`], [`Bool`], [`Number`], [`Date`],
16//!   [`Nested`], [`Binary`], [`Json`]) with operators that build a [`Query`].
17//! - [`Query`] composition (`and` / `or` / `not`) and the [`Search`] bool-clause
18//!   builder (`query` / `filter` / `must_not` / `should`, plus `sort` / `from` /
19//!   `size` / `raw`). A [`Search`] is a plain client-free value — build it
20//!   anywhere, store and reuse it; a [`Client`] appears only at the terminals:
21//!   [`Search::send`] (a typed page), [`Search::ids`] (a page of bare document
22//!   ids, `_source: false`), or [`Search::count`] (just the number of matches,
23//!   via `_count`).
24//! - Several searches in one round-trip: [`Client::msearch`] (a tuple of
25//!   `&Search<T>`, mixed document types, one typed response per slot) and
26//!   [`Client::msearch_all`] (a slice of one type).
27//! - Combined (blended) search: [`FlussoMultiDocument`] — a caller-owned enum
28//!   with one variant per document type — and its [`MultiSearch`] builder. One
29//!   query across all the union's indexes, one relevance-ranked result list,
30//!   each hit decoded into the variant matching its physical `_index`.
31//! - Typed [`SearchResponse`] / [`Hit`].
32//!
33//! Also covered: optional filters (`Option<Q>` is a [`Query`]); object/to-one-join
34//! handles ([`Object`]); shaping returned nested arrays ([`Search::filter_nested`]
35//! with [`Nested::matching`], via `inner_hits`); and scope-tagged queries —
36//! [`Query`]`<S>` carries the scope `S` it was built in ([`Root`] for the document
37//! root and flattened objects, the element type for a `nested` array), so a nested
38//! query must be lifted through [`Nested::any`]/[`Nested::all`] before it can join a
39//! root query; the compiler enforces it.
40//!
41//! # Not yet built (see CLIENT.md for the endgame)
42//!
43//! - The `#[derive(FlussoDocument)]` and `#[derive(FlussoMultiDocument)]`
44//!   proc-macros live in `flusso-query-derive` (the `derive` feature). Without
45//!   them, document structs + handles and union impls are written by hand —
46//!   exactly the calls this crate exposes (see the integration tests).
47//! - `filter_nested`'s `keep_source()` opt-out (it always replaces the array in
48//!   `source` today) and a typed `hit.nested(handle)` accessor.
49//!
50//! # Example (hand-written until the derive lands)
51//!
52//! ```no_run
53//! use flusso_query::{Client, Keyword, Number, Nested};
54//!
55//! #[derive(serde::Deserialize)]
56//! struct User {
57//!     email: String,
58//!     #[serde(rename = "orderCount")]
59//!     order_count: i64,
60//! }
61//!
62//! impl User {
63//!     fn email() -> Keyword { Keyword::at("email") }
64//!     fn order_count() -> Number<i64> { Number::at("orderCount") }
65//!     fn query() -> flusso_query::Search<User> {
66//!         flusso_query::Search::new("users", "xxxxxx")
67//!     }
68//! }
69//!
70//! # async fn run() -> flusso_query::Result<()> {
71//! // A query is a plain value — no client involved while building it.
72//! let busy = User::query()
73//!     .filter(User::email().eq("ada@example.com"))
74//!     .filter(User::order_count().gte(5))
75//!     .size(20);
76//!
77//! // The client appears once, when it's time to run.
78//! let client = Client::connect("https://localhost:9200")?;
79//! let page = busy.send(&client).await?;
80//! println!("{} matches", page.total);
81//! # Ok(())
82//! # }
83//! ```
84
85mod client;
86mod error;
87mod handles;
88mod msearch;
89mod multi;
90mod query;
91mod search;
92
93#[cfg(test)]
94mod tests;
95
96pub use client::Client;
97pub use error::{Error, Result};
98pub use handles::{
99    Binary, Bool, BoostingQuery, CombinedFieldsQuery, ConstantScoreQuery, Date, DisMaxQuery,
100    DistanceFeatureQuery, EqQuery, FlussoValue, FunctionScoreQuery, FuzzyQuery, Geo,
101    GeoDistanceQuery, GeoPoint, IdsQuery, Json, Keyword, MatchQuery, MoreLikeThisQuery,
102    MultiMatchQuery, Nested, NestedProjection, NestedQuery, Number, Object, PrefixQuery,
103    QueryStringQuery, RangeQuery, RankFeatureQuery, RegexpQuery, ScriptQuery, ScriptScoreQuery,
104    SimpleQueryStringQuery, Sort, SortMode, SortOrder, TermQuery, TermsQuery, Text, WildcardQuery,
105    boosting, combined_fields, constant_score, dis_max, distance_feature, function_score, ids,
106    kind, more_like_this, multi_match, query_string, rank_feature, script, script_score,
107    simple_query_string,
108};
109pub use msearch::MsearchBundle;
110pub use multi::{FlussoMultiDocument, MultiSearch};
111pub use query::{AsQuery, Query, Root};
112pub use search::{FlussoDocument, Highlight, Hit, Search, SearchResponse};
113
114/// `#[derive(FlussoDocument)]` — generates the typed query surface for a
115/// hand-written document struct (its field handles) and implements the
116/// [`FlussoDocument`](trait@FlussoDocument) trait (`INDEX`/`SCHEMA_HASH` +
117/// `search`/`get`). See [`CLIENT.md`](../../../CLIENT.md). Enabled by the
118/// `derive` feature.
119#[cfg(feature = "derive")]
120pub use flusso_query_derive::FlussoDocument;
121
122/// `#[derive(FlussoValue)]` — implements [`trait@FlussoValue`] for an enum or newtype
123/// wrapper, so it may stand in for a field of the chosen kind (`#[flusso(keyword)]`
124/// — the default — `#[flusso(text)]`, `#[flusso(number)]`, or `#[flusso(date)]`)
125/// in a [`FlussoDocument`] struct. Enabled by the `derive` feature.
126#[cfg(feature = "derive")]
127pub use flusso_query_derive::FlussoValue;
128
129/// `#[derive(FlussoMultiDocument)]` — implements [`trait@FlussoMultiDocument`]
130/// for an enum with one single-field variant per document type (the
131/// combined-search union): the generated impl lists every variant's index and
132/// decodes each hit into the variant matching its physical `_index`. Enabled
133/// by the `derive` feature.
134#[cfg(feature = "derive")]
135pub use flusso_query_derive::FlussoMultiDocument;
136
137// The multi-document derive's generated code deserializes variant payloads;
138// routing it through this re-export keeps it on this crate's `serde_json`.
139// Hidden: not API.
140#[doc(hidden)]
141pub use serde_json as __serde_json;
142
143/// `rust_decimal::Decimal`, re-exported for `decimal` fields. Enabled by the
144/// `decimal` feature.
145#[cfg(feature = "decimal")]
146pub use rust_decimal::Decimal;
147
148/// `chrono`, re-exported for `date`/`timestamp` fields. Enabled by the `chrono`
149/// feature.
150#[cfg(feature = "chrono")]
151pub use chrono;
152
153/// `uuid`, re-exported for `keyword` id / foreign-key fields. With this feature
154/// a `uuid::Uuid` field needs no `#[flusso(skip)]` and `id().eq(some_uuid)`
155/// works without `.to_string()`. Enabled by the `uuid` feature.
156#[cfg(feature = "uuid")]
157pub use uuid;