meilisearch_sdk/
lib.rs

1//! # 🚀 Getting started
2//!
3//! ### Add Documents <!-- omit in TOC -->
4//!
5//! ```
6//! use meilisearch_sdk::client::*;
7//! use serde::{Serialize, Deserialize};
8//! use futures::executor::block_on;
9//!
10//! #[derive(Serialize, Deserialize, Debug)]
11//! struct Movie {
12//!     id: usize,
13//!     title: String,
14//!     genres: Vec<String>,
15//! }
16//!
17//!
18//! #[tokio::main(flavor = "current_thread")]
19//! async fn main() {
20//! #   let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
21//! #   let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
22//!     // Create a client (without sending any request so that can't fail)
23//!     let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
24//!
25//! #    let index = client.create_index("movies", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
26//!     // An index is where the documents are stored.
27//!     let movies = client.index("movies");
28//!
29//!     // Add some movies in the index. If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.
30//!     movies.add_documents(&[
31//!         Movie { id: 1, title: String::from("Carol"), genres: vec!["Romance".to_string(), "Drama".to_string()] },
32//!         Movie { id: 2, title: String::from("Wonder Woman"), genres: vec!["Action".to_string(), "Adventure".to_string()] },
33//!         Movie { id: 3, title: String::from("Life of Pi"), genres: vec!["Adventure".to_string(), "Drama".to_string()] },
34//!         Movie { id: 4, title: String::from("Mad Max"), genres: vec!["Adventure".to_string(), "Science Fiction".to_string()] },
35//!         Movie { id: 5, title: String::from("Moana"), genres: vec!["Fantasy".to_string(), "Action".to_string()] },
36//!         Movie { id: 6, title: String::from("Philadelphia"), genres: vec!["Drama".to_string()] },
37//!     ], Some("id")).await.unwrap();
38//! #   index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
39//! }
40//! ```
41//!
42//! With the `uid`, you can check the status (`enqueued`, `canceled`, `processing`, `succeeded` or `failed`) of your documents addition using the [task](https://www.meilisearch.com/docs/reference/api/tasks#get-task).
43//!
44//! ### Basic Search <!-- omit in TOC -->
45//!
46//! ```
47//! # use meilisearch_sdk::client::*;
48//! # use serde::{Serialize, Deserialize};
49//! # #[derive(Serialize, Deserialize, Debug)]
50//! # struct Movie {
51//! #    id: usize,
52//! #    title: String,
53//! #    genres: Vec<String>,
54//! # }
55//! # fn main() { tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
56//! #    let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
57//! #    let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
58//! #    let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
59//! #    let movies = client.create_index("movies_2", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
60//! // Meilisearch is typo-tolerant:
61//! println!("{:?}", client.index("movies_2").search().with_query("caorl").execute::<Movie>().await.unwrap().hits);
62//! # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
63//! # })}
64//! ```
65//!
66//! Output:
67//! ```text
68//! [Movie { id: 1, title: String::from("Carol"), genres: vec!["Romance", "Drama"] }]
69//! ```
70//!
71//! Json output:
72//! ```json
73//! {
74//!   "hits": [{
75//!     "id": 1,
76//!     "title": "Carol",
77//!     "genres": ["Romance", "Drama"]
78//!   }],
79//!   "offset": 0,
80//!   "limit": 10,
81//!   "processingTimeMs": 1,
82//!   "query": "caorl"
83//! }
84//! ```
85//!
86//! ### Custom Search <!-- omit in toc -->
87//!
88//! ```
89//! # use meilisearch_sdk::{client::*, search::*};
90//! # use serde::{Serialize, Deserialize};
91//! # #[derive(Serialize, Deserialize, Debug)]
92//! # struct Movie {
93//! #    id: usize,
94//! #    title: String,
95//! #    genres: Vec<String>,
96//! # }
97//! # fn main() { tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
98//! #   let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
99//! #   let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
100//! #    let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
101//! #    let movies = client.create_index("movies_3", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
102//! let search_result = client.index("movies_3")
103//!   .search()
104//!   .with_query("phil")
105//!   .with_attributes_to_highlight(Selectors::Some(&["*"]))
106//!   .execute::<Movie>()
107//!   .await
108//!   .unwrap();
109//! println!("{:?}", search_result.hits);
110//! # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
111//! # })}
112//! ```
113//!
114//! Json output:
115//! ```json
116//! {
117//!     "hits": [
118//!         {
119//!             "id": 6,
120//!             "title": "Philadelphia",
121//!             "_formatted": {
122//!                 "id": 6,
123//!                 "title": "<em>Phil</em>adelphia",
124//!                 "genre": ["Drama"]
125//!             }
126//!         }
127//!     ],
128//!     "offset": 0,
129//!     "limit": 20,
130//!     "processingTimeMs": 0,
131//!     "query": "phil"
132//! }
133//! ```
134//!
135//! ### Custom Search With Filters <!-- omit in TOC -->
136//!
137//! If you want to enable filtering, you must add your attributes to the `filterableAttributes`
138//! index setting.
139//!
140//! ```
141//! # use meilisearch_sdk::{client::*};
142//! # use serde::{Serialize, Deserialize};
143//! # fn main() { tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
144//! #    let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
145//! #    let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
146//! #    let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
147//! #    let movies = client.create_index("movies_4", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
148//! let filterable_attributes = [
149//!     "id",
150//!     "genres",
151//! ];
152//! client.index("movies_4").set_filterable_attributes(&filterable_attributes).await.unwrap();
153//! # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
154//! # })}
155//! ```
156//!
157//! You only need to perform this operation once.
158//!
159//! Note that Meilisearch will rebuild your index whenever you update `filterableAttributes`. Depending on the size of your dataset, this might take time. You can track the process using the [tasks](https://www.meilisearch.com/docs/reference/api/tasks#get-task).
160//!
161//! Then, you can perform the search:
162//!
163//! ```
164//! # use meilisearch_sdk::{client::*, search::*};
165//! # use serde::{Serialize, Deserialize};
166//! # #[derive(Serialize, Deserialize, Debug)]
167//! # struct Movie {
168//! #    id: usize,
169//! #    title: String,
170//! #    genres: Vec<String>,
171//! # }
172//! # fn main() { tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
173//! # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
174//! # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
175//! # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
176//! # let movies = client.create_index("movies_5", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
177//! # let filterable_attributes = [
178//! #     "id",
179//! #    "genres"
180//! # ];
181//! # movies.set_filterable_attributes(&filterable_attributes).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
182//! # movies.add_documents(&[
183//! #     Movie { id: 1, title: String::from("Carol"), genres: vec!["Romance".to_string(), "Drama".to_string()] },
184//! #     Movie { id: 2, title: String::from("Wonder Woman"), genres: vec!["Action".to_string(), "Adventure".to_string()] },
185//! #     Movie { id: 3, title: String::from("Life of Pi"), genres: vec!["Adventure".to_string(), "Drama".to_string()] },
186//! #     Movie { id: 4, title: String::from("Mad Max"), genres: vec!["Adventure".to_string(), "Science Fiction".to_string()] },
187//! #     Movie { id: 5, title: String::from("Moana"), genres: vec!["Fantasy".to_string(), "Action".to_string()] },
188//! #     Movie { id: 6, title: String::from("Philadelphia"), genres: vec!["Drama".to_string()] },
189//! # ], Some("id")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
190//! let search_result = client.index("movies_5")
191//!   .search()
192//!   .with_query("wonder")
193//!   .with_filter("id > 1 AND genres = Action")
194//!   .execute::<Movie>()
195//!   .await
196//!   .unwrap();
197//! println!("{:?}", search_result.hits);
198//! # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
199//! # })}
200//! ```
201//!
202//! Json output:
203//! ```json
204//! {
205//!   "hits": [
206//!     {
207//!       "id": 2,
208//!       "title": "Wonder Woman",
209//!       "genres": ["Action", "Adventure"]
210//!     }
211//!   ],
212//!   "offset": 0,
213//!   "limit": 20,
214//!   "estimatedTotalHits": 1,
215//!   "processingTimeMs": 0,
216//!   "query": "wonder"
217//! }
218//! ```
219//!
220//! ### Customize the `HttpClient` <!-- omit in TOC -->
221//!
222//! By default, the SDK uses [`reqwest`](https://docs.rs/reqwest/latest/reqwest/) to make http calls.
223//! The SDK lets you customize the http client by implementing the `HttpClient` trait yourself and
224//! initializing the `Client` with the `new_with_client` method.
225//! You may be interested by the `futures-unsend` feature which lets you specify a non-Send http client.
226//!
227//! ### Wasm support <!-- omit in TOC -->
228//!
229//! The SDK supports wasm through reqwest. You'll need to enable the `futures-unsend` feature while importing it, though.
230#![warn(clippy::all)]
231#![allow(clippy::needless_doctest_main)]
232
233/// Module to interact with the Batches API.
234pub mod batches;
235/// Module containing the [`Client`](client::Client) struct.
236pub mod client;
237/// Module representing the [documents] structures.
238pub mod documents;
239/// Module containing the [dumps] trait.
240pub mod dumps;
241/// Module containing the [`errors::Error`] struct.
242pub mod errors;
243/// Module related to runtime and instance features.
244pub mod features;
245/// Module containing the Index struct.
246pub mod indexes;
247/// Module containing the [`Key`](key::Key) struct.
248pub mod key;
249/// Module for Network configuration API (sharding/remotes).
250pub mod network;
251pub mod request;
252/// Module related to search queries and results.
253pub mod search;
254/// Module containing [`Settings`](settings::Settings).
255pub mod settings;
256/// Module related to [similar queries](https://www.meilisearch.com/docs/learn/ai_powered_search/retrieve_related_search_results#return-similar-documents).
257pub mod similar;
258/// Module containing the [snapshots](snapshots::create_snapshot)-feature.
259pub mod snapshots;
260/// Module representing the [`TaskInfo`](task_info::TaskInfo)s.
261pub mod task_info;
262/// Module representing the [`Task`](tasks::Task)s.
263pub mod tasks;
264/// Module that generates tenant tokens.
265#[cfg(not(target_arch = "wasm32"))]
266mod tenant_tokens;
267/// Module containing utilizes functions.
268mod utils;
269/// Module to manage webhooks.
270pub mod webhooks;
271
272#[cfg(feature = "reqwest")]
273pub mod reqwest;
274
275#[cfg(feature = "reqwest")]
276pub type DefaultHttpClient = reqwest::ReqwestClient;
277
278#[cfg(not(feature = "reqwest"))]
279pub type DefaultHttpClient = std::convert::Infallible;
280
281#[cfg(test)]
282/// Support for the `IndexConfig` derive proc macro in the crate's tests.
283extern crate self as meilisearch_sdk;
284/// Can't assume that the user of proc_macro will have access to `async_trait` crate. So exporting the `async-trait` crate from `meilisearch_sdk` in a hidden module.
285#[doc(hidden)]
286pub mod macro_helper {
287    pub use async_trait::async_trait;
288}