meilisearch_sdk/
indexes.rs

1use crate::{
2    client::Client,
3    documents::{DocumentDeletionQuery, DocumentQuery, DocumentsQuery, DocumentsResults},
4    errors::{Error, MeilisearchCommunicationError, MeilisearchError, MEILISEARCH_VERSION_HINT},
5    request::*,
6    search::*,
7    similar::*,
8    task_info::TaskInfo,
9    tasks::*,
10    DefaultHttpClient,
11};
12use serde::{de::DeserializeOwned, Deserialize, Serialize};
13use std::{collections::HashMap, fmt::Display, time::Duration};
14use time::OffsetDateTime;
15
16/// A Meilisearch [index](https://www.meilisearch.com/docs/learn/core_concepts/indexes).
17///
18/// # Example
19///
20/// You can create an index remotely and, if that succeed, make an `Index` out of it.
21/// See the [`Client::create_index`] method.
22/// ```
23/// # use meilisearch_sdk::{client::*, indexes::*};
24/// #
25/// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
26/// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
27/// #
28/// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
29/// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
30///
31/// // get the index called movies or create it if it does not exist
32/// let movies = client
33///     .create_index("index", None)
34///     .await
35///     .unwrap()
36///     // We wait for the task to execute until completion
37///     .wait_for_completion(&client, None, None)
38///     .await
39///     .unwrap()
40///     // Once the task finished, we try to create an `Index` out of it
41///     .try_make_index(&client)
42///     .unwrap();
43///
44/// assert_eq!(movies.as_ref(), "index");
45/// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
46/// # });
47/// ```
48///
49/// Or, if you know the index already exist remotely you can create an [Index] with its builder.
50/// ```
51/// # use meilisearch_sdk::{client::*, indexes::*};
52/// #
53/// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
54/// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
55/// #
56/// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
57/// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
58///
59/// // Meilisearch would be able to create the index if it does not exist during:
60/// // - the documents addition (add and update routes)
61/// // - the settings update
62/// let movies = Index::new("movies", client);
63///
64/// assert_eq!(movies.uid, "movies");
65/// # });
66/// ```
67#[derive(Debug, Serialize, Clone)]
68#[serde(rename_all = "camelCase")]
69pub struct Index<Http: HttpClient = DefaultHttpClient> {
70    #[serde(skip_serializing)]
71    pub client: Client<Http>,
72    pub uid: String,
73    #[serde(with = "time::serde::rfc3339::option")]
74    pub updated_at: Option<OffsetDateTime>,
75    #[serde(with = "time::serde::rfc3339::option")]
76    pub created_at: Option<OffsetDateTime>,
77    pub primary_key: Option<String>,
78}
79
80impl<Http: HttpClient> Index<Http> {
81    pub fn new(uid: impl Into<String>, client: Client<Http>) -> Index<Http> {
82        Index {
83            uid: uid.into(),
84            client,
85            primary_key: None,
86            created_at: None,
87            updated_at: None,
88        }
89    }
90    /// Internal Function to create an [Index] from `serde_json::Value` and [Client].
91    pub(crate) fn from_value(
92        raw_index: serde_json::Value,
93        client: Client<Http>,
94    ) -> Result<Index<Http>, Error> {
95        #[derive(Deserialize, Debug)]
96        #[allow(non_snake_case)]
97        struct IndexFromSerde {
98            uid: String,
99            #[serde(with = "time::serde::rfc3339::option")]
100            updatedAt: Option<OffsetDateTime>,
101            #[serde(with = "time::serde::rfc3339::option")]
102            createdAt: Option<OffsetDateTime>,
103            primaryKey: Option<String>,
104        }
105
106        let i: IndexFromSerde = serde_json::from_value(raw_index).map_err(Error::ParseError)?;
107
108        Ok(Index {
109            uid: i.uid,
110            client,
111            created_at: i.createdAt,
112            updated_at: i.updatedAt,
113            primary_key: i.primaryKey,
114        })
115    }
116
117    /// Update an [Index].
118    ///
119    /// # Example
120    ///
121    /// ```
122    /// # use meilisearch_sdk::{client::*, indexes::*, task_info::*, tasks::*};
123    /// #
124    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
125    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
126    /// #
127    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
128    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
129    /// # let mut index = client
130    /// #   .create_index("index_update", None)
131    /// #   .await
132    /// #   .unwrap()
133    /// #   .wait_for_completion(&client, None, None)
134    /// #   .await
135    /// #   .unwrap()
136    /// # // Once the task finished, we try to create an `Index` out of it
137    /// #   .try_make_index(&client)
138    /// #   .unwrap();
139    /// #
140    /// index.primary_key = Some("special_id".to_string());
141    /// let task = index.update()
142    ///     .await
143    ///     .unwrap()
144    ///     .wait_for_completion(&client, None, None)
145    ///     .await
146    ///     .unwrap();
147    ///
148    /// let index = client.get_index("index_update").await.unwrap();
149    ///
150    /// assert_eq!(index.primary_key, Some("special_id".to_string()));
151    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
152    /// # });
153    /// ```
154    pub async fn update(&self) -> Result<TaskInfo, Error> {
155        let mut index_update = IndexUpdater::new(self, &self.client);
156
157        if let Some(ref primary_key) = self.primary_key {
158            index_update.with_primary_key(primary_key);
159        }
160
161        index_update.execute().await
162    }
163
164    /// Delete the index.
165    ///
166    /// # Example
167    ///
168    /// ```
169    /// # use meilisearch_sdk::{client::*, indexes::*};
170    /// #
171    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
172    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
173    /// #
174    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
175    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
176    /// # let index = client.create_index("delete", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
177    ///
178    /// // get the index named "movies" and delete it
179    /// let index = client.index("delete");
180    /// let task = index.delete().await.unwrap();
181    ///
182    /// client.wait_for_task(task, None, None).await.unwrap();
183    /// # });
184    /// ```
185    pub async fn delete(self) -> Result<TaskInfo, Error> {
186        self.client
187            .http_client
188            .request::<(), (), TaskInfo>(
189                &format!("{}/indexes/{}", self.client.host, self.uid),
190                Method::Delete { query: () },
191                202,
192            )
193            .await
194    }
195
196    /// Search for documents matching a specific query in the index.
197    ///
198    /// See also [`Index::search`].
199    ///
200    /// # Example
201    ///
202    /// ```
203    /// # use serde::{Serialize, Deserialize};
204    /// # use meilisearch_sdk::{client::*, indexes::*, search::*};
205    /// #
206    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
207    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
208    /// #
209    /// #[derive(Serialize, Deserialize, Debug)]
210    /// struct Movie {
211    ///     name: String,
212    ///     description: String,
213    /// }
214    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
215    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
216    /// let movies = client.index("execute_query");
217    ///
218    /// // add some documents
219    /// # movies.add_or_replace(&[Movie{name:String::from("Interstellar"), description:String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")},Movie{name:String::from("Unknown"), description:String::from("Unknown")}], Some("name")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
220    ///
221    /// let query = SearchQuery::new(&movies).with_query("Interstellar").with_limit(5).build();
222    /// let results = movies.execute_query::<Movie>(&query).await.unwrap();
223    ///
224    /// assert!(results.hits.len() > 0);
225    /// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
226    /// # });
227    /// ```
228    pub async fn execute_query<T: 'static + DeserializeOwned + Send + Sync>(
229        &self,
230        body: &SearchQuery<'_, Http>,
231    ) -> Result<SearchResults<T>, Error> {
232        self.client
233            .http_client
234            .request::<(), &SearchQuery<Http>, SearchResults<T>>(
235                &format!("{}/indexes/{}/search", self.client.host, self.uid),
236                Method::Post { body, query: () },
237                200,
238            )
239            .await
240    }
241
242    /// Search for documents matching a specific query in the index.
243    ///
244    /// See also [`Index::execute_query`].
245    ///
246    /// # Example
247    ///
248    /// ```
249    /// # use serde::{Serialize, Deserialize};
250    /// # use meilisearch_sdk::{client::*, indexes::*, search::*};
251    /// #
252    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
253    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
254    /// #
255    /// #[derive(Serialize, Deserialize, Debug)]
256    /// struct Movie {
257    ///     name: String,
258    ///     description: String,
259    /// }
260    ///
261    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
262    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
263    /// let mut movies = client.index("search");
264    /// # // add some documents
265    /// # movies.add_or_replace(&[Movie{name:String::from("Interstellar"), description:String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")},Movie{name:String::from("Unknown"), description:String::from("Unknown")}], Some("name")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
266    ///
267    /// let results = movies.search()
268    ///     .with_query("Interstellar")
269    ///     .with_limit(5)
270    ///     .execute::<Movie>()
271    ///     .await
272    ///     .unwrap();
273    ///
274    /// assert!(results.hits.len() > 0);
275    /// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
276    /// # });
277    /// ```
278    #[must_use]
279    pub fn search(&self) -> SearchQuery<'_, Http> {
280        SearchQuery::new(self)
281    }
282
283    /// Returns the facet stats matching a specific query in the index.
284    ///
285    /// See also [`Index::facet_search`].
286    ///
287    /// # Example
288    ///
289    /// ```
290    /// # use serde::{Serialize, Deserialize};
291    /// # use meilisearch_sdk::{client::*, indexes::*, search::*};
292    /// #
293    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
294    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
295    /// #
296    /// #[derive(Serialize, Deserialize, Debug)]
297    /// struct Movie {
298    ///     name: String,
299    ///     genre: String,
300    /// }
301    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
302    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
303    /// let movies = client.index("execute_query2");
304    ///
305    /// // add some documents
306    /// # movies.add_or_replace(&[Movie{name:String::from("Interstellar"), genre:String::from("scifi")},Movie{name:String::from("Inception"), genre:String::from("drama")}], Some("name")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
307    /// # movies.set_filterable_attributes(["genre"]).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
308    ///
309    /// let query = FacetSearchQuery::new(&movies, "genre").with_facet_query("scifi").build();
310    /// let res = movies.execute_facet_query(&query).await.unwrap();
311    ///
312    /// assert!(res.facet_hits.len() > 0);
313    /// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
314    /// # });
315    /// ```
316    pub async fn execute_facet_query(
317        &self,
318        body: &FacetSearchQuery<'_, Http>,
319    ) -> Result<FacetSearchResponse, Error> {
320        self.client
321            .http_client
322            .request::<(), &FacetSearchQuery<Http>, FacetSearchResponse>(
323                &format!("{}/indexes/{}/facet-search", self.client.host, self.uid),
324                Method::Post { body, query: () },
325                200,
326            )
327            .await
328    }
329
330    pub fn facet_search<'a>(&'a self, facet_name: &'a str) -> FacetSearchQuery<'a, Http> {
331        FacetSearchQuery::new(self, facet_name)
332    }
333
334    /// Get one document using its unique id.
335    ///
336    /// Serde is needed. Add `serde = {version="1.0", features=["derive"]}` in the dependencies section of your Cargo.toml.
337    ///
338    /// # Example
339    ///
340    /// ```
341    /// # use serde::{Serialize, Deserialize};
342    /// # use meilisearch_sdk::{client::*, indexes::*};
343    /// #
344    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
345    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
346    /// #
347    /// #[derive(Serialize, Deserialize, Debug, PartialEq)]
348    /// struct Movie {
349    ///     name: String,
350    ///     description: String
351    /// }
352    ///
353    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
354    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
355    /// let movies = client.index("get_document");
356    /// # movies.add_or_replace(&[Movie{name:String::from("Interstellar"), description:String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")}], Some("name")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
357    ///
358    /// // retrieve a document (you have to put the document in the index before)
359    /// let interstellar = movies.get_document::<Movie>("Interstellar").await.unwrap();
360    ///
361    /// assert_eq!(interstellar, Movie {
362    ///     name: String::from("Interstellar"),
363    ///     description: String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage."),
364    /// });
365    /// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
366    /// # });
367    /// ```
368    pub async fn get_document<T: 'static + DeserializeOwned + Send + Sync>(
369        &self,
370        document_id: &str,
371    ) -> Result<T, Error> {
372        let url = format!(
373            "{}/indexes/{}/documents/{}",
374            self.client.host, self.uid, document_id
375        );
376        self.client
377            .http_client
378            .request::<(), (), T>(&url, Method::Get { query: () }, 200)
379            .await
380    }
381
382    /// Get one document with parameters.
383    ///
384    /// # Example
385    ///
386    /// ```
387    /// # use meilisearch_sdk::{client::*, indexes::*, documents::*};
388    /// # use serde::{Deserialize, Serialize};
389    /// #
390    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
391    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
392    /// #
393    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
394    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
395    /// #[derive(Debug, Serialize, Deserialize, PartialEq)]
396    /// struct MyObject {
397    ///     id: String,
398    ///     kind: String,
399    /// }
400    ///
401    /// #[derive(Debug, Serialize, Deserialize, PartialEq)]
402    /// struct MyObjectReduced {
403    ///     id: String,
404    /// }
405    /// # let index = client.index("document_query_execute");
406    /// # index.add_or_replace(&[MyObject{id:"1".to_string(), kind:String::from("a kind")},MyObject{id:"2".to_string(), kind:String::from("some kind")}], None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
407    ///
408    /// let mut document_query = DocumentQuery::new(&index);
409    /// document_query.with_fields(["id"]);
410    ///
411    /// let document = index.get_document_with::<MyObjectReduced>("1", &document_query).await.unwrap();
412    ///
413    /// assert_eq!(
414    ///     document,
415    ///     MyObjectReduced { id: "1".to_string() }
416    /// );
417    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
418    /// # });
419    pub async fn get_document_with<T: 'static + DeserializeOwned + Send + Sync>(
420        &self,
421        document_id: &str,
422        document_query: &DocumentQuery<'_, Http>,
423    ) -> Result<T, Error> {
424        let url = format!(
425            "{}/indexes/{}/documents/{}",
426            self.client.host, self.uid, document_id
427        );
428        self.client
429            .http_client
430            .request::<&DocumentQuery<Http>, (), T>(
431                &url,
432                Method::Get {
433                    query: document_query,
434                },
435                200,
436            )
437            .await
438    }
439
440    /// Get documents by batch.
441    ///
442    /// # Example
443    ///
444    /// ```
445    /// # use serde::{Serialize, Deserialize};
446    /// # use meilisearch_sdk::{client::*, indexes::*};
447    /// #
448    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
449    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
450    /// #
451    /// #[derive(Serialize, Deserialize, PartialEq, Debug)]
452    /// struct Movie {
453    ///     name: String,
454    ///     description: String,
455    /// }
456    ///
457    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
458    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
459    /// let movie_index = client.index("get_documents");
460    /// # movie_index.add_or_replace(&[Movie{name:String::from("Interstellar"), description:String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")}], Some("name")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
461    ///
462    /// // retrieve movies (you have to put some movies in the index before)
463    /// let movies = movie_index.get_documents::<Movie>().await.unwrap();
464    ///
465    /// assert!(movies.results.len() > 0);
466    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
467    /// # });
468    /// ```
469    pub async fn get_documents<T: DeserializeOwned + 'static + Send + Sync>(
470        &self,
471    ) -> Result<DocumentsResults<T>, Error> {
472        let url = format!("{}/indexes/{}/documents", self.client.host, self.uid);
473        self.client
474            .http_client
475            .request::<(), (), DocumentsResults<T>>(&url, Method::Get { query: () }, 200)
476            .await
477    }
478
479    /// Get documents by batch with parameters.
480    ///
481    /// # Example
482    ///
483    /// ```
484    /// # use serde::{Serialize, Deserialize};
485    /// # use meilisearch_sdk::{client::*, indexes::*, documents::*};
486    /// #
487    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
488    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
489    /// #
490    /// #[derive(Serialize, Deserialize, PartialEq, Debug)]
491    /// struct Movie {
492    ///     name: String,
493    ///     description: String,
494    /// }
495    ///
496    /// #[derive(Deserialize, Debug, PartialEq)]
497    /// struct ReturnedMovie {
498    ///     name: String,
499    /// }
500    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
501    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
502    ///
503    /// let movie_index = client.index("get_documents_with");
504    /// # movie_index.add_or_replace(&[Movie{name:String::from("Interstellar"), description:String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")}], Some("name")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
505    ///
506    /// let mut query = DocumentsQuery::new(&movie_index);
507    /// query.with_limit(1);
508    /// query.with_fields(["name"]);
509    /// // retrieve movies (you have to put some movies in the index before)
510    /// let movies = movie_index.get_documents_with::<ReturnedMovie>(&query).await.unwrap();
511    ///
512    /// assert_eq!(movies.results.len(), 1);
513    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
514    /// # });
515    /// ```
516    pub async fn get_documents_with<T: DeserializeOwned + 'static + Send + Sync>(
517        &self,
518        documents_query: &DocumentsQuery<'_, Http>,
519    ) -> Result<DocumentsResults<T>, Error> {
520        if documents_query.filter.is_some() || documents_query.ids.is_some() {
521            let url = format!("{}/indexes/{}/documents/fetch", self.client.host, self.uid);
522            return self
523                .client
524                .http_client
525                .request::<(), &DocumentsQuery<Http>, DocumentsResults<T>>(
526                    &url,
527                    Method::Post {
528                        body: documents_query,
529                        query: (),
530                    },
531                    200,
532                )
533                .await
534                .map_err(|err| match err {
535                    Error::MeilisearchCommunication(error) => {
536                        Error::MeilisearchCommunication(MeilisearchCommunicationError {
537                            status_code: error.status_code,
538                            url: error.url,
539                            message: Some(format!("{MEILISEARCH_VERSION_HINT}.")),
540                        })
541                    }
542                    Error::Meilisearch(error) => Error::Meilisearch(MeilisearchError {
543                        error_code: error.error_code,
544                        error_link: error.error_link,
545                        error_type: error.error_type,
546                        error_message: format!(
547                            "{}\n{}.",
548                            error.error_message, MEILISEARCH_VERSION_HINT
549                        ),
550                    }),
551                    _ => err,
552                });
553        }
554
555        let url = format!("{}/indexes/{}/documents", self.client.host, self.uid);
556        self.client
557            .http_client
558            .request::<&DocumentsQuery<Http>, (), DocumentsResults<T>>(
559                &url,
560                Method::Get {
561                    query: documents_query,
562                },
563                200,
564            )
565            .await
566    }
567
568    /// Add a list of documents or replace them if they already exist.
569    ///
570    /// If you send an already existing document (same id) the **whole existing document** will be overwritten by the new document.
571    /// Fields previously in the document not present in the new document are removed.
572    ///
573    /// For a partial update of the document see [`Index::add_or_update`].
574    ///
575    /// You can use the alias [`Index::add_documents`] if you prefer.
576    ///
577    /// # Example
578    ///
579    /// ```
580    /// # use serde::{Serialize, Deserialize};
581    /// # use meilisearch_sdk::{client::*, indexes::*};
582    /// # use std::thread::sleep;
583    /// # use std::time::Duration;
584    /// #
585    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
586    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
587    /// #
588    /// #[derive(Serialize, Deserialize, Debug)]
589    /// struct Movie {
590    ///     name: String,
591    ///     description: String,
592    /// }
593    ///
594    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
595    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
596    /// let movie_index = client.index("add_or_replace");
597    ///
598    /// let task = movie_index.add_or_replace(&[
599    ///     Movie{
600    ///         name: String::from("Interstellar"),
601    ///         description: String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")
602    ///     },
603    ///     Movie{
604    ///         // note that the id field can only take alphanumerics characters (and '-' and '/')
605    ///         name: String::from("MrsDoubtfire"),
606    ///         description: String::from("Loving but irresponsible dad Daniel Hillard, estranged from his exasperated spouse, is crushed by a court order allowing only weekly visits with his kids. When Daniel learns his ex needs a housekeeper, he gets the job -- disguised as an English nanny. Soon he becomes not only his children's best pal but the kind of parent he should have been from the start.")
607    ///     },
608    ///     Movie{
609    ///         name: String::from("Apollo13"),
610    ///         description: String::from("The true story of technical troubles that scuttle the Apollo 13 lunar mission in 1971, risking the lives of astronaut Jim Lovell and his crew, with the failed journey turning into a thrilling saga of heroism. Drifting more than 200,000 miles from Earth, the astronauts work furiously with the ground crew to avert tragedy.")
611    ///     },
612    /// ], Some("name")).await.unwrap();
613    /// // Meilisearch may take some time to execute the request so we are going to wait till it's completed
614    /// client.wait_for_task(task, None, None).await.unwrap();
615    ///
616    /// let movies = movie_index.get_documents::<Movie>().await.unwrap();
617    /// assert!(movies.results.len() >= 3);
618    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
619    /// # });
620    /// ```
621    pub async fn add_or_replace<T: Serialize + Send + Sync>(
622        &self,
623        documents: &[T],
624        primary_key: Option<&str>,
625    ) -> Result<TaskInfo, Error> {
626        let url = if let Some(primary_key) = primary_key {
627            format!(
628                "{}/indexes/{}/documents?primaryKey={}",
629                self.client.host, self.uid, primary_key
630            )
631        } else {
632            format!("{}/indexes/{}/documents", self.client.host, self.uid)
633        };
634        self.client
635            .http_client
636            .request::<(), &[T], TaskInfo>(
637                &url,
638                Method::Post {
639                    query: (),
640                    body: documents,
641                },
642                202,
643            )
644            .await
645    }
646
647    /// Add a raw and unchecked payload to meilisearch.
648    ///
649    /// This can be useful if your application is only forwarding data from other sources.
650    ///
651    /// If you send an already existing document (same id) the **whole existing document** will be overwritten by the new document.
652    /// Fields previously in the document not present in the new document are removed.
653    ///
654    /// For a partial update of the document see [`Index::add_or_update_unchecked_payload`].
655    ///
656    /// # Example
657    ///
658    /// ```
659    /// # use serde::{Serialize, Deserialize};
660    /// # use meilisearch_sdk::{client::*, indexes::*};
661    /// # use std::thread::sleep;
662    /// # use std::time::Duration;
663    /// #
664    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
665    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
666    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
667    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
668    /// let movie_index = client.index("add_or_replace_unchecked_payload");
669    ///
670    /// let task = movie_index.add_or_replace_unchecked_payload(
671    ///     r#"{ "id": 1, "body": "doggo" }
672    ///     { "id": 2, "body": "catto" }"#.as_bytes(),
673    ///     "application/x-ndjson",
674    ///     Some("id"),
675    ///   ).await.unwrap();
676    /// // Meilisearch may take some time to execute the request so we are going to wait till it's completed
677    /// client.wait_for_task(task, None, None).await.unwrap();
678    ///
679    /// let movies = movie_index.get_documents::<serde_json::Value>().await.unwrap();
680    /// assert_eq!(movies.results.len(), 2);
681    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
682    /// # });
683    /// ```
684    pub async fn add_or_replace_unchecked_payload<
685        T: futures_io::AsyncRead + Send + Sync + 'static,
686    >(
687        &self,
688        payload: T,
689        content_type: &str,
690        primary_key: Option<&str>,
691    ) -> Result<TaskInfo, Error> {
692        let url = if let Some(primary_key) = primary_key {
693            format!(
694                "{}/indexes/{}/documents?primaryKey={}",
695                self.client.host, self.uid, primary_key
696            )
697        } else {
698            format!("{}/indexes/{}/documents", self.client.host, self.uid)
699        };
700        self.client
701            .http_client
702            .stream_request::<(), T, TaskInfo>(
703                &url,
704                Method::Post {
705                    query: (),
706                    body: payload,
707                },
708                content_type,
709                202,
710            )
711            .await
712    }
713
714    /// Alias for [`Index::add_or_replace`].
715    pub async fn add_documents<T: Serialize + Send + Sync>(
716        &self,
717        documents: &[T],
718        primary_key: Option<&str>,
719    ) -> Result<TaskInfo, Error> {
720        self.add_or_replace(documents, primary_key).await
721    }
722
723    /// Add a raw ndjson payload and update them if they already exist.
724    ///
725    /// It configures the correct content type for ndjson data.
726    ///
727    /// If you send an already existing document (same id) the old document will be only partially updated according to the fields of the new document.
728    /// Thus, any fields not present in the new document are kept and remained unchanged.
729    ///
730    /// To completely overwrite a document, check out the [`Index::add_documents_ndjson`] documents method.
731    ///
732    /// # Example
733    ///
734    /// ```
735    /// # use serde::{Serialize, Deserialize};
736    /// # use meilisearch_sdk::{client::*, indexes::*};
737    /// # use std::thread::sleep;
738    /// # use std::time::Duration;
739    /// #
740    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
741    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
742    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
743    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
744    /// let movie_index = client.index("update_documents_ndjson");
745    ///
746    /// let task = movie_index.update_documents_ndjson(
747    ///     r#"{ "id": 1, "body": "doggo" }
748    ///     { "id": 2, "body": "catto" }"#.as_bytes(),
749    ///     Some("id"),
750    ///   ).await.unwrap();
751    /// // Meilisearch may take some time to execute the request so we are going to wait till it's completed
752    /// client.wait_for_task(task, None, None).await.unwrap();
753    ///
754    /// let movies = movie_index.get_documents::<serde_json::Value>().await.unwrap();
755    /// assert_eq!(movies.results.len(), 2);
756    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
757    /// # });
758    /// ```
759    #[cfg(not(target_arch = "wasm32"))]
760    pub async fn update_documents_ndjson<T: futures_io::AsyncRead + Send + Sync + 'static>(
761        &self,
762        payload: T,
763        primary_key: Option<&str>,
764    ) -> Result<TaskInfo, Error> {
765        self.add_or_update_unchecked_payload(payload, "application/x-ndjson", primary_key)
766            .await
767    }
768
769    /// Add a raw ndjson payload to meilisearch.
770    ///
771    /// It configures the correct content type for ndjson data.
772    ///
773    /// If you send an already existing document (same id) the **whole existing document** will be overwritten by the new document.
774    /// Fields previously in the document not present in the new document are removed.
775    ///
776    /// For a partial update of the document see [`Index::update_documents_ndjson`].
777    ///
778    /// # Example
779    ///
780    /// ```
781    /// # use serde::{Serialize, Deserialize};
782    /// # use meilisearch_sdk::{client::*, indexes::*};
783    /// # use std::thread::sleep;
784    /// # use std::time::Duration;
785    /// #
786    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
787    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
788    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
789    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
790    /// let movie_index = client.index("add_documents_ndjson");
791    ///
792    /// let task = movie_index.add_documents_ndjson(
793    ///     r#"{ "id": 1, "body": "doggo" }
794    ///     { "id": 2, "body": "catto" }"#.as_bytes(),
795    ///     Some("id"),
796    ///   ).await.unwrap();
797    /// // Meilisearch may take some time to execute the request so we are going to wait till it's completed
798    /// client.wait_for_task(task, None, None).await.unwrap();
799    ///
800    /// let movies = movie_index.get_documents::<serde_json::Value>().await.unwrap();
801    /// assert_eq!(movies.results.len(), 2);
802    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
803    /// # });
804    /// ```
805    #[cfg(not(target_arch = "wasm32"))]
806    pub async fn add_documents_ndjson<T: futures_io::AsyncRead + Send + Sync + 'static>(
807        &self,
808        payload: T,
809        primary_key: Option<&str>,
810    ) -> Result<TaskInfo, Error> {
811        self.add_or_replace_unchecked_payload(payload, "application/x-ndjson", primary_key)
812            .await
813    }
814
815    /// Add a raw csv payload and update them if they already exist.
816    ///
817    /// It configures the correct content type for csv data.
818    ///
819    /// If you send an already existing document (same id) the old document will be only partially updated according to the fields of the new document.
820    /// Thus, any fields not present in the new document are kept and remained unchanged.
821    ///
822    /// To completely overwrite a document, check out the [`Index::add_documents_csv`] documents method.
823    ///
824    /// # Example
825    ///
826    /// ```
827    /// # use serde::{Serialize, Deserialize};
828    /// # use meilisearch_sdk::{client::*, indexes::*};
829    /// # use std::thread::sleep;
830    /// # use std::time::Duration;
831    /// #
832    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
833    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
834    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
835    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
836    /// let movie_index = client.index("update_documents_csv");
837    ///
838    /// let task = movie_index.update_documents_csv(
839    ///     "id,body\n1,\"doggo\"\n2,\"catto\"".as_bytes(),
840    ///     Some("id"),
841    ///   ).await.unwrap();
842    /// // Meilisearch may take some time to execute the request so we are going to wait till it's completed
843    /// client.wait_for_task(task, None, None).await.unwrap();
844    ///
845    /// let movies = movie_index.get_documents::<serde_json::Value>().await.unwrap();
846    /// assert_eq!(movies.results.len(), 2);
847    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
848    /// # });
849    /// ```
850    #[cfg(not(target_arch = "wasm32"))]
851    pub async fn update_documents_csv<T: futures_io::AsyncRead + Send + Sync + 'static>(
852        &self,
853        payload: T,
854        primary_key: Option<&str>,
855    ) -> Result<TaskInfo, Error> {
856        self.add_or_update_unchecked_payload(payload, "text/csv", primary_key)
857            .await
858    }
859
860    /// Add a raw csv payload to meilisearch.
861    ///
862    /// It configures the correct content type for csv data.
863    ///
864    /// If you send an already existing document (same id) the **whole existing document** will be overwritten by the new document.
865    /// Fields previously in the document not present in the new document are removed.
866    ///
867    /// For a partial update of the document see [`Index::update_documents_csv`].
868    ///
869    /// # Example
870    ///
871    /// ```
872    /// # use serde::{Serialize, Deserialize};
873    /// # use meilisearch_sdk::{client::*, indexes::*};
874    /// # use std::thread::sleep;
875    /// # use std::time::Duration;
876    /// #
877    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
878    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
879    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
880    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
881    /// let movie_index = client.index("add_documents_csv");
882    ///
883    /// let task = movie_index.add_documents_csv(
884    ///     "id,body\n1,\"doggo\"\n2,\"catto\"".as_bytes(),
885    ///     Some("id"),
886    ///   ).await.unwrap();
887    /// // Meilisearch may take some time to execute the request so we are going to wait till it's completed
888    /// client.wait_for_task(task, None, None).await.unwrap();
889    ///
890    /// let movies = movie_index.get_documents::<serde_json::Value>().await.unwrap();
891    /// assert_eq!(movies.results.len(), 2);
892    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
893    /// # });
894    /// ```
895    #[cfg(not(target_arch = "wasm32"))]
896    pub async fn add_documents_csv<T: futures_io::AsyncRead + Send + Sync + 'static>(
897        &self,
898        payload: T,
899        primary_key: Option<&str>,
900    ) -> Result<TaskInfo, Error> {
901        self.add_or_replace_unchecked_payload(payload, "text/csv", primary_key)
902            .await
903    }
904
905    /// Add a list of documents and update them if they already exist.
906    ///
907    /// If you send an already existing document (same id) the old document will be only partially updated according to the fields of the new document.
908    /// Thus, any fields not present in the new document are kept and remained unchanged.
909    ///
910    /// To completely overwrite a document, check out the [`Index::add_or_replace`] documents method.
911    ///
912    /// # Example
913    ///
914    /// ```
915    /// # use serde::{Serialize, Deserialize};
916    /// # use meilisearch_sdk::client::*;
917    /// # use std::thread::sleep;
918    /// # use std::time::Duration;
919    /// #
920    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
921    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
922    /// #
923    /// #[derive(Serialize, Deserialize, Debug)]
924    /// struct Movie {
925    ///     name: String,
926    ///     description: String,
927    /// }
928    ///
929    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
930    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
931    /// let movie_index = client.index("add_or_update");
932    ///
933    /// let task = movie_index.add_or_update(&[
934    ///     Movie {
935    ///         name: String::from("Interstellar"),
936    ///         description: String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")
937    ///     },
938    ///     Movie {
939    ///         // note that the id field can only take alphanumerics characters (and '-' and '/')
940    ///         name: String::from("MrsDoubtfire"),
941    ///         description: String::from("Loving but irresponsible dad Daniel Hillard, estranged from his exasperated spouse, is crushed by a court order allowing only weekly visits with his kids. When Daniel learns his ex needs a housekeeper, he gets the job -- disguised as an English nanny. Soon he becomes not only his children's best pal but the kind of parent he should have been from the start.")
942    ///     },
943    ///     Movie {
944    ///         name: String::from("Apollo13"),
945    ///         description: String::from("The true story of technical troubles that scuttle the Apollo 13 lunar mission in 1971, risking the lives of astronaut Jim Lovell and his crew, with the failed journey turning into a thrilling saga of heroism. Drifting more than 200,000 miles from Earth, the astronauts work furiously with the ground crew to avert tragedy.")
946    ///     },
947    /// ], Some("name")).await.unwrap();
948    ///
949    /// // Meilisearch may take some time to execute the request so we are going to wait till it's completed
950    /// client.wait_for_task(task, None, None).await.unwrap();
951    ///
952    /// let movies = movie_index.get_documents::<Movie>().await.unwrap();
953    /// assert!(movies.results.len() >= 3);
954    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
955    /// # });
956    /// ```
957    pub async fn add_or_update<T: Serialize + Send + Sync>(
958        &self,
959        documents: &[T],
960        primary_key: Option<&str>,
961    ) -> Result<TaskInfo, Error> {
962        let url = if let Some(primary_key) = primary_key {
963            format!(
964                "{}/indexes/{}/documents?primaryKey={}",
965                self.client.host, self.uid, primary_key
966            )
967        } else {
968            format!("{}/indexes/{}/documents", self.client.host, self.uid)
969        };
970        self.client
971            .http_client
972            .request::<(), &[T], TaskInfo>(
973                &url,
974                Method::Put {
975                    query: (),
976                    body: documents,
977                },
978                202,
979            )
980            .await
981    }
982
983    /// Add a raw and unchecked payload to meilisearch.
984    ///
985    /// This can be useful if your application is only forwarding data from other sources.
986    ///
987    /// If you send an already existing document (same id) the old document will be only partially updated according to the fields of the new document.
988    /// Thus, any fields not present in the new document are kept and remained unchanged.
989    ///
990    /// To completely overwrite a document, check out the [`Index::add_or_replace_unchecked_payload`] documents method.
991    ///
992    /// # Example
993    ///
994    /// ```
995    /// # use serde::{Serialize, Deserialize};
996    /// # use meilisearch_sdk::{client::*, indexes::*};
997    /// # use std::thread::sleep;
998    /// # use std::time::Duration;
999    /// #
1000    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1001    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1002    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1003    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1004    /// let movie_index = client.index("add_or_replace_unchecked_payload");
1005    ///
1006    /// let task = movie_index.add_or_update_unchecked_payload(
1007    ///     r#"{ "id": 1, "body": "doggo" }
1008    ///     { "id": 2, "body": "catto" }"#.as_bytes(),
1009    ///     "application/x-ndjson",
1010    ///     Some("id"),
1011    /// ).await.unwrap();
1012    /// // Meilisearch may take some time to execute the request so we are going to wait till it's completed
1013    /// client.wait_for_task(task, None, None).await.unwrap();
1014    ///
1015    /// let movies = movie_index.get_documents::<serde_json::Value>().await.unwrap();
1016    ///
1017    /// assert_eq!(movies.results.len(), 2);
1018    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1019    /// # });
1020    /// ```
1021    #[cfg(not(target_arch = "wasm32"))]
1022    pub async fn add_or_update_unchecked_payload<
1023        T: futures_io::AsyncRead + Send + Sync + 'static,
1024    >(
1025        &self,
1026        payload: T,
1027        content_type: &str,
1028        primary_key: Option<&str>,
1029    ) -> Result<TaskInfo, Error> {
1030        let url = if let Some(primary_key) = primary_key {
1031            format!(
1032                "{}/indexes/{}/documents?primaryKey={}",
1033                self.client.host, self.uid, primary_key
1034            )
1035        } else {
1036            format!("{}/indexes/{}/documents", self.client.host, self.uid)
1037        };
1038        self.client
1039            .http_client
1040            .stream_request::<(), T, TaskInfo>(
1041                &url,
1042                Method::Put {
1043                    query: (),
1044                    body: payload,
1045                },
1046                content_type,
1047                202,
1048            )
1049            .await
1050    }
1051
1052    /// Delete all documents in the [Index].
1053    ///
1054    /// # Example
1055    ///
1056    /// ```
1057    /// # use serde::{Serialize, Deserialize};
1058    /// # use meilisearch_sdk::{client::*, indexes::*};
1059    /// #
1060    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1061    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1062    /// #
1063    /// # #[derive(Serialize, Deserialize, Debug)]
1064    /// # struct Movie {
1065    /// #    name: String,
1066    /// #    description: String,
1067    /// # }
1068    /// #
1069    /// #
1070    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1071    /// #
1072    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1073    /// let movie_index = client.index("delete_all_documents");
1074    /// #
1075    /// # movie_index.add_or_replace(&[Movie{name:String::from("Interstellar"), description:String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")}], Some("name")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1076    /// #
1077    /// movie_index.delete_all_documents()
1078    ///     .await
1079    ///     .unwrap()
1080    ///     .wait_for_completion(&client, None, None)
1081    ///     .await
1082    ///     .unwrap();
1083    /// let movies = movie_index.get_documents::<Movie>().await.unwrap();
1084    /// assert_eq!(movies.results.len(), 0);
1085    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1086    /// # });
1087    /// ```
1088    pub async fn delete_all_documents(&self) -> Result<TaskInfo, Error> {
1089        self.client
1090            .http_client
1091            .request::<(), (), TaskInfo>(
1092                &format!("{}/indexes/{}/documents", self.client.host, self.uid),
1093                Method::Delete { query: () },
1094                202,
1095            )
1096            .await
1097    }
1098
1099    /// Delete one document based on its unique id.
1100    ///
1101    /// # Example
1102    ///
1103    /// ```
1104    /// # use serde::{Serialize, Deserialize};
1105    /// # use meilisearch_sdk::client::*;
1106    /// #
1107    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1108    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1109    /// #
1110    /// # #[derive(Serialize, Deserialize, Debug)]
1111    /// # struct Movie {
1112    /// #    name: String,
1113    /// #    description: String,
1114    /// # }
1115    /// #
1116    /// #
1117    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1118    /// #
1119    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1120    /// let mut movies = client.index("delete_document");
1121    /// # movies.add_or_replace(&[Movie{name:String::from("Interstellar"), description:String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")}], Some("name")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1122    /// // add a document with id = Interstellar
1123    /// movies.delete_document("Interstellar")
1124    ///     .await
1125    ///     .unwrap()
1126    ///     .wait_for_completion(&client, None, None)
1127    ///     .await
1128    ///     .unwrap();
1129    /// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1130    /// # });
1131    /// ```
1132    pub async fn delete_document<T: Display>(&self, uid: T) -> Result<TaskInfo, Error> {
1133        self.client
1134            .http_client
1135            .request::<(), (), TaskInfo>(
1136                &format!(
1137                    "{}/indexes/{}/documents/{}",
1138                    self.client.host, self.uid, uid
1139                ),
1140                Method::Delete { query: () },
1141                202,
1142            )
1143            .await
1144    }
1145
1146    /// Delete a selection of documents based on array of document id's.
1147    ///
1148    /// # Example
1149    ///
1150    /// ```
1151    /// # use serde::{Serialize, Deserialize};
1152    /// # use meilisearch_sdk::client::*;
1153    /// #
1154    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1155    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1156    /// #
1157    /// # #[derive(Serialize, Deserialize, Debug)]
1158    /// # struct Movie {
1159    /// #    name: String,
1160    /// #    description: String,
1161    /// # }
1162    /// #
1163    /// #
1164    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1165    /// #
1166    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1167    /// let movies = client.index("delete_documents");
1168    /// #
1169    /// # // add some documents
1170    /// # movies.add_or_replace(&[Movie{name:String::from("Interstellar"), description:String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")},Movie{name:String::from("Unknown"), description:String::from("Unknown")}], Some("name")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1171    /// #
1172    /// // delete some documents
1173    /// movies.delete_documents(&["Interstellar", "Unknown"])
1174    ///     .await
1175    ///     .unwrap()
1176    ///     .wait_for_completion(&client, None, None)
1177    ///     .await
1178    ///     .unwrap();
1179    /// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1180    /// # });
1181    /// ```
1182    pub async fn delete_documents<T: Display + Serialize + std::fmt::Debug + Send + Sync>(
1183        &self,
1184        uids: &[T],
1185    ) -> Result<TaskInfo, Error> {
1186        self.client
1187            .http_client
1188            .request::<(), &[T], TaskInfo>(
1189                &format!(
1190                    "{}/indexes/{}/documents/delete-batch",
1191                    self.client.host, self.uid
1192                ),
1193                Method::Post {
1194                    query: (),
1195                    body: uids,
1196                },
1197                202,
1198            )
1199            .await
1200    }
1201
1202    /// Delete a selection of documents with filters.
1203    ///
1204    /// # Example
1205    ///
1206    /// ```
1207    /// # use serde::{Serialize, Deserialize};
1208    /// # use meilisearch_sdk::{client::*, documents::*};
1209    /// #
1210    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1211    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1212    /// #
1213    /// # #[derive(Serialize, Deserialize, Debug)]
1214    /// # struct Movie {
1215    /// #    name: String,
1216    /// #    id: String,
1217    /// # }
1218    /// #
1219    /// #
1220    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1221    /// #
1222    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1223    /// let index = client.index("delete_documents_with");
1224    /// #
1225    /// # index.set_filterable_attributes(["id"]);
1226    /// # // add some documents
1227    /// # index.add_or_replace(&[Movie{id:String::from("1"), name: String::from("First movie") }, Movie{id:String::from("1"), name: String::from("First movie") }], Some("id")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1228    ///
1229    /// let mut query = DocumentDeletionQuery::new(&index);
1230    /// query.with_filter("id = 1");
1231    /// // delete some documents
1232    /// index.delete_documents_with(&query)
1233    ///     .await
1234    ///     .unwrap()
1235    ///     .wait_for_completion(&client, None, None)
1236    ///     .await
1237    ///     .unwrap();
1238    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1239    /// # });
1240    /// ```
1241    pub async fn delete_documents_with(
1242        &self,
1243        query: &DocumentDeletionQuery<'_, Http>,
1244    ) -> Result<TaskInfo, Error> {
1245        self.client
1246            .http_client
1247            .request::<(), &DocumentDeletionQuery<Http>, TaskInfo>(
1248                &format!("{}/indexes/{}/documents/delete", self.client.host, self.uid),
1249                Method::Post {
1250                    query: (),
1251                    body: query,
1252                },
1253                202,
1254            )
1255            .await
1256    }
1257
1258    /// Alias for the [`Index::update`] method.
1259    pub async fn set_primary_key(
1260        &mut self,
1261        primary_key: impl AsRef<str>,
1262    ) -> Result<TaskInfo, Error> {
1263        self.primary_key = Some(primary_key.as_ref().to_string());
1264
1265        self.update().await
1266    }
1267
1268    /// Fetch the information of the index as a raw JSON [Index], this index should already exist.
1269    ///
1270    /// If you use it directly from the [Client], you can use the method [`Client::get_raw_index`], which is the equivalent method from the client.
1271    ///
1272    /// # Example
1273    ///
1274    /// ```
1275    /// # use meilisearch_sdk::{client::*, indexes::*};
1276    /// #
1277    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1278    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1279    /// #
1280    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1281    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1282    /// # let index = client.create_index("fetch_info", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
1283    /// let mut idx = client.index("fetch_info");
1284    /// idx.fetch_info().await.unwrap();
1285    ///
1286    /// println!("{idx:?}");
1287    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1288    /// # });
1289    /// ```
1290    pub async fn fetch_info(&mut self) -> Result<(), Error> {
1291        let v = self.client.get_raw_index(&self.uid).await?;
1292        *self = Index::from_value(v, self.client.clone())?;
1293        Ok(())
1294    }
1295
1296    /// Fetch the primary key of the index.
1297    ///
1298    /// # Example
1299    ///
1300    /// ```
1301    /// # use meilisearch_sdk::{client::*, indexes::*};
1302    /// #
1303    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1304    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1305    /// #
1306    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1307    /// # // create the client
1308    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1309    /// let mut index = client.create_index("get_primary_key", Some("id"))
1310    ///     .await
1311    ///     .unwrap()
1312    ///     .wait_for_completion(&client, None, None)
1313    ///     .await.unwrap()
1314    ///     .try_make_index(&client)
1315    ///     .unwrap();
1316    ///
1317    /// let primary_key = index.get_primary_key().await.unwrap();
1318    ///
1319    /// assert_eq!(primary_key, Some("id"));
1320    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1321    /// # });
1322    /// ```
1323    pub async fn get_primary_key(&mut self) -> Result<Option<&str>, Error> {
1324        self.fetch_info().await?;
1325        Ok(self.primary_key.as_deref())
1326    }
1327
1328    /// Compact this index to reduce disk usage.
1329    ///
1330    /// Triggers a compaction task for the current index. Once completed, the
1331    /// index data is compacted on disk.
1332    ///
1333    /// # Example
1334    ///
1335    /// ```
1336    /// # use meilisearch_sdk::{client::*, indexes::*, tasks::Task};
1337    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1338    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1339    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1340    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1341    /// # let index = client
1342    /// #   .create_index("compact_example", None)
1343    /// #   .await
1344    /// #   .unwrap()
1345    /// #   .wait_for_completion(&client, None, None)
1346    /// #   .await
1347    /// #   .unwrap()
1348    /// #   .try_make_index(&client)
1349    /// #   .unwrap();
1350    ///
1351    /// let task = index
1352    ///     .compact()
1353    ///     .await
1354    ///     .unwrap()
1355    ///     .wait_for_completion(&client, None, None)
1356    ///     .await
1357    ///     .unwrap();
1358    ///
1359    /// assert!(matches!(task, Task::Succeeded { .. }));
1360    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1361    /// # });
1362    /// ```
1363    pub async fn compact(&self) -> Result<TaskInfo, Error> {
1364        self.client
1365            .http_client
1366            .request::<(), (), TaskInfo>(
1367                &format!("{}/indexes/{}/compact", self.client.host, self.uid),
1368                Method::Post {
1369                    query: (),
1370                    body: (),
1371                },
1372                202,
1373            )
1374            .await
1375    }
1376
1377    /// Get a [Task] from a specific [Index] to keep track of [asynchronous operations](https://www.meilisearch.com/docs/learn/advanced/asynchronous_operations).
1378    ///
1379    /// # Example
1380    ///
1381    /// ```
1382    /// # use serde::{Serialize, Deserialize};
1383    /// # use std::thread::sleep;
1384    /// # use std::time::Duration;
1385    /// # use meilisearch_sdk::{client::*, indexes::*, tasks::Task};
1386    /// #
1387    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1388    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1389    /// #
1390    /// # #[derive(Debug, Serialize, Deserialize, PartialEq)]
1391    /// # struct Document {
1392    /// #    id: usize,
1393    /// #    value: String,
1394    /// #    kind: String,
1395    /// # }
1396    /// #
1397    /// #
1398    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1399    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1400    /// let movies = client.index("get_task");
1401    ///
1402    /// let task = movies.add_documents(&[
1403    ///     Document { id: 0, kind: "title".into(), value: "The Social Network".to_string() }
1404    /// ], None).await.unwrap();
1405    /// # task.clone().wait_for_completion(&client, None, None).await.unwrap();
1406    ///
1407    /// // Get task status from the index, using `uid`
1408    /// let status = movies.get_task(&task).await.unwrap();
1409    ///
1410    /// let from_index = match status {
1411    ///     Task::Enqueued { content } => content.uid,
1412    ///     Task::Processing { content } => content.uid,
1413    ///     Task::Failed { content } => content.task.uid,
1414    ///     Task::Succeeded { content } => content.uid,
1415    /// };
1416    ///
1417    /// assert_eq!(task.get_task_uid(), from_index);
1418    /// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1419    /// # });
1420    /// ```
1421    pub async fn get_task(&self, uid: impl AsRef<u32>) -> Result<Task, Error> {
1422        self.client
1423            .http_client
1424            .request::<(), (), Task>(
1425                &format!("{}/tasks/{}", self.client.host, uid.as_ref()),
1426                Method::Get { query: () },
1427                200,
1428            )
1429            .await
1430    }
1431
1432    /// Get the status of all tasks in a given index.
1433    ///
1434    /// # Example
1435    ///
1436    /// ```
1437    /// # use serde::{Serialize, Deserialize};
1438    /// # use meilisearch_sdk::{client::*, indexes::*};
1439    /// #
1440    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1441    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1442    /// #
1443    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1444    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1445    /// # let index = client.create_index("get_tasks", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
1446    /// let tasks = index.get_tasks().await.unwrap();
1447    ///
1448    /// assert!(tasks.results.len() > 0);
1449    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1450    /// # });
1451    /// ```
1452    pub async fn get_tasks(&self) -> Result<TasksResults, Error> {
1453        let mut query = TasksSearchQuery::new(&self.client);
1454        query.with_index_uids([self.uid.as_str()]);
1455
1456        self.client.get_tasks_with(&query).await
1457    }
1458
1459    /// Get the status of all tasks in a given index.
1460    ///
1461    /// # Example
1462    ///
1463    /// ```
1464    /// # use serde::{Serialize, Deserialize};
1465    /// # use meilisearch_sdk::{client::*, indexes::*, tasks::*};
1466    /// #
1467    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1468    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1469    /// #
1470    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1471    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1472    /// # let index = client.create_index("get_tasks_with", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
1473    /// let mut query = TasksSearchQuery::new(&client);
1474    /// query.with_index_uids(["none_existant"]);
1475    ///
1476    /// let tasks = index.get_tasks_with(&query).await.unwrap();
1477    ///
1478    /// assert!(tasks.results.len() > 0);
1479    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1480    /// # });
1481    /// ```
1482    pub async fn get_tasks_with(
1483        &self,
1484        tasks_query: &TasksQuery<'_, TasksPaginationFilters, Http>,
1485    ) -> Result<TasksResults, Error> {
1486        let mut query = tasks_query.clone();
1487        query.with_index_uids([self.uid.as_str()]);
1488
1489        self.client.get_tasks_with(&query).await
1490    }
1491
1492    /// Get stats of an index.
1493    ///
1494    /// # Example
1495    ///
1496    /// ```
1497    /// # use meilisearch_sdk::{client::*, indexes::*};
1498    /// #
1499    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1500    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1501    /// #
1502    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1503    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1504    /// # let index = client.create_index("get_stats", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
1505    /// let stats = index.get_stats().await.unwrap();
1506    ///
1507    /// assert_eq!(stats.is_indexing, false);
1508    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1509    /// # });
1510    /// ```
1511    pub async fn get_stats(&self) -> Result<IndexStats, Error> {
1512        self.client
1513            .http_client
1514            .request::<(), (), IndexStats>(
1515                &format!("{}/indexes/{}/stats", self.client.host, self.uid),
1516                Method::Get { query: () },
1517                200,
1518            )
1519            .await
1520    }
1521
1522    /// Wait until Meilisearch processes a [Task], and get its status.
1523    ///
1524    /// `interval` = The frequency at which the server should be polled. **Default = 50ms**
1525    ///
1526    /// `timeout` = The maximum time to wait for processing to complete. **Default = 5000ms**
1527    ///
1528    /// If the waited time exceeds `timeout` then an [`Error::Timeout`] will be returned.
1529    ///
1530    /// See also [`Client::wait_for_task`, `Task::wait_for_completion`].
1531    ///
1532    /// # Example
1533    ///
1534    /// ```
1535    /// # use meilisearch_sdk::{client::*, indexes::*, tasks::Task};
1536    /// # use serde::{Serialize, Deserialize};
1537    /// #
1538    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1539    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1540    /// #
1541    /// # #[derive(Debug, Serialize, Deserialize, PartialEq)]
1542    /// # struct Document {
1543    /// #    id: usize,
1544    /// #    value: String,
1545    /// #    kind: String,
1546    /// # }
1547    /// #
1548    /// #
1549    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1550    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1551    /// let movies = client.index("movies_index_wait_for_task");
1552    ///
1553    /// let task = movies.add_documents(&[
1554    ///     Document { id: 0, kind: "title".into(), value: "The Social Network".to_string() },
1555    ///     Document { id: 1, kind: "title".into(), value: "Harry Potter and the Sorcerer's Stone".to_string() },
1556    /// ], None).await.unwrap();
1557    ///
1558    /// let status = movies.wait_for_task(task, None, None).await.unwrap();
1559    ///
1560    /// assert!(matches!(status, Task::Succeeded { .. }));
1561    /// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1562    /// # });
1563    /// ```
1564    pub async fn wait_for_task(
1565        &self,
1566        task_id: impl AsRef<u32>,
1567        interval: Option<Duration>,
1568        timeout: Option<Duration>,
1569    ) -> Result<Task, Error> {
1570        self.client.wait_for_task(task_id, interval, timeout).await
1571    }
1572
1573    /// Add documents to the index in batches.
1574    ///
1575    /// `documents` = A slice of documents
1576    ///
1577    /// `batch_size` = Optional parameter that allows you to specify the size of the batch
1578    ///
1579    /// **`batch_size` is 1000 by default**
1580    ///
1581    /// # Example
1582    ///
1583    /// ```
1584    /// # use serde::{Serialize, Deserialize};
1585    /// # use meilisearch_sdk::client::*;
1586    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1587    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1588    /// #
1589    /// #[derive(Serialize, Deserialize, Debug)]
1590    /// struct Movie {
1591    ///     name: String,
1592    ///     description: String,
1593    /// }
1594    ///
1595    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1596    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1597    /// let movie_index = client.index("add_documents_in_batches");
1598    ///
1599    /// let tasks = movie_index.add_documents_in_batches(&[
1600    ///  Movie {
1601    ///         name: String::from("Interstellar"),
1602    ///         description: String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")
1603    ///  },
1604    ///  Movie {
1605    ///         // note that the id field can only take alphanumerics characters (and '-' and '/')
1606    ///         name: String::from("MrsDoubtfire"),
1607    ///         description: String::from("Loving but irresponsible dad Daniel Hillard, estranged from his exasperated spouse, is crushed by a court order allowing only weekly visits with his kids. When Daniel learns his ex needs a housekeeper, he gets the job -- disguised as an English nanny. Soon he becomes not only his children's best pal but the kind of parent he should have been from the start.")
1608    ///  },
1609    ///  Movie {
1610    ///         name: String::from("Apollo13"),
1611    ///         description: String::from("The true story of technical troubles that scuttle the Apollo 13 lunar mission in 1971, risking the lives of astronaut Jim Lovell and his crew, with the failed journey turning into a thrilling saga of heroism. Drifting more than 200,000 miles from Earth, the astronauts work furiously with the ground crew to avert tragedy.")
1612    ///     }],
1613    ///     Some(1),
1614    ///     Some("name")
1615    /// ).await.unwrap();
1616    ///
1617    /// client.wait_for_task(tasks.last().unwrap(), None, None).await.unwrap();
1618    ///
1619    /// let movies = movie_index.get_documents::<Movie>().await.unwrap();
1620    ///
1621    /// assert!(movies.results.len() >= 3);
1622    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None,
1623    /// # None).await.unwrap();
1624    /// # });
1625    /// ```
1626    pub async fn add_documents_in_batches<T: Serialize + Send + Sync>(
1627        &self,
1628        documents: &[T],
1629        batch_size: Option<usize>,
1630        primary_key: Option<&str>,
1631    ) -> Result<Vec<TaskInfo>, Error> {
1632        let mut task = Vec::with_capacity(documents.len());
1633        for document_batch in documents.chunks(batch_size.unwrap_or(1000)) {
1634            task.push(self.add_documents(document_batch, primary_key).await?);
1635        }
1636        Ok(task)
1637    }
1638
1639    /// Update documents to the index in batches.
1640    ///
1641    /// `documents` = A slice of documents
1642    ///
1643    /// `batch_size` = Optional parameter that allows you to specify the size of the batch
1644    ///
1645    /// **`batch_size` is 1000 by default**
1646    ///
1647    /// # Example
1648    ///
1649    /// ```
1650    /// # use serde::{Serialize, Deserialize};
1651    /// # use meilisearch_sdk::client::*;
1652    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1653    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1654    /// #
1655    /// #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
1656    /// struct Movie {
1657    ///     name: String,
1658    ///     description: String,
1659    /// }
1660    ///
1661    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1662    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1663    /// let movie_index = client.index("update_documents_in_batches");
1664    ///
1665    /// let tasks = movie_index.add_documents_in_batches(&[
1666    ///  Movie {
1667    ///         name: String::from("Interstellar"),
1668    ///         description: String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")
1669    ///  },
1670    ///  Movie {
1671    ///         // note that the id field can only take alphanumerics characters (and '-' and '/')
1672    ///         name: String::from("MrsDoubtfire"),
1673    ///         description: String::from("Loving but irresponsible dad Daniel Hillard, estranged from his exasperated spouse, is crushed by a court order allowing only weekly visits with his kids. When Daniel learns his ex needs a housekeeper, he gets the job -- disguised as an English nanny. Soon he becomes not only his children's best pal but the kind of parent he should have been from the start.")
1674    ///  },
1675    ///  Movie {
1676    ///         name: String::from("Apollo13"),
1677    ///         description: String::from("The true story of technical troubles that scuttle the Apollo 13 lunar mission in 1971, risking the lives of astronaut Jim Lovell and his crew, with the failed journey turning into a thrilling saga of heroism. Drifting more than 200,000 miles from Earth, the astronauts work furiously with the ground crew to avert tragedy.")
1678    ///     }],
1679    ///     Some(1),
1680    ///     Some("name")
1681    /// ).await.unwrap();
1682    ///
1683    /// client.wait_for_task(tasks.last().unwrap(), None, None).await.unwrap();
1684    ///
1685    /// let movies = movie_index.get_documents::<Movie>().await.unwrap();
1686    /// assert!(movies.results.len() >= 3);
1687    ///
1688    /// let updated_movies = [
1689    ///  Movie {
1690    ///         name: String::from("Interstellar"),
1691    ///         description: String::from("Updated!")
1692    ///  },
1693    ///  Movie {
1694    ///         // note that the id field can only take alphanumerics characters (and '-' and '/')
1695    ///         name: String::from("MrsDoubtfire"),
1696    ///         description: String::from("Updated!")
1697    ///  },
1698    ///  Movie {
1699    ///         name: String::from("Apollo13"),
1700    ///         description: String::from("Updated!")
1701    /// }];
1702    ///
1703    /// let tasks = movie_index.update_documents_in_batches(&updated_movies, Some(1), None).await.unwrap();
1704    ///
1705    /// client.wait_for_task(tasks.last().unwrap(), None, None).await.unwrap();
1706    ///
1707    /// let movies_updated = movie_index.get_documents::<Movie>().await.unwrap();
1708    ///
1709    /// assert!(movies_updated.results.len() >= 3);
1710    /// # movie_index.delete().await.unwrap().wait_for_completion(&client, None,
1711    /// # None).await.unwrap();
1712    /// # });
1713    /// ```
1714    pub async fn update_documents_in_batches<T: Serialize + Send + Sync>(
1715        &self,
1716        documents: &[T],
1717        batch_size: Option<usize>,
1718        primary_key: Option<&str>,
1719    ) -> Result<Vec<TaskInfo>, Error> {
1720        let mut task = Vec::with_capacity(documents.len());
1721        for document_batch in documents.chunks(batch_size.unwrap_or(1000)) {
1722            task.push(self.add_or_update(document_batch, primary_key).await?);
1723        }
1724        Ok(task)
1725    }
1726
1727    /// Get similar documents in the index.
1728    ///
1729    /// # Example
1730    ///
1731    /// ```no_run
1732    /// # use serde::{Serialize, Deserialize};
1733    /// # use meilisearch_sdk::{client::*, indexes::*, similar::*};
1734    /// #
1735    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1736    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1737    /// #
1738    /// # #[derive(Serialize, Deserialize, Debug)]
1739    /// # struct Movie {
1740    /// #    name: String,
1741    /// #    description: String,
1742    /// # }
1743    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1744    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1745    /// # let movies = client.index("similar_query");
1746    /// #
1747    /// let query = SimilarQuery::new(&movies, "1", "default");
1748    /// let results = movies.execute_similar_query::<Movie>(&query).await.unwrap();
1749    ///
1750    /// assert!(results.hits.len() > 0);
1751    /// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1752    /// # });
1753    /// ```
1754    pub async fn execute_similar_query<T: 'static + DeserializeOwned + Send + Sync>(
1755        &self,
1756        body: &SimilarQuery<'_, Http>,
1757    ) -> Result<SimilarResults<T>, Error> {
1758        self.client
1759            .http_client
1760            .request::<(), &SimilarQuery<Http>, SimilarResults<T>>(
1761                &format!("{}/indexes/{}/similar", self.client.host, self.uid),
1762                Method::Post { body, query: () },
1763                200,
1764            )
1765            .await
1766    }
1767
1768    pub fn similar_search<'a>(
1769        &'a self,
1770        document_id: &'a str,
1771        index_name: &'a str,
1772    ) -> SimilarQuery<'a, Http> {
1773        SimilarQuery::new(self, document_id, index_name)
1774    }
1775}
1776
1777impl<Http: HttpClient> AsRef<str> for Index<Http> {
1778    fn as_ref(&self) -> &str {
1779        &self.uid
1780    }
1781}
1782
1783/// An [`IndexUpdater`] used to update the specifics of an index.
1784///
1785/// # Example
1786///
1787/// ```
1788/// # use meilisearch_sdk::{client::*, indexes::*, task_info::*, tasks::{Task, SucceededTask}};
1789/// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1790/// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1791/// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1792/// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1793/// # let index = client
1794/// #   .create_index("index_updater", None)
1795/// #   .await
1796/// #   .unwrap()
1797/// #   .wait_for_completion(&client, None, None)
1798/// #   .await
1799/// #   .unwrap()
1800/// # // Once the task finished, we try to create an `Index` out of it
1801/// #   .try_make_index(&client)
1802/// #   .unwrap();
1803/// let task = IndexUpdater::new("index_updater", &client)
1804///     .with_primary_key("special_id")
1805///     .execute()
1806///     .await
1807///     .unwrap()
1808///     .wait_for_completion(&client, None, None)
1809///     .await
1810///     .unwrap();
1811///
1812/// let index = client.get_index("index_updater").await.unwrap();
1813///
1814/// assert_eq!(index.primary_key, Some("special_id".to_string()));
1815/// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1816/// # });
1817/// ```
1818#[derive(Debug, Serialize, Clone)]
1819#[serde(rename_all = "camelCase")]
1820pub struct IndexUpdater<'a, Http: HttpClient> {
1821    #[serde(skip)]
1822    pub client: &'a Client<Http>,
1823    #[serde(skip_serializing)]
1824    pub uid: String,
1825    /// New uid to rename the index to
1826    #[serde(rename = "uid", skip_serializing_if = "Option::is_none")]
1827    pub new_uid: Option<String>,
1828    pub primary_key: Option<String>,
1829}
1830
1831impl<'a, Http: HttpClient> IndexUpdater<'a, Http> {
1832    pub fn new(uid: impl AsRef<str>, client: &Client<Http>) -> IndexUpdater<'_, Http> {
1833        IndexUpdater {
1834            client,
1835            primary_key: None,
1836            uid: uid.as_ref().to_string(),
1837            new_uid: None,
1838        }
1839    }
1840    /// Define the new `primary_key` to set on the [Index].
1841    ///
1842    /// # Example
1843    ///
1844    /// ```
1845    /// # use meilisearch_sdk::{client::*, indexes::*, task_info::*, tasks::{Task, SucceededTask}};
1846    /// #
1847    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1848    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1849    /// #
1850    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1851    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1852    /// # let index = client
1853    /// #   .create_index("index_updater_with_primary_key", None)
1854    /// #   .await
1855    /// #   .unwrap()
1856    /// #   .wait_for_completion(&client, None, None)
1857    /// #   .await
1858    /// #   .unwrap()
1859    /// # // Once the task finished, we try to create an `Index` out of it
1860    /// #   .try_make_index(&client)
1861    /// #   .unwrap();
1862    /// let task = IndexUpdater::new("index_updater_with_primary_key", &client)
1863    ///     .with_primary_key("special_id")
1864    ///     .execute()
1865    ///     .await
1866    ///     .unwrap()
1867    ///     .wait_for_completion(&client, None, None)
1868    ///     .await
1869    ///     .unwrap();
1870    ///
1871    /// let index = client.get_index("index_updater_with_primary_key").await.unwrap();
1872    ///
1873    /// assert_eq!(index.primary_key, Some("special_id".to_string()));
1874    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1875    /// # });
1876    /// ```
1877    pub fn with_primary_key(
1878        &mut self,
1879        primary_key: impl AsRef<str>,
1880    ) -> &mut IndexUpdater<'a, Http> {
1881        self.primary_key = Some(primary_key.as_ref().to_string());
1882        self
1883    }
1884
1885    /// Define a new `uid` to rename the index.
1886    pub fn with_uid(&mut self, new_uid: impl AsRef<str>) -> &mut IndexUpdater<'a, Http> {
1887        self.new_uid = Some(new_uid.as_ref().to_string());
1888        self
1889    }
1890
1891    /// Alias for `with_uid` with clearer intent.
1892    pub fn with_new_uid(&mut self, new_uid: impl AsRef<str>) -> &mut IndexUpdater<'a, Http> {
1893        self.with_uid(new_uid)
1894    }
1895
1896    /// Execute the update of an [Index] using the [`IndexUpdater`].
1897    ///
1898    /// # Example
1899    ///
1900    /// ```
1901    /// # use meilisearch_sdk::{client::*, indexes::*, task_info::*, tasks::{Task, SucceededTask}};
1902    /// #
1903    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1904    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1905    /// #
1906    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1907    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1908    /// # let index = client
1909    /// #   .create_index("index_updater_execute", None)
1910    /// #   .await
1911    /// #   .unwrap()
1912    /// #   .wait_for_completion(&client, None, None)
1913    /// #   .await
1914    /// #   .unwrap()
1915    /// # // Once the task finished, we try to create an `Index` out of it
1916    /// #   .try_make_index(&client)
1917    /// #   .unwrap();
1918    /// let task = IndexUpdater::new("index_updater_execute", &client)
1919    ///     .with_primary_key("special_id")
1920    ///     .execute()
1921    ///     .await
1922    ///     .unwrap()
1923    ///     .wait_for_completion(&client, None, None)
1924    ///     .await
1925    ///     .unwrap();
1926    ///
1927    /// let index = client.get_index("index_updater_execute").await.unwrap();
1928    ///
1929    /// assert_eq!(index.primary_key, Some("special_id".to_string()));
1930    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
1931    /// # });
1932    /// ```
1933    pub async fn execute(&'a self) -> Result<TaskInfo, Error> {
1934        self.client
1935            .http_client
1936            .request::<(), &IndexUpdater<Http>, TaskInfo>(
1937                &format!("{}/indexes/{}", self.client.host, self.uid),
1938                Method::Patch {
1939                    query: (),
1940                    body: self,
1941                },
1942                202,
1943            )
1944            .await
1945    }
1946}
1947
1948impl<Http: HttpClient> AsRef<str> for IndexUpdater<'_, Http> {
1949    fn as_ref(&self) -> &str {
1950        &self.uid
1951    }
1952}
1953
1954impl<'a, Http: HttpClient> AsRef<IndexUpdater<'a, Http>> for IndexUpdater<'a, Http> {
1955    fn as_ref(&self) -> &IndexUpdater<'a, Http> {
1956        self
1957    }
1958}
1959
1960#[derive(Debug, Clone, Deserialize)]
1961#[serde(rename_all = "camelCase")]
1962pub struct IndexStats {
1963    /// Total number of documents in an index
1964    pub number_of_documents: usize,
1965
1966    /// Total number of documents with at least one embedding
1967    pub number_of_embedded_documents: usize,
1968
1969    /// Total number of embeddings in an index
1970    pub number_of_embeddings: usize,
1971
1972    /// Storage space claimed by all documents in the index in bytes
1973    pub raw_document_db_size: usize,
1974
1975    /// Total size of the documents stored in an index divided by the number of documents in that same index
1976    pub avg_document_size: usize,
1977
1978    /// If `true`, the index is still processing documents and attempts to search will yield impredictable results
1979    pub is_indexing: bool,
1980
1981    /// Shows every field in the index along with the total number of documents containing that field in said index
1982    pub field_distribution: HashMap<String, usize>,
1983}
1984
1985/// An [`IndexesQuery`] containing filter and pagination parameters when searching for [Indexes](Index).
1986///
1987/// # Example
1988///
1989/// ```
1990/// # use meilisearch_sdk::{client::*, indexes::*};
1991/// #
1992/// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
1993/// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
1994/// #
1995/// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
1996/// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
1997/// # let index = client
1998/// #   .create_index("index_query_builder", None)
1999/// #   .await
2000/// #   .unwrap()
2001/// #   .wait_for_completion(&client, None, None)
2002/// #   .await
2003/// #   .unwrap()
2004/// #   // Once the task finished, we try to create an `Index` out of it.
2005/// #   .try_make_index(&client)
2006/// #   .unwrap();
2007/// let mut indexes = IndexesQuery::new(&client)
2008///     .with_limit(1)
2009///     .execute().await.unwrap();
2010///
2011/// assert_eq!(indexes.results.len(), 1);
2012/// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
2013/// # });
2014/// ```
2015#[derive(Debug, Serialize, Clone)]
2016#[serde(rename_all = "camelCase")]
2017pub struct IndexesQuery<'a, Http: HttpClient> {
2018    #[serde(skip_serializing)]
2019    pub client: &'a Client<Http>,
2020    /// The number of [Indexes](Index) to skip.
2021    ///
2022    /// If the value of the parameter `offset` is `n`, the `n` first indexes will not be returned.
2023    /// This is helpful for pagination.
2024    ///
2025    /// Example: If you want to skip the first index, set offset to `1`.
2026    #[serde(skip_serializing_if = "Option::is_none")]
2027    pub offset: Option<usize>,
2028
2029    /// The maximum number of [Indexes](Index) returned.
2030    ///
2031    /// If the value of the parameter `limit` is `n`, there will never be more than `n` indexes in the response.
2032    /// This is helpful for pagination.
2033    ///
2034    /// Example: If you don't want to get more than two indexes, set limit to `2`.
2035    ///
2036    /// **Default: `20`**
2037    #[serde(skip_serializing_if = "Option::is_none")]
2038    pub limit: Option<usize>,
2039}
2040
2041impl<'a, Http: HttpClient> IndexesQuery<'a, Http> {
2042    #[must_use]
2043    pub fn new(client: &Client<Http>) -> IndexesQuery<'_, Http> {
2044        IndexesQuery {
2045            client,
2046            offset: None,
2047            limit: None,
2048        }
2049    }
2050
2051    /// Specify the offset.
2052    ///
2053    /// # Example
2054    ///
2055    /// ```
2056    /// # use meilisearch_sdk::{client::*, indexes::*};
2057    /// #
2058    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
2059    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
2060    /// #
2061    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
2062    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
2063    /// # let index = client
2064    /// #   .create_index("index_query_with_offset", None)
2065    /// #   .await
2066    /// #   .unwrap()
2067    /// #   .wait_for_completion(&client, None, None)
2068    /// #   .await
2069    /// #   .unwrap()
2070    /// #   // Once the task finished, we try to create an `Index` out of it
2071    /// #   .try_make_index(&client)
2072    /// #   .unwrap();
2073    /// let mut indexes = IndexesQuery::new(&client)
2074    ///     .with_offset(1)
2075    ///     .execute().await.unwrap();
2076    ///
2077    /// assert_eq!(indexes.offset, 1);
2078    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
2079    /// # });
2080    /// ```
2081    pub fn with_offset(&mut self, offset: usize) -> &mut IndexesQuery<'a, Http> {
2082        self.offset = Some(offset);
2083        self
2084    }
2085
2086    /// Specify the maximum number of [Indexes](Index) to return.
2087    ///
2088    /// # Example
2089    ///
2090    /// ```
2091    /// # use meilisearch_sdk::{client::*, indexes::*};
2092    /// #
2093    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
2094    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
2095    /// #
2096    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
2097    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
2098    /// # let index = client
2099    /// #   .create_index("index_query_with_limit", None)
2100    /// #   .await
2101    /// #   .unwrap()
2102    /// #   .wait_for_completion(&client, None, None)
2103    /// #   .await
2104    /// #   .unwrap()
2105    /// #   // Once the task finished, we try to create an `Index` out of it
2106    /// #   .try_make_index(&client)
2107    /// #   .unwrap();
2108    /// let mut indexes = IndexesQuery::new(&client)
2109    ///     .with_limit(1)
2110    ///     .execute().await.unwrap();
2111    ///
2112    /// assert_eq!(indexes.results.len(), 1);
2113    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
2114    /// # });
2115    /// ```
2116    pub fn with_limit(&mut self, limit: usize) -> &mut IndexesQuery<'a, Http> {
2117        self.limit = Some(limit);
2118        self
2119    }
2120    /// Get [Indexes](Index).
2121    ///
2122    /// # Example
2123    ///
2124    /// ```
2125    /// # use meilisearch_sdk::{indexes::IndexesQuery, client::Client};
2126    /// #
2127    /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
2128    /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
2129    /// #
2130    /// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
2131    /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
2132    /// # let index = client
2133    /// #   .create_index("index_query_with_execute", None)
2134    /// #   .await
2135    /// #   .unwrap()
2136    /// #   .wait_for_completion(&client, None, None)
2137    /// #   .await
2138    /// #   .unwrap()
2139    /// #   // Once the task finished, we try to create an `Index` out of it
2140    /// #   .try_make_index(&client)
2141    /// #   .unwrap();
2142    /// let mut indexes = IndexesQuery::new(&client)
2143    ///     .with_limit(1)
2144    ///     .execute().await.unwrap();
2145    ///
2146    /// assert_eq!(indexes.results.len(), 1);
2147    /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
2148    /// # });
2149    /// ```
2150    pub async fn execute(&self) -> Result<IndexesResults<Http>, Error> {
2151        self.client.list_all_indexes_with(self).await
2152    }
2153}
2154
2155#[derive(Debug, Clone)]
2156pub struct IndexesResults<Http: HttpClient = DefaultHttpClient> {
2157    pub results: Vec<Index<Http>>,
2158    pub limit: u32,
2159    pub offset: u32,
2160    pub total: u32,
2161}
2162
2163#[cfg(test)]
2164mod tests {
2165    use super::*;
2166
2167    use big_s::S;
2168    use meilisearch_test_macro::meilisearch_test;
2169    use serde_json::json;
2170
2171    #[meilisearch_test]
2172    async fn test_from_value(client: Client) {
2173        let t = OffsetDateTime::now_utc();
2174        let trfc3339 = t
2175            .format(&time::format_description::well_known::Rfc3339)
2176            .unwrap();
2177
2178        let value = json!({
2179            "createdAt": &trfc3339,
2180            "primaryKey": null,
2181            "uid": "test_from_value",
2182            "updatedAt": &trfc3339,
2183        });
2184
2185        let idx = Index {
2186            uid: S("test_from_value"),
2187            primary_key: None,
2188            created_at: Some(t),
2189            updated_at: Some(t),
2190            client: client.clone(),
2191        };
2192
2193        let res = Index::from_value(value, client).unwrap();
2194
2195        assert_eq!(res.updated_at, idx.updated_at);
2196        assert_eq!(res.created_at, idx.created_at);
2197        assert_eq!(res.uid, idx.uid);
2198        assert_eq!(res.primary_key, idx.primary_key);
2199        assert_eq!(res.client.host, idx.client.host);
2200        assert_eq!(res.client.api_key, idx.client.api_key);
2201    }
2202
2203    #[meilisearch_test]
2204    async fn test_fetch_info(mut index: Index) {
2205        let res = index.fetch_info().await;
2206        assert!(res.is_ok());
2207        assert!(index.updated_at.is_some());
2208        assert!(index.created_at.is_some());
2209        assert!(index.primary_key.is_none());
2210    }
2211
2212    #[meilisearch_test]
2213    async fn test_get_documents(index: Index) {
2214        #[derive(Debug, Serialize, Deserialize, PartialEq)]
2215        struct Object {
2216            id: usize,
2217            value: String,
2218            kind: String,
2219        }
2220        let res = index.get_documents::<Object>().await.unwrap();
2221
2222        assert_eq!(res.limit, 20);
2223    }
2224
2225    #[meilisearch_test]
2226    async fn test_get_documents_with(index: Index) {
2227        #[derive(Debug, Serialize, Deserialize, PartialEq)]
2228        struct Object {
2229            id: usize,
2230            value: String,
2231            kind: String,
2232        }
2233
2234        let mut documents_query = DocumentsQuery::new(&index);
2235        documents_query.with_limit(1).with_offset(2);
2236
2237        let res = index
2238            .get_documents_with::<Object>(&documents_query)
2239            .await
2240            .unwrap();
2241
2242        assert_eq!(res.limit, 1);
2243        assert_eq!(res.offset, 2);
2244    }
2245
2246    #[meilisearch_test]
2247    async fn test_update_document_json(client: Client, index: Index) -> Result<(), Error> {
2248        let old_json = [
2249            json!({ "id": 1, "body": "doggo" }),
2250            json!({ "id": 2, "body": "catto" }),
2251        ];
2252        let updated_json = [
2253            json!({ "id": 1, "second_body": "second_doggo" }),
2254            json!({ "id": 2, "second_body": "second_catto" }),
2255        ];
2256
2257        let task = index
2258            .add_documents(&old_json, Some("id"))
2259            .await
2260            .unwrap()
2261            .wait_for_completion(&client, None, None)
2262            .await
2263            .unwrap();
2264        let _ = index.get_task(task).await?;
2265
2266        let task = index
2267            .add_or_update(&updated_json, None)
2268            .await
2269            .unwrap()
2270            .wait_for_completion(&client, None, None)
2271            .await
2272            .unwrap();
2273
2274        let status = index.get_task(task).await?;
2275        let elements = index.get_documents::<serde_json::Value>().await.unwrap();
2276
2277        assert!(matches!(status, Task::Succeeded { .. }));
2278        assert_eq!(elements.results.len(), 2);
2279
2280        let expected_result = vec![
2281            json!( {"body": "doggo", "id": 1, "second_body": "second_doggo"}),
2282            json!( {"body": "catto", "id": 2, "second_body": "second_catto"}),
2283        ];
2284
2285        assert_eq!(elements.results, expected_result);
2286
2287        Ok(())
2288    }
2289
2290    #[meilisearch_test]
2291    async fn test_rename_index_via_update(client: Client, name: String) -> Result<(), Error> {
2292        let from = format!("{name}_from");
2293        let to = format!("{name}_to");
2294
2295        // Create source index
2296        client
2297            .create_index(&from, None)
2298            .await?
2299            .wait_for_completion(&client, None, None)
2300            .await?;
2301
2302        // Rename using index update
2303        IndexUpdater::new(&from, &client)
2304            .with_uid(&to)
2305            .execute()
2306            .await?
2307            .wait_for_completion(&client, None, None)
2308            .await?;
2309
2310        // New index should exist
2311        let new_index = client.get_index(&to).await?;
2312        assert_eq!(new_index.uid, to);
2313
2314        // Old index should no longer exist
2315        let old_index = client.get_index(&from).await;
2316        assert!(old_index.is_err(), "old uid still resolves after rename");
2317
2318        // cleanup
2319        new_index
2320            .delete()
2321            .await?
2322            .wait_for_completion(&client, None, None)
2323            .await?;
2324
2325        // defensive cleanup if rename semantics change
2326        if let Ok(idx) = client.get_index(&from).await {
2327            idx.delete()
2328                .await?
2329                .wait_for_completion(&client, None, None)
2330                .await?;
2331        }
2332
2333        Ok(())
2334    }
2335
2336    #[meilisearch_test]
2337    async fn test_add_documents_ndjson(client: Client, index: Index) -> Result<(), Error> {
2338        let ndjson = r#"{ "id": 1, "body": "doggo" }{ "id": 2, "body": "catto" }"#.as_bytes();
2339
2340        let task = index
2341            .add_documents_ndjson(ndjson, Some("id"))
2342            .await?
2343            .wait_for_completion(&client, None, None)
2344            .await?;
2345
2346        let status = index.get_task(task).await?;
2347        let elements = index.get_documents::<serde_json::Value>().await.unwrap();
2348        assert!(matches!(status, Task::Succeeded { .. }));
2349        assert_eq!(elements.results.len(), 2);
2350
2351        Ok(())
2352    }
2353
2354    #[meilisearch_test]
2355    async fn test_update_documents_ndjson(client: Client, index: Index) -> Result<(), Error> {
2356        let old_ndjson = r#"{ "id": 1, "body": "doggo" }{ "id": 2, "body": "catto" }"#.as_bytes();
2357        let updated_ndjson =
2358            r#"{ "id": 1, "second_body": "second_doggo" }{ "id": 2, "second_body": "second_catto" }"#.as_bytes();
2359        // Add first njdson document
2360        let task = index
2361            .add_documents_ndjson(old_ndjson, Some("id"))
2362            .await?
2363            .wait_for_completion(&client, None, None)
2364            .await?;
2365        let _ = index.get_task(task).await?;
2366
2367        // Update via njdson document
2368        let task = index
2369            .update_documents_ndjson(updated_ndjson, Some("id"))
2370            .await?
2371            .wait_for_completion(&client, None, None)
2372            .await?;
2373
2374        let status = index.get_task(task).await?;
2375        let elements = index.get_documents::<serde_json::Value>().await.unwrap();
2376
2377        assert!(matches!(status, Task::Succeeded { .. }));
2378        assert_eq!(elements.results.len(), 2);
2379
2380        let expected_result = vec![
2381            json!( {"body": "doggo", "id": 1, "second_body": "second_doggo"}),
2382            json!( {"body": "catto", "id": 2, "second_body": "second_catto"}),
2383        ];
2384
2385        assert_eq!(elements.results, expected_result);
2386
2387        Ok(())
2388    }
2389
2390    #[meilisearch_test]
2391    async fn test_add_documents_csv(client: Client, index: Index) -> Result<(), Error> {
2392        let csv_input = "id,body\n1,\"doggo\"\n2,\"catto\"".as_bytes();
2393
2394        let task = index
2395            .add_documents_csv(csv_input, Some("id"))
2396            .await?
2397            .wait_for_completion(&client, None, None)
2398            .await?;
2399
2400        let status = index.get_task(task).await?;
2401        let elements = index.get_documents::<serde_json::Value>().await.unwrap();
2402        assert!(matches!(status, Task::Succeeded { .. }));
2403        assert_eq!(elements.results.len(), 2);
2404
2405        Ok(())
2406    }
2407
2408    #[meilisearch_test]
2409    async fn test_update_documents_csv(client: Client, index: Index) -> Result<(), Error> {
2410        let old_csv = "id,body\n1,\"doggo\"\n2,\"catto\"".as_bytes();
2411        let updated_csv = "id,body\n1,\"new_doggo\"\n2,\"new_catto\"".as_bytes();
2412        // Add first njdson document
2413        let task = index
2414            .add_documents_csv(old_csv, Some("id"))
2415            .await?
2416            .wait_for_completion(&client, None, None)
2417            .await?;
2418        let _ = index.get_task(task).await?;
2419
2420        // Update via njdson document
2421        let task = index
2422            .update_documents_csv(updated_csv, Some("id"))
2423            .await?
2424            .wait_for_completion(&client, None, None)
2425            .await?;
2426
2427        let status = index.get_task(task).await?;
2428        let elements = index.get_documents::<serde_json::Value>().await.unwrap();
2429
2430        assert!(matches!(status, Task::Succeeded { .. }));
2431        assert_eq!(elements.results.len(), 2);
2432
2433        let expected_result = vec![
2434            json!( {"body": "new_doggo", "id": "1"}),
2435            json!( {"body": "new_catto", "id": "2"}),
2436        ];
2437
2438        assert_eq!(elements.results, expected_result);
2439
2440        Ok(())
2441    }
2442    #[meilisearch_test]
2443
2444    async fn test_get_one_task(client: Client, index: Index) -> Result<(), Error> {
2445        let task = index
2446            .delete_all_documents()
2447            .await?
2448            .wait_for_completion(&client, None, None)
2449            .await?;
2450
2451        let status = index.get_task(task).await?;
2452
2453        match status {
2454            Task::Enqueued {
2455                content:
2456                    EnqueuedTask {
2457                        index_uid: Some(index_uid),
2458                        ..
2459                    },
2460            }
2461            | Task::Processing {
2462                content:
2463                    ProcessingTask {
2464                        index_uid: Some(index_uid),
2465                        ..
2466                    },
2467            }
2468            | Task::Failed {
2469                content:
2470                    FailedTask {
2471                        task:
2472                            SucceededTask {
2473                                index_uid: Some(index_uid),
2474                                ..
2475                            },
2476                        ..
2477                    },
2478            }
2479            | Task::Succeeded {
2480                content:
2481                    SucceededTask {
2482                        index_uid: Some(index_uid),
2483                        ..
2484                    },
2485            } => assert_eq!(index_uid, *index.uid),
2486            task => panic!(
2487                "The task should have an index_uid that is not null {:?}",
2488                task
2489            ),
2490        }
2491        Ok(())
2492    }
2493
2494    #[meilisearch_test]
2495    async fn test_compact_index_succeeds(client: Client, index: Index) -> Result<(), Error> {
2496        let task = index
2497            .compact()
2498            .await?
2499            .wait_for_completion(&client, None, None)
2500            .await?;
2501
2502        assert!(task.is_success());
2503        Ok(())
2504    }
2505}