>
{
{ "great", new string[] { "fantastic" } },
{ "fantastic", new string[] { "great" } }
};
await client.Index("movies").UpdateSynonymsAsync(synonyms);
```
```rust
let mut synonyms = std::collections::HashMap::new();
synonyms.insert(String::from("great"), vec![String::from("fantastic")]);
synonyms.insert(String::from("fantastic"), vec![String::from("great")]);
let task: TaskInfo = client
.index("movies")
.set_synonyms(&synonyms)
.await
.unwrap();
```
```swift
let synonyms: [String: [String]] = [
"great": ["fantastic"],
"fantastic": ["great"]
]
client.index("movies").updateSynonyms(synonyms) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').updateSynonyms({
'great': ['fantastic'],
'fantastic': ['great'],
});
```
With the new settings, searching for `great` returns 595 results and `fantastic` returns 423 results. This is due to various factors like [typos](/learn/relevancy/typo_tolerance_settings#minwordsizefortypos) and [splitting the query](/learn/engine/concat#split-queries) to find relevant documents. The search for `great` will allow only one typo (for example, `create`) and take into account all variations of `great` (for instance, `greatest`) along with `fantastic`.
The number of search results may vary depending on changes to the `movies` dataset.
### Normalization
All synonyms are **lowercased** and **de-unicoded** during the indexing process.
##### Example
Consider a situation where `Résumé` and `CV` are set as synonyms.
```json
{
"Résumé": [
"CV"
],
"CV": [
"Résumé"
]
}
```
A search for `cv` would return any documents containing `cv` or `CV`, in addition to any that contain `Résumé`, `resumé`, `resume`, etc., unaffected by case or accent marks.
### One-way association
Use this when you want one word to be synonymous with another, but not the other way around.
```
phone => iphone
```
A search for `phone` will return documents containing `iphone` as if they contained the word `phone`.
However, if you search for `iphone`, documents containing `phone` will be ranked lower in the results due to [the typo rule](/learn/relevancy/ranking_rules).
##### Example
To create a one-way synonym list, this is the JSON syntax that should be [added to the settings](/reference/api/settings#update-synonyms).
```json
{
"phone": [
"iphone"
]
}
```
### Relevancy
**The exact search query will always take precedence over its synonyms.** The `exactness` ranking rule favors exact words over synonyms when ranking search results.
Taking the following set of search results:
```json
[
{
"id": 0,
"title": "Ghouls 'n Ghosts"
},
{
"id": 1,
"title": "Phoenix Wright: Spirit of Justice"
}
]
```
If you configure `ghost` as a synonym of `spirit`, queries searching for `spirit` will return document `1` before document `0`.
### Mutual association
By associating one or more synonyms with each other, they will be considered the same in both directions.
```
shoe <=> boot <=> slipper <=> sneakers
```
When a search is done with one of these words, all synonyms will be considered as the same word and will appear in the search results.
##### Example
To create a mutual association between four words, this is the JSON syntax that should be [added to the settings](/reference/api/settings#update-synonyms).
```json
{
"shoe": [
"boot",
"slipper",
"sneakers"
],
"boot": [
"shoe",
"slipper",
"sneakers"
],
"slipper": [
"shoe",
"boot",
"sneakers"
],
"sneakers": [
"shoe",
"boot",
"slipper"
]
}
```
### Multi-word synonyms
Meilisearch treats multi-word synonyms as [phrases](/reference/api/search#phrase-search).
##### Example
Suppose you set `San Francisco` and `SF` as synonyms with a [mutual association](#mutual-association)
```json
{
"san francisco": [
"sf"
],
"sf": [
"san francisco"
]
}
```
If you input `SF` as a search query, Meilisearch will also return results containing the phrase `San Francisco`. However, depending on the ranking rules, they might be considered less [relevant](/learn/relevancy/relevancy) than those containing `SF`. The reverse is also true: if your query is `San Francisco`, documents containing `San Francisco` may rank higher than those containing `SF`.
### Maximum number of synonyms per term
A single term may have up to 50 synonyms. Meilisearch silently ignores any synonyms beyond this limit. For example, if you configure 51 synonyms for `book`, Meilisearch will only return results containing the term itself and the first 50 synonyms.
If any synonyms for a term contain more than one word, the sum of all words across all synonyms for that term cannot exceed 100 words. Meilisearch silently ignores any synonyms beyond this limit. For example, if you configure 40 synonyms for `computer` in your application, taken together these synonyms must contain fewer than 100 words.
---
title: Known limitations — Meilisearch documentation
description: Meilisearch has a number of known limitations. These are hard limits you cannot change and should take into account when designing your application.
---
## Known limitations
Meilisearch has a number of known limitations. Some of these limitations are the result of intentional design trade-offs, while others can be attributed to [LMDB](/learn/engine/storage), the key-value store that Meilisearch uses under the hood.
This article covers hard limits that cannot be altered. Meilisearch also has some default limits that _can_ be changed, such as a [default payload limit of 100MB](/learn/self_hosted/configure_meilisearch_at_launch#payload-limit-size) and a [default search limit of 20 hits](/reference/api/search#limit).
### Maximum Meilisearch Cloud upload size
**Limitation:** The maximum file upload size when using the Meilisearch Cloud interface is 20mb.
**Explanation:** Handling large files may result in degraded user experience and performance issues. To add datasets larger than 20mb to a Meilisearch Cloud project, use the [add documents endpoint](/reference/api/documents#add-or-replace-documents) or [`meilisearch-importer`](https://github.com/meilisearch/meilisearch-importer).
### Maximum number of query words
**Limitation:** The maximum number of terms taken into account for each [search query](/reference/api/search#query-q) is 10. If a search query includes more than 10 words, all words after the 10th will be ignored.
**Explanation:** Queries with many search terms can lead to long response times. This goes against our goal of providing a fast search-as-you-type experience.
### Maximum number of words per attribute
**Limitation:** Meilisearch can index a maximum of 65535 positions per attribute. Any words exceeding the 65535 position limit will be silently ignored.
**Explanation:** This limit is enforced for relevancy reasons. The more words there are in a given attribute, the less relevant the search queries will be.
#### Example
Suppose you have three similar queries: `Hello World`, `Hello, World`, and `Hello - World`. Due to how our tokenizer works, each one of them will be processed differently and take up a different number of "positions" in our internal database.
If your query is `Hello World`:
- `Hello` takes the position `0` of the attribute
- `World` takes the position `1` of the attribute
If your query is `Hello, World`:
- `Hello` takes the position `0` of the attribute
- `,` takes the position `8` of the attribute
- `World` takes the position `9` of the attribute
`,` takes 8 positions as it is a hard separator. You can read more about word separators in our [article about data types](/learn/engine/datatypes#string).
If your query is `Hello - World`:
- `Hello` takes the position `0` of the attribute
- `-` takes the position `1` of the attribute
- `World` takes the position `2` of the attribute
`-` takes 1 position as it is a soft separator. You can read more about word separators in our [article about data types](/learn/engine/datatypes#string).
### Maximum number of attributes per document
**Limitation:** Meilisearch can index a maximum of **65,536 attributes per document**. If a document contains more than 65,536 attributes, an error will be thrown.
**Explanation:** This limit is enforced for performance and storage reasons. Overly large internal data structures—resulting from documents with too many fields—lead to overly large databases on disk, and slower search performance.
### Maximum number of documents in an index
**Limitation:** An index can contain no more than 4,294,967,296 documents.
**Explanation:** This is the largest possible value for a 32-bit unsigned integer. Since Meilisearch's engine uses unsigned integers to identify documents internally, this is the maximum number of documents that can be stored in an index.
### Maximum number of concurrent search requests
**Limitation:** Meilisearch handles a maximum of 1000 concurrent search requests.
**Explanation:** This limit exists to prevent Meilisearch from queueing an unlimited number of requests and potentially consuming an unbounded amount of memory. If Meilisearch receives a new request when the queue is already full, it drops a random search request and returns a 503 `too_many_search_requests` error with a `Retry-After` header set to 10 seconds. Configure this limit with [`--experimental-search-queue-size`](/learn/self_hosted/configure_meilisearch_at_launch).
### Length of primary key values
**Limitation:** Primary key values are limited to 511 bytes.
**Explanation:** Meilisearch stores primary key values as LMDB keys, a data type whose size is limited to 511 bytes. If a primary key value exceeds 511 bytes, the task containing these documents will fail.
### Length of individual `filterableAttributes` values
**Limitation:** Individual `filterableAttributes` values are limited to 468 bytes.
**Explanation:** Meilisearch stores `filterableAttributes` values as keys in LMDB, a data type whose size is limited to 511 bytes, to which Meilisearch adds a margin of 44 bytes. Note that this only applies to individual values—for example, a `genres` attribute can contain any number of values such as `horror`, `comedy`, or `cyberpunk` as long as each one of them is smaller than 468 bytes.
### Maximum filter depth
**Limitation:** searches using the [`filter` search parameter](/reference/api/search#filter) may have a maximum filtering depth of 2000.
**Explanation:** mixing and alternating `AND` and `OR` operators filters creates nested logic structures. Excessive nesting can lead to stack overflow.
#### Example
The following filter is composed of a number of filter expressions. Since these statements are all chained with `OR` operators, there is no nesting:
```sql
genre = "romance" OR genre = "horror" OR genre = "adventure"
```
Replacing `OR` with `AND` does not change the filter structure. The following filter's nesting level remains 1:
```sql
genre = "romance" AND genre = "horror" AND genre = "adventure"
```
Nesting only occurs when alternating `AND` and `OR` operators. The following example fetches documents that either belong only to `user` `1`, or belong to users `2` and `3`:
```sql
## AND is nested inside OR, creating a second level of nesting
user = 1 OR user = 2 AND user = 3
```
Adding parentheses can help visualizing nesting depth:
```sql
## Depth 2
user = 1 OR (user = 2 AND user = 3)
## Depth 4
user = 1 OR (user = 2 AND (user = 3 OR (user = 4 AND user = 5)))
## Though this filter is longer, its nesting depth is still 2
user = 1 OR (user = 2 AND user = 3) OR (user = 4 AND user = 5) OR user = 6
```
### Size of integer fields
**Limitation:** Meilisearch can only exactly represent integers between -2⁵³ and 2⁵³.
**Explanation:** Meilisearch stores numeric values as double-precision floating-point numbers. This allows for greater precision and increases the range of magnitudes that Meilisearch can represent, but leads to inaccuracies in [values beyond certain thresholds](https://en.wikipedia.org/wiki/Double-precision_floating-point_format#Precision_limitations_on_integer_values).
### Maximum number of results per search
**Limitation:** By default, Meilisearch returns up to 1000 documents per search.
**Explanation:** Meilisearch limits the maximum amount of returned search results to protect your database from malicious scraping. You may change this by using the `maxTotalHits` property of the [pagination index settings](/reference/api/settings#pagination-object). `maxTotalHits` only applies to the [search route](/reference/api/search) and has no effect on the [get documents with POST](/reference/api/documents#get-documents-with-post) and [get documents with GET](/reference/api/documents#get-documents-with-get) endpoints.
### Large datasets and internal errors
**Limitation:** Meilisearch might throw an internal error when indexing large batches of documents.
**Explanation:** Indexing a large batch of documents, such as a JSON file over 3.5GB in size, can result in Meilisearch opening too many file descriptors. Depending on your machine, this might reach your system's default resource usage limits and trigger an internal error. Use [`ulimit`](https://www.ibm.com/docs/en/aix/7.1?topic=u-ulimit-command) or a similar tool to increase resource consumption limits before running Meilisearch. For example, call `ulimit -Sn 3000` in a UNIX environment to raise the number of allowed open file descriptors to 3000.
### Maximum database size
**Limitation:** Meilisearch supports a maximum index size of around 80TiB on Linux environments. For performance reasons, Meilisearch recommends keeping indexes under 2TiB.
**Explanation:** Meilisearch can accommodate indexes of any size as long the combined size of active databases is below the maximum virtual address space the OS devotes to a single process. On 64-bit Linux, this limit is approximately 80TiB.
### Maximum task database size
**Limitation:** Meilisearch supports a maximum task database size of 10GiB.
**Explanation:** Depending on your setup, 10GiB should correspond to 5M to 15M tasks. Once the task database contains over 1M entries (roughly 1GiB on average), Meilisearch tries to automatically delete finished tasks while continuing to enqueue new tasks as usual. This ensures the task database does not use an excessive amount of resources. If your database reaches the 10GiB limit, Meilisearch will log a warning indicating the engine is not working properly and refuse to enqueue new tasks.
### Maximum number of indexes in an instance
**Limitation:** Meilisearch can accommodate an arbitrary number of indexes as long as their size does not exceed 2TiB. When dealing with larger indexes, Meilisearch can accommodate up to 20 indexes as long as their combined size does not exceed the OS's virtual address space limit.
**Explanation:** While Meilisearch supports an arbitrary number of indexes under 2TiB, accessing hundreds of different databases in short periods of time might lead to decreased performance and should be avoided when possible.
### Facet Search limitation
**Limitation:** When [searching for facet values](/reference/api/facet_search), Meilisearch returns a maximum of 100 facets.
**Explanation:** the limit to the maximum number of returned facets has been implemented to offer a good balance between usability and comprehensive results. Facet search allows users to filter a large list of facets so they may quickly find categories relevant to their query. This is different from searching through an index of documents. Faceting index settings such as the `maxValuesPerFacet` limit do not impact facet search and only affect queries searching through documents.
---
title: Experimental features overview — Meilisearch documentation
description: This article covers how to activate activate and configure Meilisearch experimental features.
---
## Experimental features
Meilisearch periodically introduces new experimental features. Experimental features are not always ready for production, but offer functionality that might benefit some users.
An experimental feature's API can change significantly and become incompatible between releases. Keep this in mind when using experimental features in a production environment.
Meilisearch makes experimental features available expecting they will become stable in a future release, but this is not guaranteed.
### Activating experimental features
Experimental features fall into two groups based on how they are activated or deactivated:
1. Those that are activated at launch with a command-line flag or environment variable
2. Those that are activated with the [`/experimental-features` API route](/reference/api/experimental_features).
### Activating experimental features at launch
Some experimental features can be [activated at launch](/learn/self_hosted/configure_meilisearch_at_launch), for example with a command-line flag:
```sh
./meilisearch --experimental-enable-metrics
```
Flags and environment variables for experimental features are not included in the [regular configuration options list](/learn/self_hosted/configure_meilisearch_at_launch#all-instance-options). Instead, consult the specific documentation page for the feature you are interested in, which can be found in the experimental section.
Command-line flags for experimental features are always prefixed with `--experimental`. Environment variables for experimental features are always prefixed with `MEILI_EXPERIMENTAL`.
Activating or deactivating experimental features this way requires you to relaunch Meilisearch.
#### Activating experimental features during runtime
Some experimental features can be activated via an HTTP call using the [`/experimental-features` API route](/reference/api/experimental_features):
```bash
curl \
-X PATCH 'MEILISEARCH_URL/experimental-features/' \
-H 'Content-Type: application/json' \
--data-binary '{
"metrics": true
}'
```
```ruby
client.update_experimental_features(metrics: true)
```
```go
client.ExperimentalFeatures().SetMetrics(true).Update()
```
```rust
let client = Client::new("http://localhost:7700", Some("apiKey"));
let features = ExperimentalFeatures::new(&client);
features.set_metrics(true)
let res = features
.update()
.await
.unwrap();
```
Activating or deactivating experimental features this way does not require you to relaunch Meilisearch.
### Current experimental features
| Name | Description | How to configure |
| ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------- |
| [Limit task batch size](/learn/self_hosted/configure_meilisearch_at_launch) | Limits number of tasks processed in a single batch | CLI flag or environment variable |
| [Log customization](/reference/api/logs) | Customize log output and set up log streams | CLI flag or environment variable, API route |
| [Metrics API](/reference/api/metrics) | Exposes Prometheus-compatible analytics data | CLI flag or environment variable, API route |
| [Reduce indexing memory usage](/learn/self_hosted/configure_meilisearch_at_launch) | Optimizes indexing performance | CLI flag or environment variable |
| [Replication parameters](/learn/self_hosted/configure_meilisearch_at_launch) | Alters task processing for clustering compatibility | CLI flag or environment variable |
| [Search queue size](/learn/self_hosted/configure_meilisearch_at_launch) | Configure maximum number of concurrent search requests | CLI flag or environment variable |
| [`CONTAINS` filter operator](/learn/filtering_and_sorting/filter_expression_reference#contains) | Enables usage of `CONTAINS` with the `filter` search parameter | API route |
| [Edit documents with function](/reference/api/documents#update-documents-with-function) | Use a RHAI function to edit documents directly in the Meilisearch database | API route |
| [`/network` route](/reference/api/network) | Enable `/network` route | API route |
| [Dumpless upgrade](/learn/self_hosted/configure_meilisearch_at_launch#dumpless-upgrade) | Upgrade Meilisearch without generating a dump | API route |
| [Composite embedders](/reference/api/settings#composite-embedders) | Enable composite embedders | API route |
| [Search query embedding cache](/learn/self_hosted/configure_meilisearch_at_launch#search-query-embedding-cache) | Enable a cache for search query embeddings | CLI flag or environment variable |
---
title: FAQ — Meilisearch documentation
description: Frequently asked questions
---
## FAQ
### I have never used a search engine before. Can I use Meilisearch anyway?
Of course! No knowledge of ElasticSearch or Solr is required to use Meilisearch.
Meilisearch is really **easy to use** and thus accessible to all kinds of developers.
[Take a quick tour](/learn/self_hosted/getting_started_with_self_hosted_meilisearch) to learn the basics of Meilisearch!
We also provide a lot of tools, including [SDKs](/learn/resources/sdks), to help you integrate easily Meilisearch in your project. We're adding new tools every day!
Plus, you can [contact us](https://discord.meilisearch.com) if you need any help.
### How to know if Meilisearch perfectly fits my use cases?
Since Meilisearch is an open-source and easy-to-use tool, you can give it a try using your data. Follow this [guide](/learn/self_hosted/getting_started_with_self_hosted_meilisearch) to get a quick start!
Besides, we published a [comparison between Meilisearch and other search engines](/learn/resources/comparison_to_alternatives) with the goal of providing an overview of Meilisearch alternatives.
### I am trying to add my documents but I keep receiving a `400 - Bad Request` response
The `400 - Bad request` response often means that your data is not in an expected format. You might have extraneous commas, mismatched brackets, missing quotes, etc. Meilisearch API accepts JSON, CSV, and NDJSON formats.
When [adding or replacing documents](/reference/api/documents#add-or-replace-documents), you must enclose them in an array even if there is only one new document.
### I have uploaded my documents, but I get no result when I search in my index
Your document upload probably failed. To understand why, please check the status of the document addition task using the returned [`taskUid`](/reference/api/tasks#get-one-task). If the task failed, the response should contain an `error` object.
Here is an example of a failed task:
```json
{
"uid": 1,
"indexUid": "movies",
"status": "failed",
"type": "documentAdditionOrUpdate",
"canceledBy": null,
"details": {
"receivedDocuments": 67493,
"indexedDocuments": 0
},
"error": {
"message": "Document does not have a `:primaryKey` attribute: `:documentRepresentation`.",
"code": "internal",
"type": "missing_document_id",
"link": "https://docs.meilisearch.com/errors#missing-document-id",
},
"duration": "PT1S",
"enqueuedAt": "2021-08-10T14:29:17.000000Z",
"startedAt": "2021-08-10T14:29:18.000000Z",
"finishedAt": "2021-08-10T14:29:19.000000Z"
}
```
Check your error message for more information.
### Is killing a Meilisearch process safe?
Killing Meilisearch is **safe**, even in the middle of a process (ex: adding a batch of documents). When you restart the server, it will start the task from the beginning.
More information in the [asynchronous operations guide](/learn/async/asynchronous_operations).
### Do you provide a public roadmap for Meilisearch and its integration tools?
Yes, as Meilisearch and its integration tools are open source, we maintain a [public roadmap](https://roadmap.meilisearch.com/) for the general features we plan to do.
For more accurate features and issues, everything is detailed in the issues of all our [GitHub repositories](https://github.com/meilisearch/meilisearch/issues).
### What are the recommended requirements for hosting a Meilisearch instance?
**The short answer:**
The recommended requirements for hosting a Meilisearch instance will depend on many factors, such as the number of documents, the size of those documents, the number of filters/sorts you will need, and more. For a quick estimate to start with, try to use a machine that has at least ten times the disk space of your dataset.
**The long answer:**
Indexing documents is a complex process, making it difficult to accurately estimate the size and memory use of a Meilisearch database. There are a few aspects to keep in mind when optimizing your instance.
#### Memory usage
There are two things that can cause your memory usage (RAM) to spike:
1. Adding documents
2. Updating index settings (if index contains documents)
To reduce memory use and indexing time, follow this best practice: **always update index settings before adding your documents**. This avoids unnecessary double-indexing.
#### Disk usage
The following factors have a great impact on the size of your database (in no particular order):
- The number of documents
- The size of documents
- The number of searchable fields
- The number of filterable fields
- The size of each update
- The number of different words present in the dataset
Beware heavily multi-lingual datasets and datasets with many unique words, such as IDs or URLs, as they can slow search speed and greatly increase database size. If you do have ID or URL fields, [make them non-searchable](/reference/api/settings#update-searchable-attributes) unless they are useful as search criteria.
#### Search speed
Because Meilisearch uses a [memory map](/learn/engine/storage#lmdb), **search speed is based on the ratio between RAM and database size**. In other words:
- A big database + a small amount of RAM => slow search
- A small database + tons of RAM => lightning fast search
Meilisearch also uses disk space as [virtual memory](/learn/engine/storage#memory-usage). This disk space does not correspond to database size; rather, it provides speed and flexibility to the engine by allowing it to go over the limits of physical RAM.
At this time, the number of CPU cores has no direct impact on index or search speed. However, **the more cores you provide to the engine, the more search queries it will be able to process at the same time**.
##### Speeding up Meilisearch
Meilisearch is designed to be fast (≤50ms response time), so speeding it up is rarely necessary. However, if you find that your Meilisearch instance is querying slowly, there are two primary methods to improve search performance:
1. Increase the amount of RAM (or virtual memory)
2. Reduce the size of the database
In general, we recommend the former. However, if you need to reduce the size of your database for any reason, keep in mind that:
- **More relevancy rules => a larger database**
- The proximity [ranking rule](/learn/relevancy/ranking_rules) alone can be responsible for almost 80% of database size
- Adding many attributes to [`filterableAttributes`](/reference/api/settings#filterable-attributes) also consumes a large amount of disk space
- Multi-lingual datasets are costly, so split your dataset—one language per index
- [Stop words](/reference/api/settings#stop-words) are essential to reducing database size
- Not all attributes need to be [searchable](/learn/relevancy/displayed_searchable_attributes#searchable-fields). Avoid indexing unique IDs.
### Why does Meilisearch send data to Segment? Does Meilisearch track its users?
**Meilisearch will never track or identify individual users**. That being said, we do use Segment to collect anonymous data about user trends, feature usage, and bugs.
You can read more about what metrics we collect, why we collect them, and how to disable it on our [telemetry page](/learn/resources/telemetry). Issues of transparency and privacy are very important to us, so if you feel we are lacking in this area please [open an issue](https://github.com/meilisearch/documentation/issues/new/choose) or send an email to our dedicated email address: [privacy@meilisearch.com](mailto:privacy@meilisearch.com).
---
title: Official SDKs and libraries — Meilisearch documentation
description: Meilisearch SDKs are available in many popular programming languages and frameworks. Consult this page for a full list of officially supported libraries.
---
## Official SDKs and libraries
Our team and community have worked hard to bring Meilisearch to almost all popular web development languages, frameworks, and deployment options.
New integrations are constantly in development. If you'd like to contribute, [see below](/learn/resources/sdks#contributing).
### SDKs
You can use Meilisearch API wrappers in your favorite language. These libraries support all API routes.
- [.NET](https://github.com/meilisearch/meilisearch-dotnet)
- [Dart](https://github.com/meilisearch/meilisearch-dart)
- [Golang](https://github.com/meilisearch/meilisearch-go)
- [Java](https://github.com/meilisearch/meilisearch-java)
- [JavaScript](https://github.com/meilisearch/meilisearch-js)
- [PHP](https://github.com/meilisearch/meilisearch-php)
- [Python](https://github.com/meilisearch/meilisearch-python)
- [Ruby](https://github.com/meilisearch/meilisearch-ruby)
- [Rust](https://github.com/meilisearch/meilisearch-rust)
- [Swift](https://github.com/meilisearch/meilisearch-swift)
### Framework integrations
- Laravel: the official [Laravel-Scout](https://github.com/laravel/scout) package supports Meilisearch.
- [Ruby on Rails](https://github.com/meilisearch/meilisearch-rails)
- [Symfony](https://github.com/meilisearch/meilisearch-symfony)
### Front-end tools
- [Angular](https://github.com/meilisearch/meilisearch-angular)
- [React](https://github.com/meilisearch/meilisearch-react)
- [Vue](https://github.com/meilisearch/meilisearch-vue)
- [Instant Meilisearch](https://github.com/meilisearch/meilisearch-js-plugins/tree/main/packages/instant-meilisearch)
- [Autocomplete client](https://github.com/meilisearch/meilisearch-js-plugins/tree/main/packages/autocomplete-client)
- [docs-searchbar.js](https://github.com/tauri-apps/meilisearch-docsearch)
### DevOps tools
- [meilisearch-aws](https://github.com/meilisearch/cloud-providers/)
- Guide: [How to deploy a Meilisearch instance on Amazon Web Services](/guides/deployment/aws)
- [meilisearch-digitalocean](https://github.com/meilisearch/cloud-providers/)
- Guide: [How to deploy a Meilisearch instance on DigitalOcean](/guides/deployment/digitalocean)
- [meilisearch-kubernetes](https://github.com/meilisearch/meilisearch-kubernetes)
### Platform plugins
- [VuePress plugin](https://github.com/meilisearch/vuepress-plugin-meilisearch)
- [Strapi plugin](https://github.com/meilisearch/strapi-plugin-meilisearch/)
- [Gatsby plugin](https://github.com/meilisearch/gatsby-plugin-meilisearch/)
- [Firebase](https://github.com/meilisearch/firestore-meilisearch)
### Other tools
- [docs-scraper](https://github.com/meilisearch/docs-scraper): a scraper tool to automatically read the content of your documentation and store it into Meilisearch.
### Contributing
If you want to build a new integration for Meilisearch, you are more than welcome to and we would be happy to help you!
We are proud that some of our libraries were developed and are still maintained by external contributors! ♥️
We recommend to follow [these guidelines](https://github.com/meilisearch/integrations-guides) so that it will be easier to integrate your work.
---
title: Comparison to alternatives — Meilisearch documentation
description: "Deciding on a search engine for your project is an important but difficult task. This article describes the differences between Meilisearch and other search engines."
sidebarDepth: 4
---
## Comparison to alternatives
There are many search engines on the web, both open-source and otherwise. Deciding which search solution is the best fit for your project is very important, but also difficult. In this article, we'll go over the differences between Meilisearch and other search engines:
- In the [comparison table](#comparison-table), we present a general overview of the differences between Meilisearch and other search engines
- In the [approach comparison](#approach-comparison), instead, we focus on how Meilisearch measures up against [ElasticSearch](#meilisearch-vs-elasticsearch) and [Algolia](#meilisearch-vs-algolia), currently two of the biggest solutions available in the market
- Finally, we end this article with [an in-depth analysis of the broader search engine landscape](#a-quick-look-at-the-search-engine-landscape)
Please be advised that many of the search products described below are constantly evolving—just like Meilisearch. These are only our own impressions, and may not reflect recent changes. If something appears inaccurate, please don't hesitate to open an [issue or pull request](https://github.com/meilisearch/documentation).
### Comparison table
#### General overview
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:----:|:----:|:-----:|:----:|
| Source code licensing | [MIT](https://choosealicense.com/licenses/mit/)
(Fully open-source) | Closed-source | [GPL-3](https://choosealicense.com/licenses/gpl-3.0/)
(Fully open-source) | [AGPLv3](https://choosealicense.com/licenses/agpl-3.0/)
(open-source) |
| Built with | Rust
[Check out why we believe in Rust](https://www.abetterinternet.org/docs/memory-safety/). | C++ | C++ | Java |
| Data storage | Disk with Memory Mapping -- Not limited by RAM | Limited by RAM | Limited by RAM | Disk with RAM cache |
#### Features
##### Integrations and SDKs
Note: we are only listing libraries officially supported by the internal teams of each different search engine.
Can't find a client you'd like us to support? [Submit your idea or vote for it](https://roadmap.meilisearch.com/tabs/1-under-consideration) 😇
| SDK | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| REST API | ✅ | ✅ | ✅ | ✅ |
| [JavaScript client](https://github.com/meilisearch/meilisearch-js) | ✅ | ✅ | ✅ | ✅ |
| [PHP client](https://github.com/meilisearch/meilisearch-php) | ✅ | ✅ | ✅ | ✅ |
| [Python client](https://github.com/meilisearch/meilisearch-python) | ✅ | ✅ | ✅ | ✅ |
| [Ruby client](https://github.com/meilisearch/meilisearch-ruby) | ✅ | ✅ | ✅ | ✅ |
| [Java client](https://github.com/meilisearch/meilisearch-java) | ✅ | ✅ | ✅ | ✅ |
| [Swift client](https://github.com/meilisearch/meilisearch-swift) | ✅ | ✅ | ✅ | ❌ |
| [.NET client](https://github.com/meilisearch/meilisearch-dotnet) | ✅ | ✅ | ✅ | ✅ |
| [Rust client](https://github.com/meilisearch/meilisearch-rust) | ✅ | ❌ | 🔶
WIP | ✅ |
| [Go client](https://github.com/meilisearch/meilisearch-go) | ✅ | ✅ | ✅ | ✅ |
| [Dart client](https://github.com/meilisearch/meilisearch-dart) | ✅ | ✅ | ✅ | ❌ |
| [Symfony](https://github.com/meilisearch/meilisearch-symfony) | ✅ | ✅ | ✅ | ❌ |
| [Django](https://roadmap.meilisearch.com/c/60-django) | ❌ | ✅ | ❌ | ❌ |
| [Rails](https://github.com/meilisearch/meilisearch-rails) | ✅ | ✅ | 🔶
WIP | ✅ ||
| [Official Laravel Scout Support](https://github.com/laravel/scout) | ✅ | ✅ | ❌
Available as a standalone module | ❌
Available as a standalone module |
| [Instantsearch](https://github.com/meilisearch/meilisearch-js-plugins/tree/main/packages/instant-meilisearch) | ✅ | ✅ | ✅ | ✅ |
| [Autocomplete](https://github.com/meilisearch/meilisearch-js-plugins/tree/main/packages/autocomplete-client) | ✅ | ✅ | ✅ | ✅ |
| [Docsearch](https://github.com/meilisearch/docs-scraper) | ✅ | ✅ | ✅ | ❌ |
| [Strapi](https://github.com/meilisearch/strapi-plugin-meilisearch) | ✅ | ✅ | ❌ | ❌ |
| [Gatsby](https://github.com/meilisearch/gatsby-plugin-meilisearch) | ✅ | ✅ | ✅ | ❌ |
| [Firebase](https://github.com/meilisearch/firestore-meilisearch) | ✅ | ✅ | ✅ | ❌ |
##### Configuration
###### Document schema
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| Schemaless | ✅ | ✅ | 🔶
`id` field is required and must be a string | ✅ |
| Nested field support | ✅ | ✅ | ✅ | ✅ |
| Nested document querying | ❌ | ❌ | ❌ | ✅ |
| Automatic document ID detection | ✅ | ❌ | ❌ | ❌ |
| Native document formats | `JSON`, `NDJSON`, `CSV` | `JSON` | `NDJSON` | `JSON`, `NDJSON`, `CSV` |
| Compression Support | Gzip, Deflate, and Brotli | Gzip | ❌
Reads payload as JSON which can lead to document corruption | Gzip |
###### Relevancy
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| Typo tolerant | ✅ | ✅ | ✅ | 🔶
Needs to be specified by fuzzy queries |
| Orderable ranking rules | ✅ | ✅ | 🔶
Field weight can be changed, but ranking rules order cannot be changed. | ❌|
| Custom ranking rules | ✅ | ✅ | ✅ | 🔶
Function score query |
| Query field weights | ✅ | ✅ | ✅ | ✅ |
| Synonyms | ✅ | ✅ | ✅ | ✅ |
| Stop words | ✅ | ✅ | ❌ | ✅ |
| Automatic language detection | ✅ | ✅ | ❌ | ❌ |
| All language supports | ✅ | ✅ | ✅ | ✅ |
| Ranking Score Details | ✅ | ✅ | ❌ | ✅ |
###### Security
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| API Key Management | ✅ | ✅ | ✅ | ✅ |
| Tenant tokens & multi-tenant indexes | ✅
[Multitenancy support](/learn/security/multitenancy_tenant_tokens) | ✅ | ✅ | ✅
Role-based |
###### Search
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| Placeholder search | ✅ | ✅ | ✅ | ✅ |
| Multi-index search | ✅ | ✅ | ✅ | ✅ |
| Federated search | ✅ | ❌ | ❌ | ✅ |
| Exact phrase search | ✅ | ✅ | ✅ | ✅ |
| Geo search | ✅ | ✅ | ✅ | ✅ |
| Sort by | ✅ | 🔶
Limited to one `sort_by` rule per index. Indexes may have to be duplicated for each sort field and sort order | ✅
Up to 3 sort fields per search query | ✅ |
| Filtering | ✅
Support complex filter queries with an SQL-like syntax. | 🔶
Does not support `OR` operation across multiple fields | ✅ | ✅ |
| Faceted search | ✅ | ✅ | ✅
Faceted fields must be searchable
Faceting can take several seconds when >10 million facet values must be returned | ✅ |
| Distinct attributes
De-duplicate documents by a field value
| ✅ | ✅ | ✅ | ✅ |
| Grouping
Bucket documents by field values
| ❌ | ✅ | ✅ | ✅ |
###### AI-powered search
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| Semantic Search | ✅ | 🔶
Under Premium plan | ✅ | ✅ |
| Hybrid Search | ✅ | 🔶
Under Premium plan | ✅ | ✅ |
| Embedding Generation | ✅
OpenAI
HuggingFace
REST embedders
| Undisclosed |
OpenAI
GCP Vertex AI | ✅
ELSER
E5
Cohere
OpenAI
Azure
Google AI Studio
Hugging Face
|
| Prompt Templates | ✅ | Undisclosed | ❌ | ❌ |
| Vector Store | ✅ | Undisclosed | ✅ | ✅ |
| Langchain Integration | ✅ | ❌ | ✅ | ✅ |
| GPU support | ✅
CUDA | Undisclosed | ✅
CUDA | ❌ |
###### Visualize
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| [Mini Dashboard](https://github.com/meilisearch/mini-dashboard) | ✅ | 🔶
Cloud product | 🔶
Cloud product | ✅ |
| Search Analytics | ✅
[Cloud product](https://www.meilisearch.com/cloud) | ✅
Cloud Product | ❌ | ✅
Cloud Product |
| Monitoring Dashboard | ✅
[Cloud product](https://www.meilisearch.com/docs/learn/analytics/monitoring) | ✅
Cloud Product | ✅
Cloud Product | ✅
Cloud Product |
##### Deployment
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| Self-hosted | ✅ | ❌ | ✅ | ✅ |
| Platform Support | ARM
x86
x64 | n/a | 🔶 ARM (requires Docker on macOS)
x86
x64 | ARM
x86
x64 |
| Official 1-click deploy | ✅
[DigitalOcean](https://marketplace.digitalocean.com/apps/meilisearch)
[Platform.sh](https://console.platform.sh/projects/create-project?template=https://raw.githubusercontent.com/platformsh/template-builder/master/templates/meilisearch/.platform.template.yaml)
[Azure](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fcmaneu%2Fmeilisearch-on-azure%2Fmain%2Fmain.json)
[Railway](https://railway.app/new/template/TXxa09?referralCode=YltNo3)
[Koyeb](https://app.koyeb.com/deploy?type=docker&image=getmeili/meilisearch&name=meilisearch-on-koyeb&ports=7700;http;/&env%5BMEILI_MASTER_KEY%5D=REPLACE_ME_WITH_A_STRONG_KEY) | ❌ | 🔶
Only for the cloud-hosted solution | ❌ |
| Official cloud-hosted solution | [Meilisearch Cloud](https://www.meilisearch.com/cloud?utm_campaign=oss&utm_source=docs&utm_medium=comparison-table) | ✅ | ✅ | ✅ |
| High availability | Available with [Meilisearch Cloud](https://www.meilisearch.com/cloud?utm_campaign=oss&utm_source=docs&utm_medium=comparison-table) | ✅ | ✅ | ✅ |
| Run-time dependencies | None | N/A | None | None |
| Backward compatibility | ✅ | N/A | ✅ | ✅ |
| Upgrade path | Documents are automatically reindexed on upgrade | N/A | Documents are automatically reindexed on upgrade | Documents are automatically reindexed on upgrade, up to 1 major version |
#### Limits
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| Maximum number of indexes | No limitation | 1000, increasing limit possible by contacting support | No limitation | No limitation |
| Maximum index size | 80TiB | 128GB | Constrained by RAM | No limitation |
| Maximum document size | No limitation | 100KB, configurable | No limitation | 100KB default, configurable |
#### Community
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| GitHub stars of the main project | 42K | N/A | 17K | 66K |
| Number of contributors on the main project | 179 | N/A | 38 | 1,900 |
| Public Discord/Slack community size | 2,100 | N/A | 2,000 | 16K |
#### Support
| | Meilisearch | Algolia | Typesense | Elasticsearch |
|---|:---:|:----:|:---:|:---:|
| Status page | ✅ | ✅ | ✅ | ✅ |
| Free support channels | Instant messaging / chatbox (2-3h delay),
emails,
public Discord community,
GitHub issues & discussions | Instant messaging / chatbox,
public community forum | Instant messaging/chatbox (24h-48h delay),
public Slack community,
GitHub issues. | Public Slack community,
public community forum,
GitHub issues |
| Paid support channels | Slack Channel, emails, personalized support — whatever you need, we’ll be there! | Emails | Emails,
phone,
private Slack | Web support,
emails,
phone |
### Approach comparison
#### Meilisearch vs Elasticsearch
Elasticsearch is designed as a backend search engine. Although it is not suited for this purpose, it is commonly used to build search bars for end-users.
Elasticsearch can handle searching through massive amounts of data and performing text analysis. In order to make it effective for end-user searching, you need to spend time understanding more about how Elasticsearch works internally to be able to customize and tailor it to fit your needs.
Unlike Elasticsearch, which is a general search engine designed for large amounts of log data (for example, back-facing search), Meilisearch is intended to deliver performant instant-search experiences aimed at end-users (for example, front-facing search).
Elasticsearch can sometimes be too slow if you want to provide a full instant search experience. Most of the time, it is significantly slower in returning search results compared to Meilisearch.
Meilisearch is a perfect choice if you need a simple and easy tool to deploy a typo-tolerant search bar. It provides prefix searching capability, makes search intuitive for users, and returns results instantly with excellent relevance out of the box.
For a more detailed analysis of how it compares with Meilisearch, refer to our [blog post on Elasticsearch](https://blog.meilisearch.com/meilisearch-vs-elasticsearch/?utm_campaign=oss&utm_source=docs&utm_medium=comparison).
#### Meilisearch vs Algolia
Meilisearch was inspired by Algolia's product and the algorithms behind it. We indeed studied most of the algorithms and data structures described in their blog posts in order to implement our own. Meilisearch is thus a new search engine based on the work of Algolia and recent research papers.
Meilisearch provides similar features and reaches the same level of relevance just as quickly as its competitor.
If you are a current Algolia user considering a switch to Meilisearch, you may be interested in our [migration guide](/learn/update_and_migration/algolia_migration).
##### Key similarities
Some of the most significant similarities between Algolia and Meilisearch are:
- [Features](/learn/getting_started/what_is_meilisearch#features) such as search-as-you-type, typo tolerance, faceting, etc.
- Fast results targeting an instant search experience (answers < 50 milliseconds)
- Schemaless indexing
- Support for all JSON data types
- Asynchronous API
- Similar query response
##### Key differences
Contrary to Algolia, Meilisearch is open-source and can be forked or self-hosted.
Additionally, Meilisearch is written in Rust, a modern systems-level programming language. Rust provides speed, portability, and flexibility, which makes the deployment of our search engine inside virtual machines, containers, or even [Lambda@Edge](https://aws.amazon.com/lambda/edge/) a seamless operation.
##### Pricing
The [pricing model for Algolia](https://www.algolia.com/pricing/) is based on the number of records kept and the number of API operations performed. It can be prohibitively expensive for small and medium-sized businesses.
Meilisearch is an **open-source** search engine available via [Meilisearch Cloud](https://meilisearch.com/cloud?utm_campaign=oss&utm_source=docs&utm_medium=comparison) or self-hosted. Unlike Algolia, [Meilisearch pricing](https://www.meilisearch.com/pricing?utm_campaign=oss&utm_source=docs&utm_medium=comparison) is based on the number of documents stored and the number of search operations performed. However, Meilisearch offers a more generous free tier that allows more documents to be stored as well as fairer pricing for search usage. Meilisearch also offers a Pro tier for larger use cases to allow for more predictable pricing.
### A quick look at the search engine landscape
#### Open source
##### Lucene
Apache Lucene is a free and open-source search library used for indexing and searching full-text documents. It was created in 1999 by Doug Cutting, who had previously written search engines at Xerox's Palo Alto Research Center (PARC) and Apple. Written in Java, Lucene was developed to build web search applications such as Google and DuckDuckGo, the last of which still uses Lucene for certain types of searches.
Lucene has since been divided into several projects:
- **Lucene itself**: the full-text search library.
- **Solr**: an enterprise search server with a powerful REST API.
- **Nutch**: an extensible and scalable web crawler relying on Apache Hadoop.
Since Lucene is the technology behind many open source or closed source search engines, it is considered as the reference search library.
##### Sonic
Sonic is a lightweight and schema-less search index server written in Rust. Sonic cannot be considered as an out-of-the-box solution, and compared to Meilisearch, it does not ensure relevancy ranking. Instead of storing documents, it comprises an inverted index with a Levenshtein automaton. This means any application querying Sonic has to retrieve the search results from an external database using the returned IDs and then apply some relevancy ranking.
Its ability to run on a few MBs of RAM makes it a minimalist and resource-efficient alternative to database tools that can be too heavyweight to scale.
##### Typesense
Like Meilisearch, Typesense is a lightweight open-source search engine optimized for speed. To better understand how it compares with Meilisearch, refer to our [blog post on Typesense](https://blog.meilisearch.com/meilisearch-vs-typesense/?utm_campaign=oss&utm_source=docs&utm_medium=comparison).
##### Lucene derivatives
##### Lucene-Solr
Solr is a subproject of Apache Lucene, created in 2004 by Yonik Seeley, and is today one of the most widely used search engines available worldwide. Solr is a search platform, written in Java, and built on top of Lucene. In other words, Solr is an HTTP wrapper around Lucene's Java API, meaning you can leverage all the features of Lucene by using it. In addition, Solr server is combined with Solr Cloud, providing distributed indexing and searching capabilities, thus ensuring high availability and scalability. Data is shared but also automatically replicated.
Furthermore, Solr is not only a search engine; it is often used as a document-structured NoSQL database. Documents are stored in collections, which can be comparable to tables in a relational database.
Due to its extensible plugin architecture and customizable features, Solr is a search engine with an endless number of use cases even though, since it can index and search documents and email attachments, it is specifically popular for enterprise search.
##### Bleve & Tantivy
Bleve and Tantivy are search engine projects, respectively written in Golang and Rust, inspired by Apache Lucene and its algorithms (for example, tf-idf, short for term frequency-inverse document frequency). Such as Lucene, both are libraries to be used for any search project; however they are not ready-to-use APIs.
#### Source available
##### Elasticsearch
Elasticsearch is a search engine based on the Lucene library and is most popular for full-text search. It provides a REST API accessed by JSON over HTTP. One of its key options, called index sharding, gives you the ability to divide indexes into physical spaces in order to increase performance and ensure high availability. Both Lucene and Elasticsearch have been designed for processing high-volume data streams, analyzing logs, and running complex queries. You can perform operations and analysis (for example, calculate the average age of all users named "Thomas") on documents that match a specified query.
Today, Lucene and Elasticsearch are dominant players in the search engine landscape. They both are solid solutions for a lot of different use cases in search, and also for building your own recommendation engine. They are good general products, but they require to be configured properly to get similar results to those of Meilisearch or Algolia.
#### Closed source
##### Algolia
Algolia is a company providing a search engine on a SaaS model. Its software is closed source. In its early stages, Algolia offered mobile search engines that could be embedded in apps, facing the challenge of implementing the search algorithms from scratch. From the very beginning, the decision was made to build a search engine directly dedicated to the end-users, specifically, implementing search within mobile apps or websites.
Algolia successfully demonstrated over the past few years how critical tolerating typos was in order to improve the users' experience, and in the same way, its impact on reducing bounce rate and increasing conversion.
Apart from Algolia, a wide choice of SaaS products are available on the Search Engine Market. Most of them use Elasticsearch and fine-tune its settings in order to have a custom and personalized solution.
##### Swiftype
Swiftype is a search service provider specialized in website search and analytics. Swiftype was founded in 2012 by Matt Riley and Quin Hoxie, and is now owned by Elastic since November 2017. It is an end-to-end solution built on top of Elasticsearch, meaning it has the ability to leverage the Elastic Stack.
##### Doofinder
Doofinder is a paid on-site search service that is developed to integrate into any website with very little configuration. Doofinder is used by online stores to increase their sales, aiming to facilitate the purchase process.
### Conclusions
Each Search solution fits best with the constraints of a particular use case. Since each type of search engine offers a unique set of features, it wouldn't be easy nor relevant to compare their performance. For instance, it wouldn't be fair to make a comparison of speed between Elasticsearch and Algolia over a product-based database. The same goes for a very large full text-based database.
We cannot, therefore, compare ourselves with Lucene-based or other search engines targeted to specific tasks.
In the particular use case we cover, the most similar solution to Meilisearch is Algolia.
While Algolia offers the most advanced and powerful search features, this efficiency comes with an expensive pricing. Moreover, their service is marketed to big companies.
Meilisearch is dedicated to all types of developers. Our goal is to deliver a developer-friendly tool, easy to install, and to deploy. Because providing an out-of-the-box awesome search experience for the end-users matters to us, we want to give everyone access to the best search experiences out there with minimum effort and without requiring any financial resources.
Usually, when a developer is looking for a search tool to integrate into their application, they will go for ElasticSearch or less effective choices. Even if Elasticsearch is not best suited for this use case, it remains a great source available solution. However, it requires technical know-how to execute advanced features and hence more time to customize it to your business.
We aim to become the default solution for developers.
---
title: Telemetry — Meilisearch documentation
description: Meilisearch collects anonymized data from users in order to improve our product. Consult this page for an exhaustive list of collected data and instructions on how to deactivate telemetry.
sidebarDepth: 2
---
## Telemetry
Meilisearch collects anonymized data from users in order to improve our product. This can be [deactivated at any time](#how-to-disable-data-collection), and any data that has already been collected can be [deleted on request](#how-to-delete-all-collected-data).
### What tools do we use to collect and visualize data?
We use [Segment](https://segment.com/), a platform for data collection and management, to collect usage data. We then feed that data into [Amplitude](https://amplitude.com/), a tool for graphing and highlighting data, so that we can build visualizations according to our needs.
### What kind of data do we collect?
Our data collection is focused on the following categories:
- **System** metrics, such as the technical specs of the device running Meilisearch, the software version, and the OS
- **Performance** metrics, such as the success rate of search requests and the average latency
- **Usage** metrics, aimed at evaluating our newest features. These change with each new version
See below for the [complete list of metrics we currently collect](#exhaustive-list-of-all-collected-data).
**We will never:**
- Identify or track users
- Collect personal information such as IP addresses, email addresses, or website URLs
- Store data from documents added to a Meilisearch instance
### Why collect telemetry data?
We collect telemetry data for only two reasons: so that we can improve our product, and so that we can continue working on this project full-time.
In order to create a better product, we need reliable quantitative information. The data we collect helps us fix bugs, evaluate the success of features, and better understand our users' needs.
We also need to prove that people are actually using Meilisearch. Usage metrics help us justify our existence to investors so that we can keep this project alive.
### Why should you trust us?
**Don't trust us—hold us accountable.** We feel that it is understandable, and in fact wise, to be distrustful of tech companies when it comes to your private data. That is why we attempt to maintain [complete transparency about our data collection](#exhaustive-list-of-all-collected-data), provide an [opt-out](#how-to-disable-data-collection), and enable users to [request the deletion of all their collected data](#how-to-delete-all-collected-data) at any time. In the absence of global data protection laws, we believe that this is the only ethical way to approach data collection.
No company is perfect. If you ever feel that we are being anything less than 100% transparent or collecting data that is infringing on your personal privacy, please let us know by emailing our dedicated account: [privacy@meilisearch.com](mailto:privacy@meilisearch.com). Similarly, if you discover a data rights initiative or data protection tool that you think is relevant to us, please share it. We are passionate about this subject and take it very seriously.
### How to disable data collection
Data collection can be disabled at any time by setting a command-line option or environment variable, then restarting the Meilisearch instance.
```bash
meilisearch --no-analytics
```
```bash
export MEILI_NO_ANALYTICS=true
meilisearch
```
```bash
## The following procedure should work for all cloud providers,
## including DigitalOcean, Google Cloud Platform, and Amazon Web Services.
## First, open /etc/systemd/system/meilisearch.service with a text editor:
nano /etc/systemd/system/meilisearch.service
## Then add --no-analytics at the end of the command in ExecStart
## Don't forget to save and quit!
## Finally, run the following two commands:
systemctl daemon-reload
systemctl restart meilisearch
```
For more information about configuring Meilisearch, read our [configuration reference](/learn/self_hosted/configure_meilisearch_at_launch).
### How to delete all collected data
We, the Meilisearch team, provide an email address so that users can request the complete removal of their data from all of our tools.
To do so, send an email to [privacy@meilisearch.com](mailto:privacy@meilisearch.com) containing the unique identifier generated for your Meilisearch installation (`Instance UID` when launching Meilisearch). Any questions regarding the management of the data we collect can also be sent to this email address.
### Exhaustive list of all collected data
Whenever an event is triggered that collects some piece of data, Meilisearch does not send it immediately. Instead, it bundles it with other data in a batch of up to `500kb`. Batches are sent either every hour, or after reaching `500kb`—whichever occurs first. This is done in order to improve performance and reduce network traffic.
This list is liable to change with every new version of Meilisearch. It's not because we're trying to be sneaky! It's because when we add new features we need to collect additional data points to see how they perform.
| Metric name | Description | Example
|----------------------------------------------------|---------------------------------------------------------------------------------------------|--------------------
| `context.app.version` | Meilisearch version number | 1.3.0
| `infos.env` | Value of `--env`/`MEILI_ENV` | production
| `infos.db_path` | `true` if `--db-path`/`MEILI_DB_PATH` is specified | true
| `infos.import_dump` | `true` if `--import-dump` is specified | true
| `infos.dump_dir` | `true` if `--dump-dir`/`MEILI_DUMP_DIR` is specified | true
| `infos.ignore_missing_dump` | `true` if `--ignore-missing-dump` is activated | true
| `infos.ignore_dump_if_db_exists` | `true` if `--ignore-dump-if-db-exists` is activated | true
| `infos.import_snapshot` | `true` if `--import-snapshot` is specified | true
| `infos.schedule_snapshot` | Value of `--schedule_snapshot`/`MEILI_SCHEDULE_SNAPSHOT` if set, otherwise `None` | 86400
| `infos.snapshot_dir` | `true` if `--snapshot-dir`/`MEILI_SNAPSHOT_DIR` is specified | true
| `infos.ignore_missing_snapshot` | `true` if `--ignore-missing-snapshot` is activated | true
| `infos.ignore_snapshot_if_db_exists` | `true` if `--ignore-snapshot-if-db-exists` is activated | true
| `infos.http_addr` | `true` if `--http-addr`/`MEILI_HTTP_ADDR` is specified | true
| `infos.http_payload_size_limit` | Value of `--http-payload-size-limit`/`MEILI_HTTP_PAYLOAD_SIZE_LIMIT` in bytes | 336042103
| `infos.log_level` | Value of `--log-level`/`MEILI_LOG_LEVEL` | debug
| `infos.max_indexing_memory` | Value of `--max-indexing-memory`/`MEILI_MAX_INDEXING_MEMORY` in bytes | 336042103
| `infos.max_indexing_threads` | Value of `--max-indexing-threads`/`MEILI_MAX_INDEXING_THREADS` in integer | 4
| `infos.log_level` | Value of `--log-level`/`MEILI_LOG_LEVEL` | debug
| `infos.ssl_auth_path` | `true` if `--ssl-auth-path`/`MEILI_SSL_AUTH_PATH` is specified | false
| `infos.ssl_cert_path` | `true` if `--ssl-cert-path`/`MEILI_SSL_CERT_PATH` is specified | false
| `infos.ssl_key_path` | `true` if `--ssl-key-path`/`MEILI_SSL_KEY_PATH` is specified | false
| `infos.ssl_ocsp_path` | `true` if `--ssl-ocsp-path`/`MEILI_SSL_OCSP_PATH` is specified | false
| `infos.ssl_require_auth` | Value of `--ssl-require-auth`/`MEILI_SSL_REQUIRE_AUTH` as a boolean | false
| `infos.ssl_resumption` | `true` if `--ssl-resumption`/`MEILI_SSL_RESUMPTION` is specified | false
| `infos.ssl_tickets` | `true` if `--ssl-tickets`/`MEILI_SSL_TICKETS` is specified | false
| `system.distribution` | Distribution on which Meilisearch is launched | Arch Linux
| `system.kernel_version` | Kernel version on which Meilisearch is launched | 5.14.10
| `system.cores` | Number of cores | 24
| `system.ram_size` | Total RAM capacity. Expressed in `KB` | 16777216
| `system.disk_size` | Total capacity of the largest disk. Expressed in `Bytes` | 1048576000
| `system.server_provider` | Value of `MEILI_SERVER_PROVIDER` environment variable | AWS
| `stats.database_size` | Database size. Expressed in `Bytes` | 2621440
| `stats.indexes_number` | Number of indexes | 2
| `start_since_days` | Number of days since instance was launched | 365
| `user_agent` | User-agent header encountered during API calls | ["Meilisearch Ruby (2.1)", "Ruby (3.0)"]
| `requests.99th_response_time` | Highest latency from among the fastest 99% of successful search requests | 57ms
| `requests.total_succeeded` | Total number of successful requests | 3456
| `requests.total_failed` | Total number of failed requests | 24
| `requests.total_received` | Total number of received search requests | 3480
| `requests.total_degraded` | Total number of searches canceled after reaching search time cut-off | 100
| `requests.total_used_negative_operator` | Count searches using either a negative word or a negative phrase operator | 173
| `sort.with_geoPoint` | `true` if the sort rule `_geoPoint` is specified | true
| `sort.avg_criteria_number` | Average number of sort criteria among all search requests containing the `sort` parameter | 2
| `filter.with_geoBoundingBox` | `true` if the filter rule `_geoBoundingBox` is specified | false
| `filter.with_geoRadius` | `true` if the filter rule `_geoRadius` is specified | false
| `filter.most_used_syntax` | Most used filter syntax among all search requests containing the `filter` parameter | string
| `q.max_terms_number` | Highest number of terms given for the `q` parameter | 5
| `pagination.max_limit` | Highest value given for the `limit` parameter | 60
| `pagination.max_offset` | Highest value given for the `offset` parameter | 1000
| `formatting.max_attributes_to_retrieve` | Maximum number of attributes to retrieve | 100
| `formatting.max_attributes_to_highlight` | Maximum number of attributes to highlight | 100
| `formatting.highlight_pre_tag` | `true` if `highlightPreTag` is specified | false
| `formatting.highlight_post_tag` | `true` if `highlightPostTag` is specified | false
| `formatting.max_attributes_to_crop` | Maximum number of attributes to crop | 100
| `formatting.crop_length` | `true` if `cropLength` is specified | false
| `formatting.crop_marker` | `true` if `cropMarker` is specified | false
| `formatting.show_matches_position` | `true` if `showMatchesPosition` is used in this batch | false
| `facets.avg_facets_number` | Average number of facets | 10
| `primary_key` | Name of primary key when explicitly set. Otherwise `null` | id
| `payload_type` | All values encountered in the `Content-Type` header, including invalid ones | ["application/json", "text/plain", "application/x-ndjson"]
| `index_creation` | `true` if a document addition or update request triggered index creation | true
| `ranking_rules.words_position` | Position of the `words` ranking rule if any, otherwise `null` | 1
| `ranking_rules.typo_position` | Position of the `typo` ranking rule if any, otherwise `null` | 2
| `ranking_rules.proximity_position` | Position of the `proximity` ranking rule if any, otherwise `null` | 3
| `ranking_rules.attribute_position` | Position of the `attribute` ranking rule if any, otherwise `null` | 4
| `ranking_rules.sort_position` | Position of the `sort` ranking rule | 5
| `ranking_rules.exactness_position` | Position of the `exactness` ranking rule if any, otherwise `null` | 6
| `ranking_rules.values` | A string representing the ranking rules without the custom asc-desc rules | "words, typo, attribute, sort, exactness"
| `sortable_attributes.total` | Number of sortable attributes | 3
| `sortable_attributes.has_geo` | `true` if `_geo` is set as a sortable attribute | true
| `filterable_attributes.total` | Number of filterable attributes | 3
| `filterable_attributes.has_geo` | `true` if `_geo` is set as a filterable attribute | false
| `filterable_attributes.has_patterns` | `true` if `filterableAttributes` uses `attributePatterns` | true
| `searchable_attributes.total` | Number of searchable attributes | 4
| `searchable_attributes.with_wildcard` | `true` if `*` is specified as a searchable attribute | false
| `per_task_uid` | `true` if a `uids` is used to fetch a particular task resource | true
| `filtered_by_uid` | `true` if tasks are filtered by the `uids` query parameter | false
| `filtered_by_index_uid` | `true` if tasks are filtered by the `indexUids` query parameter | false
| `filtered_by_type` | `true` if tasks are filtered by the `types` query parameter | false
| `filtered_by_status` | `true` if tasks are filtered by the `statuses` query parameter | false
| `filtered_by_canceled_by` | `true` if tasks are filtered by the `canceledBy` query parameter | false
| `filtered_by_before_enqueued_at` | `true` if tasks are filtered by the `beforeEnqueuedAt` query parameter | false
| `filtered_by_after_enqueued_at` | `true` if tasks are filtered by the `afterEnqueuedAt` query parameter | false
| `filtered_by_before_started_at` | `true` if tasks are filtered by the `beforeStartedAt` query parameter | false
| `filtered_by_after_started_at` | `true` if tasks are filtered by the `afterStartedAt` query parameter | false
| `filtered_by_before_finished_at` | `true` if tasks are filtered by the `beforeFinishedAt` query parameter | false
| `filtered_by_after_finished_at` | `true` if tasks are filtered by the `afterFinishedAt` query parameter | false
| `typo_tolerance.enabled` | `true` if typo tolerance is enabled | true
| `typo_tolerance.disable_on_attributes` | `true` if at least one value is defined for `disableOnAttributes` | false
| `typo_tolerance.disable_on_words` | `true` if at least one value is defined for `disableOnWords` | false
| `typo_tolerance.min_word_size_for_typos.one_typo` | The defined value for the `minWordSizeForTypos.oneTypo` parameter | 5
| `typo_tolerance.min_word_size_for_typos.two_typos` | The defined value for the `minWordSizeForTypos.twoTypos` parameter | 9
| `pagination.max_total_hits` | The defined value for the `pagination.maxTotalHits` property | 1000
| `faceting.max_values_per_facet` | The defined value for the `faceting.maxValuesPerFacet` property | 100
| `distinct_attribute.set` | `true` if a field name is specified | false
| `distinct` | `true` if a distinct was specified in an aggregated list of requests | true
| `proximity_precision.set` | `true` if the setting has been manually set. | false
| `proximity_precision.value` | `byWord` or `byAttribute`. | byWord
| `facet_search.set` | `facetSearch` has been changed by the user | true
| `facet_search.value` | `facetSearch` value set by the user | true
| `prefix_search.set` | `prefixSearch` has been changed by the user | true
| `prefix_search.value` | `prefixSearch` value set by the user | indexingTime
| `displayed_attributes.total` | Number of displayed attributes | 3
| `displayed_attributes.with_wildcard` | `true` if `*` is specified as a displayed attribute | false
| `stop_words.total` | Number of stop words | 3
| `separator_tokens.total` | Number of separator tokens | 3
| `non_separator_tokens.total` | Number of non-separator tokens | 3
| `dictionary.total` | Number of words in the dictionary | 3
| `synonyms.total` | Number of synonyms | 3
| `per_index_uid` | `true` if the `uid` is used to fetch an index stat resource | false
| `searches.avg_search_count` | The average number of search queries received per call for the aggregated event | 4.2
| `searches.total_search_count` | The total number of search queries received for the aggregated event | 16023
| `indexes.avg_distinct_index_count` | The average number of queried indexes received per call for the aggregated event | 1.2
| `indexes.total_distinct_index_count` | The total number of distinct index queries for the aggregated event | 6023
| `indexes.total_single_index` | The total number of calls when only one index is queried | 2007
| `matching_strategy.most_used_strategy` | Most used word matching strategy | last
| `infos.with_configuration_file` | `true` if the instance is launched with a configuration file | false
| `infos.experimental_composite_embedders` | `true` if the `compositeEmbedders` feature is set to `true` for this instance | false
| `infos.experimental_contains_filter` | `true` if the `containsFilter` experimental feature is enabled | false
| `infos.experimental_edit_documents_by_function` | `true` if the `editDocumentsByFunction` experimental feature is enabled | false
| `infos.experimental_enable_metrics` | `true` if `--experimental-enable-metrics` is specified at launch | false
| `infos.experimental_embedding_cache_entries` | Size of configured embedding cache | 100
| `infos.experimental_replication_parameters` | `true` if `--experimental-replication-parameters` is specified at launch | false
| `infos.experimental_reduce_indexing_memory_usage` | `true` if `--experimental-reduce-indexing-memory-usage` is specified at launch | false
| `infos.experimental_logs_mode` | `human` or `json` depending on the value specified | human
| `infos.experimental_enable_logs_route` | `true` if `--experimental-enable-logs-route` is specified at launch | false
| `infos.gpu_enabled` | `true` if Meilisearch was compiled with CUDA support | false
| `swap_operation_number` | Number of swap operations | 2
| `pagination.most_used_navigation` | Most used search results navigation | estimated
| `per_document_id` | `true` if the `DELETE /indexes/:indexUid/documents/:documentUid` endpoint was used | false
| `per_filter` | `true` if `POST /indexes/:indexUid/documents/fetch`, `GET /indexes/:indexUid/documents/`, or `POST /indexes/:indexUid/documents/delete` endpoints were used | false
| `clear_all` | `true` if `DELETE /indexes/:indexUid/documents` endpoint was used | false
| `per_batch` | `true` if the `POST /indexes/:indexUid/documents/delete-batch` endpoint was used | false
| `facets.total_distinct_facet_count` | Total number of distinct facets queried for the aggregated event | false
| `facets.additional_search_parameters_provided` | `true` if additional search parameters were provided for the aggregated event | false
| `faceting.sort_facet_values_by_star_count` | `true` if all fields are set to be sorted by count | false
| `faceting.sort_facet_values_by_total` | The number of different values that were set | 10
| `scoring.show_ranking_score` | `true` if `showRankingScore` used in the aggregated event | true
| `scoring.show_ranking_score_details` | `true` if `showRankingScoreDetails` was used in the aggregated event | true
| `scoring.ranking_score_threshold` | `true` if rankingScoreThreshold was specified in an aggregated list of requests | true
| `attributes_to_search_on.total_number_of_uses` | Total number of queries where `attributesToSearchOn` is set | 5
| `vector.max_vector_size` | Highest number of dimensions given for the `vector` parameter in this batch | 1536
| `vector.retrieve_vectors` | `true` if the retrieve_vectors parameter has been used in this batch. | false
| `hybrid.enabled` | `true` if hybrid search been used in the aggregated event | true
| `hybrid.semantic_ratio` | `true` if semanticRatio was used in this batch, otherwise false | false
| `embedders.total` | Numbers of defined embedders | 2
| `embedders.sources` | An array representing the different provided sources | ["huggingFace", "userProvided"]
| `embedders.document_template_used` | A boolean indicating if one of the provided embedders has a custom template defined | true
| `embedders.document_template_max_bytes` | A value indicating the largest value for document TemplateMaxBytes across all embedder | 400
| `embedders.binary_quantization_used` | `true` if the user updated the binary quantized field of the embedded settings | false
| `infos.task_queue_webhook` | `true` if the instance is launched with a task queue webhook | false
| `infos.experimental_search_queue_size` | Size of the search queue | 750
| `infos.experimental_dumpless_upgrade` | `true` if instance is launched with the parameter | true
| `locales` | List of locales used with `/search` and `/settings` routes | ["fra", "eng"]
| `federation.use_federation` | `true` when at least one multi-search request contains a top-level federation object | false
| `network_has_self` | `true` if the network object has a non-null self field | true
| `network_size` | Number of declared remotes | 0
| `network` | `true` when the network experimental feature is enabled | true
| `experimental_network` | `true` when the network experimental feature is enabled | true
| `remotes.total_distinct_remote_count` | Sum of the number of distinct remotes appearing in each search request of the aggregate | 48
| `remotes.avg_distinct_remote_count` | Average number of distinct remotes appearing in a search request of the aggregate | 2.33
---
title: Language — Meilisearch documentation
description: Meilisearch is compatible with datasets in any language. Additionally, it features optimized support for languages using whitespace to separate words, Chinese, Hebrew, Japanese, Korean, and Thai.
---
## Language
Meilisearch is multilingual, featuring optimized support for:
- Any language that uses whitespace to separate words
- Chinese
- Hebrew
- Japanese
- Khmer
- Korean
- Swedish
- Thai
We aim to provide global language support, and your feedback helps us move closer to that goal. If you notice inconsistencies in your search results or the way your documents are processed, please [open an issue in the Meilisearch repository](https://github.com/meilisearch/meilisearch/issues/new/choose).
[Read more about our tokenizer](/learn/indexing/tokenization)
### Improving our language support
While we have employees from all over the world at Meilisearch, we don't speak every language. We rely almost entirely on feedback from external contributors to understand how our engine is performing across different languages.
If you'd like to request optimized support for a language, please upvote the related [discussion in our product repository](https://github.com/meilisearch/product/discussions?discussions_q=label%3Ascope%3Atokenizer+) or [open a new one](https://github.com/meilisearch/product/discussions/new?category=feedback-feature-proposal) if it doesn't exist.
If you'd like to help by developing a tokenizer pipeline yourself: first of all, thank you! We recommend that you take a look at the [tokenizer contribution guide](https://github.com/meilisearch/charabia/blob/main/CONTRIBUTING.md) before making a PR.
### FAQ
#### What do you mean when you say Meilisearch offers _optimized_ support for a language?
Optimized support for a language means Meilisearch has implemented internal processes specifically tailored to parsing that language, leading to more relevant results.
#### My language does not use whitespace to separate words. Can I still use Meilisearch?
Yes, but search results might be less relevant than in one of the fully optimized languages.
#### My language does not use the Roman alphabet. Can I still use Meilisearch?
Yes—our users work with many different alphabets and writing systems, such as Cyrillic, Thai, and Japanese.
#### Does Meilisearch plan to support additional languages in the future?
Yes, we definitely do. The more feedback we get from native speakers, the easier it is for us to understand how to improve performance for those languages. Similarly, the more requests we get to improve support for a specific language, the more likely we are to devote resources to that project.
---
title: Versioning policy — Meilisearch documentation
description: This article describes the system behind Meilisearch's SDK and engine version numbering and compatibility.
---
## Versioning policy
This article describes the system behind Meilisearch's version numbering, compatibility between Meilisearch versions, and how Meilisearch version numbers relate to SDK and documentation versions.
### Engine versioning
Release versions follow the MAJOR.MINOR.PATCH format and adhere to the [Semantic Versioning 2.0.0 convention](https://semver.org/#semantic-versioning-200).
- MAJOR versions contain changes that break compatibility between releases
- MINOR versions introduce new features that are backwards compatible
- PATCH versions only contain high-priority bug fixes and security updates
Prior to Meilisearch v1, MINOR versions also broke compatibility between releases.
#### Release schedule
Meilisearch releases new versions between four and six times a year. This number does not include PATCH releases.
#### Support for previous versions
Meilisearch only maintains the latest engine release. Currently, there are no EOL (End of Life) or LTS (Long-Term Support) policies.
Consult the [engine versioning policy](https://github.com/meilisearch/engine-team/blob/main/resources/versioning-policy.md) for more information.
### SDK versioning
Meilisearch version numbers have no relationship to SDK version numbers. For example, `meilisearch-go` v0.22 introduced compatibility with Meilisearch v0.30. SDKs follow their own release schedules and must address issues beyond compatibility with Meilisearch.
When using an SDK, always consult its repository README, release description, and any dedicated documentation to determine which Meilisearch versions and features it supports.
### Documentation versioning
This Meilisearch documentation website follows the latest Meilisearch version. We do not maintain documentation for past releases.
It is possible to [access previous versions of the Meilisearch documentation website](/learn/update_and_migration/previous_docs_version), but the process and results are less than ideal. Users are strongly encouraged to always use the latest Meilisearch release.
---
title: Contributing to our documentation — Meilisearch documentation
description: The Meilisearch documentation is open-source. Learn how to help make it even better.
sidebarDepth: 3
---
## Contributing to our documentation
This documentation website is hosted in a [public GitHub repository](https://github.com/meilisearch/documentation). It is built with [Next.js](https://nextjs.org), written in [MDX](https://mdxjs.com), and deployed on [Vercel](https://www.vercel.com).
### Our documentation philosophy
Our documentation aims to be:
- **Efficient**: we don't want to waste anyone's time
- **Accessible**: reading the texts here shouldn't require native English or a computer science degree
- **Thorough**: the documentation website should contain all information anyone needs to use Meilisearch
- **Open source**: this is a resource by Meilisearch users, for Meilisearch users
### Documentation repository and local development
The Meilisearch documentation repository only stores the content of the docs website. Because the code that makes up the website lives in another repository, **it is not possible to run a local copy of the documentation**.
#### Handling images and other static assets
When contributing content to the Meilisearch docs, store screenshots, images, GIFs, and videos in the relevant directory under `/assets`.
The build process does not currently support static assets with relative paths. When adding them to a document, make sure the asset URL points to the raw GitHub file address:
```markdown
\!\[Image description\]\(https://raw.githubusercontent.com/meilisearch/documentation/[branch_name]/assets/images/[guide_name]/diagram.png\)
```
### How to contribute?
#### Issues
The maintainers of Meilisearch's documentation use [GitHub Issues](https://github.com/meilisearch/documentation/issues/new) to track tasks. Helpful issues include:
- Notify the docs team about content that is inaccurate, outdated, or confusing
- Requests for new features such as versioning or an embedded console
- Requests for new content such as new guides and tutorials
Before opening an issue or PR, please look through our [open issues](https://github.com/meilisearch/documentation/issues) to see if one already exists for your problem. If yes, please leave a comment letting us know that you're waiting for a fix or willing to work on it yourself. If not, please open a new issue describing the problem and informing us whether you want to work on it or not.
We love issues at Meilisearch, because they help us do our jobs better. Nine times out of ten, the most useful contribution is a simple GitHub issue that points out a problem and proposes a solution.
##### Creating your first issue
To open an issue you need a [GitHub account](https://github.com). Create one if necessary, then follow these steps:
1. Log into your account
2. Go to the [Meilisearch Documentation repository](https://github.com/meilisearch/documentation)
3. Click on "Issues"
4. Use the search bar to check if somebody else has reported the same problem. If they have, upvote with a 👍 and **don't create a new issue**!
5. If no one reported the problem you experienced, click on "New issue"
6. Write a short and descriptive title, then add a longer summary explaining the issue. If you're reporting a bug, make sure to include steps to reproduce the error, as well as your OS and browser version
7. Click on "Submit new issue"
8. A member of our team should [get back to you](#how-we-review-contributions) shortly
9. Enjoy the feeling of a job well done! 🎉
#### Pull requests
You can also improve the documentation by making a [pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests).
Pull requests ("PRs" for short) are requests to integrate changes into a GitHub repository. The simplest way to create a PR on our documentation is using the "Edit this page" link at the bottom left of every page.
Pull requests are particularly good when you want to:
- **Solve an [existing issue](https://github.com/meilisearch/documentation/issues)**
- Fix a small error, such as a typo or broken link
- Create or improve content about something you know very well—for example, a guide on how to integrate Meilisearch with a tool you have mastered
In most cases, it is a good idea to [create an issue](#creating-your-first-issue) before making a PR. This allows you to coordinate with the documentation maintainers and find the best way of addressing the problem you want to solve.
##### Creating your first PR
To create a PR you need a [GitHub account](https://github.com). Create one if necessary, then follow these steps:
1. Go to the documentation page you'd like to edit, scroll down, and click "Edit this page" at the bottom left of the screen. This will take you to GitHub
2. If you're not already signed in, do so now. You may be prompted to create a [fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo)
3. Use GitHub's text editor to update the page
4. Scroll down until you reach a box named "Propose changes"
5. Fill in the first field to give your PR a short and descriptive title—for example, "Fix typo in search API reference"
6. Use the second field to add a more detailed explanation of the changes you're proposing
7. Click the "Propose changes" button to continue. You should see a page that says "Comparing changes"
8. Make sure the base repository is set to `meilisearch/documentation` and the base branch is set to `main`. You can ignore the remaining fields
9. This screen will also show you a "diff", which allows you to see the changes you made compared to what's currently published on the documentation website
10. Click "Create pull request"
11. **Congrats, you made your first PR!** A documentation maintainer will review your pull request shortly. They may ask for changes, so keep an eye on your GitHub notifications
12. If everything looks good, your work will be merged into the `main` branch and become part of the official documentation site. You are now a Meilisearch Contributor! 🚀
### How we review contributions
#### How we review issues
When **reviewing issues**, we consider a few criteria:
1. Is this task a priority for the documentation maintainers?
2. Is the documentation website the best place for this information? Sometimes an idea might work better on our blog than the docs, or it might be more effective to link to an external resource than write and maintain it ourselves
3. If it's a bug report, can we reproduce the error?
If users show interest in an issue by upvoting or reporting similar problems, it is more likely the documentation will dedicate resources to that task.
#### How we review PRs
For **reviewing contributor PRs**, we start by making sure the PR is up to our **quality standard**.
We ask the following questions:
1. Is the information **accurate**?
2. Is it **easy to understand**?
3. Do the code samples run without errors? Do they help users understand what we are explaining?
4. Is the English **clear and concise**? Can a non-native speaker understand it?
5. Is the grammar perfect? Are there any typos?
6. Can we shorten text **without losing any important information**?
7. Do the suggested changes require updating other pages in the documentation website?
8. In the case of new content, is the article in the right place? Should other articles in the documentation link to it?
Nothing makes us happier than a thoughtful and helpful PR. Your PRs often save us time and effort, and they make the documentation **even stronger**.
Our only major requirement for PR contributions is that the author responds to communication requests within a reasonable time frame.
Once you've opened a PR in this repository, one of our team members will stop by shortly to review it. If your PR is approved, nothing further is required from you. However, **if in seven days you have not responded to a request for further changes or more information, we will consider the PR abandoned and close it**.
If this happens to you and you think there has been some mistake, please let us know and we will try to rectify the situation.
### Contributing to Meilisearch
There are many ways to contribute to Meilisearch directly as well, such as:
- Contributing to the [main engine](https://github.com/meilisearch/meilisearch/blob/main/CONTRIBUTING.md)
- Contributing to [our integrations](https://github.com/meilisearch/integration-guides)
- [Creating an integration](https://github.com/meilisearch/integration-guides/blob/main/resources/build-integration.md)
- Creating written or video content (tutorials, blog posts, etc.)
- Voting on our [public roadmap](https://roadmap.meilisearch.com/tabs/5-ideas)
There are also many valuable ways of supporting the above repositories:
- Giving feedback
- Suggesting features
- Creating tests
- Fixing bugs
- Adding content
- Developing features
---
title: Overview — Meilisearch API reference
description: Consult this page for an overview of how to query Meilisearch's API, which types of parameters it supports, and how it structures its responses.
---
## API reference
This reference describes the general behavior of Meilisearch's RESTful API.
If you are new to Meilisearch, check out the [getting started](/learn/self_hosted/getting_started_with_self_hosted_meilisearch).
### OpenAPI
You can get the [Meilisearch OpenAPI specifications here](https://github.com/meilisearch/open-api/blob/main/open-api.json).
### Document conventions
This API documentation uses the following conventions:
- Curly braces (`{}`) in API routes represent path parameters, for example, GET `/indexes/{index_uid}`
- Required fields are marked by an asterisk (`*`)
- Placeholder text is in uppercase characters with underscore delimiters, for example, `MASTER_KEY`
### Authorization
By [providing Meilisearch with a master key at launch](/learn/security/basic_security), you protect your instance from unauthorized requests. The provided master key must be at least 16 bytes. From then on, you must include the `Authorization` header along with a valid API key to access protected routes (all routes except [`/health`](/reference/api/health).
```bash
# replace the MASTER_KEY placeholder with your master key
curl \
-X GET 'MEILISEARCH_URL/keys' \
-H 'Authorization: Bearer MASTER_KEY'
```
```js
const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'masterKey' })
client.getKeys()
```
```py
client = Client('http://localhost:7700', 'masterKey')
client.get_keys()
```
```php
$client = new Client('http://localhost:7700', 'masterKey');
$client->getKeys();
```
```java
Client client = new Client(new Config("http://localhost:7700", "masterKey"));
client.getKeys();
```
```ruby
client = MeiliSearch::Client.new('http://localhost:7700', 'masterKey')
client.keys
```
```go
client := meilisearch.New("http://localhost:7700", meilisearch.WithAPIKey("masterKey"))
client.GetKeys(nil);
```
```csharp
MeilisearchClient client = new MeilisearchClient("http://localhost:7700", "masterKey");
var keys = await client.GetKeysAsync();
```
```rust
let client = Client::new("http://localhost:7700", Some("masterKey")); let keys = client .get_keys() .await .unwrap();
```
```swift
client = try MeiliSearch(host: "http://localhost:7700", apiKey: "masterKey")
client.getKeys { result in
switch result {
case .success(let keys):
print(keys)
case .failure(let error):
print(error)
}
}
```
```dart
var client = MeiliSearchClient('http://localhost:7700', 'masterKey');
await client.getKeys();
```
The [`/keys`](/reference/api/keys) route can only be accessed using the master key. For security reasons, we recommend using regular API keys for all other routes.
v0.24 and below use the `X-MEILI-API-KEY: apiKey` authorization header:
```bash
curl \
-X GET 'http:///version' \
-H 'X-Meili-API-Key: API_KEY'
```
[To learn more about keys and security, refer to our security tutorial.](/learn/security/basic_security)
### Pagination
Meilisearch paginates all GET routes that return multiple resources, for example, GET `/indexes`, GET `/documents`, GET `/keys`, etc. This allows you to work with manageable chunks of data. All these routes return 20 results per page, but you can configure it using the `limit` query parameter. You can move between pages using `offset`.
All paginated responses contain the following fields:
| Name | Type | Description |
| :----------- | :------ | :--------------------------- |
| **`offset`** | Integer | Number of resources skipped |
| **`limit`** | Integer | Number of resources returned |
| **`total`** | Integer | Total number of resources |
#### `/tasks` endpoint
Since the `/tasks` endpoint uses a different type of pagination, the response contains different fields. You can read more about it in the [tasks API reference](/reference/api/tasks#get-tasks).
### Parameters
Parameters are options you can pass to an API endpoint to modify its response. There are three main types of parameters in Meilisearch's API: request body parameters, path parameters, and query parameters.
#### Request body parameters
These parameters are mandatory parts of POST, PUT, and PATCH requests. They accept a wide variety of values and data types depending on the resource you're modifying. You must add these parameters to your request's data payload.
#### Path parameters
These are parameters you pass to the API in the endpoint's path. They are used to identify a resource uniquely. You can have multiple path parameters, for example, `/indexes/{index_uid}/documents/{document_id}`.
If an endpoint does not take any path parameters, this section is not present in that endpoint's documentation.
#### Query parameters
These optional parameters are a sequence of key-value pairs and appear after the question mark (`?`) in the endpoint. You can list multiple query parameters by separating them with an ampersand (`&`). The order of query parameters does not matter. They are mostly used with GET endpoints.
If an endpoint does not take any query parameters, this section is not present in that endpoint's documentation.
### Headers
#### Content type
Any API request with a payload (`--data-binary`) requires a `Content-Type` header. Content type headers indicate the media type of the resource, helping the client process the response body correctly.
Meilisearch currently supports the following formats:
- `Content-Type: application/json` for JSON
- `Content-Type: application/x-ndjson` for NDJSON
- `Content-Type: text/csv` for CSV
Only the [add documents](/reference/api/documents#add-or-replace-documents) and [update documents](/reference/api/documents#add-or-update-documents) endpoints accept NDJSON and CSV. For all others, use `Content-Type: application/json`.
#### Content encoding
The `Content-Encoding` header indicates the media type is compressed by a given algorithm. Compression improves transfer speed and reduces bandwidth consumption by sending and receiving smaller payloads. The `Accept-Encoding` header, instead, indicates the compression algorithm the client understands.
Meilisearch supports the following compression methods:
- `br`: uses the [Brotli](https://en.wikipedia.org/wiki/Brotli) algorithm
- `deflate`: uses the [zlib](https://en.wikipedia.org/wiki/Zlib) structure with the [deflate](https://en.wikipedia.org/wiki/DEFLATE) compression algorithm
- `gzip`: uses the [gzip](https://en.wikipedia.org/wiki/Gzip) algorithm
##### Request compression
The code sample below uses the `Content-Encoding: gzip` header, indicating that the request body is compressed using the `gzip` algorithm:
```
cat ~/movies.json | gzip | curl -X POST 'MEILISEARCH_URL/indexes/movies/documents' --data-binary @- -H 'Content-Type: application/json' -H 'Content-Encoding: gzip'
```
##### Response compression
Meilisearch compresses a response if the request contains the `Accept-Encoding` header. The code sample below uses the `gzip` algorithm:
```
curl -sH 'Accept-encoding: gzip' 'MEILISEARCH_URL/indexes/movies/search' | gzip -
```
### Request body
The request body is data sent to the API. It is used with PUT, POST, and PATCH methods to create or update a resource. You must provide request bodies in JSON.
### Response body
Meilisearch is an **asynchronous API**. This means that in response to most write requests, you will receive a summarized version of the `task` object:
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "indexUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
See more information about [asynchronous operations](/learn/async/asynchronous_operations).
### Data types
The Meilisearch API supports [JSON data types](https://www.w3schools.com/js/js_json_datatypes.asp).
---
title: Indexes — Meilisearch API reference
description: The /indexes route allows you to create, manage, and delete your indexes.
---
## Indexes
The `/indexes` route allows you to create, manage, and delete your indexes.
[Learn more about indexes](/learn/getting_started/indexes).
### Index object
```json
{
"uid": "movies",
"createdAt": "2022-02-10T07:45:15.628261Z",
"updatedAt": "2022-02-21T15:28:43.496574Z",
"primaryKey": "id"
}
```
| Name | Type | Default value | Description |
| :--------------- | :-------------- | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`uid`** | String | N/A | [Unique identifier](/learn/getting_started/indexes#index-uid) of the index. Once created, it cannot be changed |
| **`createdAt`** | String | N/A | Creation date of the index, represented in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. Auto-generated on index creation |
| **`updatedAt`** | String | N/A | Latest date of index update, represented in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. Auto-generated on index creation or update |
| **`primaryKey`** | String / `null` | `null` | [Primary key](/learn/getting_started/primary_key#primary-field) of the index. If not specified, Meilisearch [guesses your primary key](/learn/getting_started/primary_key#meilisearch-guesses-your-primary-key) from the first document you add to the index |
### List all indexes
List all indexes. Results can be paginated by using the `offset` and `limit` query parameters.
#### Query parameters
| Query parameter | Description | Default value |
| :-------------- | :-------------------------- | :------------ |
| **`offset`** | Number of indexes to skip | `0` |
| **`limit`** | Number of indexes to return | `20` |
#### Response
| Name | Type | Description |
| :------------ | :------ | :----------------------------------- |
| **`results`** | Array | An array of [indexes](#index-object) |
| **`offset`** | Integer | Number of indexes skipped |
| **`limit`** | Integer | Number of indexes returned |
| **`total`** | Integer | Total number of indexes |
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes?limit=3'
```
```js
client.getIndexes({ limit: 3 })
```
```py
client.get_indexes({'limit': 3})
```
```php
$client->getIndexes((new IndexesQuery())->setLimit(3));
```
```java
IndexesQuery query = new IndexesQuery().setLimit(3);
client.getIndexes(query);
```
```ruby
client.indexes(limit: 3)
```
```go
client.GetIndexes(&meilisearch.IndexesQuery{
Limit: 3,
})
```
```csharp
await client.GetAllIndexesAsync(new IndexesQuery { Limit = 3 });
```
```rust
let mut indexes = IndexesQuery::new(&client)
.with_limit(3)
.execute()
.await
.unwrap();
```
```swift
client.getIndexes { (result) in
switch result {
case .success(let indexes):
print(indexes)
case .failure(let error):
print(error)
}
}
```
```dart
await client.getIndexes(params: IndexesQuery(limit: 3));
```
##### Response: `200 Ok`
```json
{
"results": [
{
"uid": "books",
"createdAt": "2022-03-08T10:00:27.377346Z",
"updatedAt": "2022-03-08T10:00:27.391209Z",
"primaryKey": "id"
},
{
"uid": "meteorites",
"createdAt": "2022-03-08T10:00:44.518768Z",
"updatedAt": "2022-03-08T10:00:44.582083Z",
"primaryKey": "id"
},
{
"uid": "movies",
"createdAt": "2022-02-10T07:45:15.628261Z",
"updatedAt": "2022-02-21T15:28:43.496574Z",
"primaryKey": "id"
}
],
"offset": 0,
"limit": 3,
"total": 5
}
```
### Get one index
Get information about an index.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies'
```
```js
client.index('movies').getRawInfo()
```
```py
client.get_index('movies')
```
```php
$client->index('movies')->fetchRawInfo();
```
```java
client.getIndex("movies");
```
```ruby
client.fetch_index('movies')
```
```go
client.GetIndex("movies")
```
```csharp
await client.GetIndexAsync("movies");
```
```rust
let movies: Index = client
.get_index("movies")
.await
.unwrap();
```
```swift
client.getIndex("movies") { (result) in
switch result {
case .success(let index):
print(index)
case .failure(let error):
print(error)
}
}
```
```dart
await client.getIndex('movies');
```
##### Response: `200 Ok`
```json
{
"uid": "movies",
"createdAt": "2022-02-10T07:45:15.628261Z",
"updatedAt": "2022-02-21T15:28:43.496574Z",
"primaryKey": "id"
}
```
### Create an index
Create an index.
#### Body
| Name | Type | Default value | Description |
| :--------------- | :-------------- | :------------ | :---------------------------------------------------------------------------------------- |
| **`uid`** * | String | N/A | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
| **`primaryKey`** | String / `null` | `null` | [`Primary key`](/learn/getting_started/primary_key#primary-field) of the requested index |
```json
{
"uid": "movies",
"primaryKey": "id"
}
```
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes' \
-H 'Content-Type: application/json' \
--data-binary '{
"uid": "movies",
"primaryKey": "id"
}'
```
```js
client.createIndex('movies', { primaryKey: 'id' })
```
```py
client.create_index('movies', {'primaryKey': 'id'})
```
```php
$client->createIndex('movies', ['primaryKey' => 'id']);
```
```java
client.createIndex("movies", "id");
```
```ruby
client.create_index('movies', primary_key: 'id')
```
```go
client.CreateIndex(&meilisearch.IndexConfig{
Uid: "movies",
PrimaryKey: "id",
})
```
```csharp
TaskInfo task = await client.CreateIndexAsync("movies", "id");
```
```rust
client.create_index("movies", Some("id"))
.await
.unwrap();
```
```swift
client.createIndex(uid: "movies", primaryKey: "id") { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.createIndex('movies', primaryKey: 'id');
```
##### Response: `202 Accepted`
```json
{
"taskUid": 0,
"indexUid": "movies",
"status": "enqueued",
"type": "indexCreation",
"enqueuedAt": "2021-08-12T10:00:00.000000Z"
}
```
You can use the response's `taskUid` to [track the status of your request](/reference/api/tasks#get-one-task).
### Update an index
Update an index's [primary key](/learn/getting_started/primary_key#primary-key). You can freely update the primary key of an index as long as it contains no documents.
To change the primary key of an index that already contains documents, you must first delete all documents in that index. You may then change the primary key and index your dataset again.
It is not possible to change an index's `uid`.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Body
| Name | Type | Default value | Description |
| :----------------- | :-------------- | :------------ | :---------------------------------------------------------------------------------------- |
| **`primaryKey`** * | String / `null` | N/A | [`Primary key`](/learn/getting_started/primary_key#primary-field) of the requested index |
#### Example
```bash
curl \
-X PATCH 'MEILISEARCH_URL/indexes/movies' \
-H 'Content-Type: application/json' \
--data-binary '{ "primaryKey": "id" }'
```
```js
client.updateIndex('movies', { primaryKey: 'id' })
```
```py
client.index('movies').update(primary_key='id')
```
```php
$client->updateIndex('movies', ['primaryKey' => 'id']);
```
```java
client.updateIndex("movies", "id");
```
```ruby
client.index('movies').update(primary_key: 'movie_id')
```
```go
client.Index("movies").UpdateIndex("id")
```
```csharp
TaskInfo task = await client.UpdateIndexAsync("movies", "id");
```
```rust
let task = IndexUpdater::new("movies", &client)
.with_primary_key("movie_review_id")
.execute()
.await
.unwrap();
```
```swift
client.index("movies").update(primaryKey: "id") { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').update(primaryKey: 'id');
```
##### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "indexUpdate",
"enqueuedAt": "2021-08-12T10:00:00.000000Z"
}
```
You can use the response's `taskUid` to [track the status of your request](/reference/api/tasks#get-one-task).
### Delete an index
Delete an index.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies'
```
```js
client.deleteIndex('movies')
```
```py
client.delete_index('movies')
// OR
client.index('movies').delete()
```
```php
$client->deleteIndex('movies');
```
```java
client.deleteIndex("movies");
```
```ruby
client.delete_index('movies')
```
```go
client.DeleteIndex("movies")
// OR
client.Index("movies").Delete()
```
```csharp
await client.DeleteIndexAsync("movies");
```
```rust
client.index("movies")
.delete()
.await
.unwrap();
```
```swift
client.index("movies").delete { (result) in
switch result {
case .success:
print("Index deleted")
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').delete();
```
##### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "indexDeletion",
"enqueuedAt": "2021-08-12T10:00:00.000000Z"
}
```
You can use the response's `taskUid` to [track the status of your request](/reference/api/tasks#get-one-task).
### Swap indexes
Swap the documents, settings, and task history of two or more indexes. **You can only swap indexes in pairs.** However, a single request can swap as many index pairs as you wish.
Swapping indexes is an atomic transaction: **either all indexes are successfully swapped, or none are.**
Swapping `indexA` and `indexB` will also replace every mention of `indexA` by `indexB` and vice-versa in the task history. `enqueued` tasks are left unmodified.
[To learn more about index swapping, refer to this short guide.](/learn/getting_started/indexes#swapping-indexes)
#### Body
An array of objects. Each object has only one key: `indexes`.
| Name | Type | Default value | Description |
| :------------- | :--------------- | :------------ | :----------------------------------------- |
| **`indexes`*** | Array of strings | N/A | Array of the two `indexUid`s to be swapped |
Each `indexes` array must contain only two elements: the `indexUid`s of the two indexes to be swapped. Sending an empty array (`[]`) is valid, but no swap operation will be performed.
You can swap multiple pairs of indexes with a single request. To do so, there must be one object for each pair of indexes to be swapped.
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/swap-indexes' \
-H 'Content-Type: application/json' \
--data-binary '[
{
"indexes": [
"indexA",
"indexB"
]
},
{
"indexes": [
"indexX",
"indexY"
]
}
]'
```
```js
client.swapIndexes([
{ 'indexes': ['indexA', 'indexB'] },
{ 'indexes': ['indexX', 'indexY'] }
])
```
```py
client.swap_indexes([{'indexes': ['indexA', 'indexB']}, {'indexes': ['indexX', 'indexY']}])
```
```php
$client->swapIndexes([['indexA', 'indexB'], ['indexX', 'indexY']]);
```
```java
SwapIndexesParams[] params =
new SwapIndexesParams[] {
new SwapIndexesParams().setIndexes(new String[] {"indexA", "indexB"}),
new SwapIndexesParams().setIndexes(new String[] {"indexX", "indexY"})
};
TaskInfo task = client.swapIndexes(params);
```
```ruby
client.swap_indexes(['indexA', 'indexB'], ['indexX', 'indexY'])
```
```go
client.SwapIndexes([]SwapIndexesParams{
{Indexes: []string{"indexA", "indexB"}},
{Indexes: []string{"indexX", "indexY"}},
})
```
```csharp
await client.SwapIndexesAsync(new List { new IndexSwap("indexA", "indexB"), new IndexSwap("indexX", "indexY") } });
```
```rust
client.swap_indexes([
&SwapIndexes {
indexes: (
"indexA".to_string(),
"indexB".to_string(),
),
}, &SwapIndexes {
indexes: (
"indexX".to_string(),
"indexY".to_string(),
),
}])
```
```swift
let task = try await self.client.swapIndexes([
("indexA", "indexB"),
("indexX", "indexY")
])
```
```dart
await client.swapIndexes([
SwapIndex(['indexA', 'indexB']),
SwapIndex(['indexX', 'indexY']),
]);
```
##### Response
```json
{
"taskUid": 3,
"indexUid": null,
"status": "enqueued",
"type": "indexSwap",
"enqueuedAt": "2021-08-12T10:00:00.000000Z"
}
```
Since `indexSwap` is a [global task](/learn/async/asynchronous_operations#global-tasks), the `indexUid` is always `null`.
You can use the response's `taskUid` to [track the status of your request](/reference/api/tasks#get-one-task).
---
title: Documents — Meilisearch API reference
description: The /documents route allows you to create, manage, and delete documents.
---
## Documents
The `/documents` route allows you to create, manage, and delete documents.
[Learn more about documents.](/learn/getting_started/documents)
### Get documents with POST
Get a set of documents.
Use `offset` and `limit` to browse through documents.
`filter` will not work without first explicitly adding attributes to the [`filterableAttributes` list](/reference/api/settings#update-filterable-attributes). [Learn more about filters in our dedicated guide.](/learn/filtering_and_sorting/filter_search_results)
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Body
| Name | Type | Default Value | Description |
| :----------- | :-------------------------------------- | :------------ | :-------------------------------------------------------------------- |
| **`offset`** | Integer | `0` | Number of documents to skip |
| **`limit`** | Integer | `20` | Number of documents to return |
| **`fields`** | Array of strings/`null` | `*` | Document attributes to show (case-sensitive, comma-separated) |
| **`filter`** | String/Array of array of strings/`null` | N/A | Refine results based on attributes in the `filterableAttributes` list |
| **`retrieveVectors`** | Boolean | `false` | Return document vector data with search result |
| **`ids`** | Array of primary keys | `null` | Return documents based on their primary keys |
Sending an empty payload (`--data-binary '{}'`) will return all documents in the index.
#### Response
| Name | Type | Description |
| :------------ | :------ | :------------------------------------- |
| **`results`** | Array | An array of documents |
| **`offset`** | Integer | Number of documents skipped |
| **`limit`** | Integer | Number of documents returned |
| **`total`** | Integer | Total number of documents in the index |
`/indexes/{index_uid}/documents/fetch` and `/indexes/{index_uid}/documents` responses do not return documents following the order of their primary keys.
#### Example
```bash
curl \
-X POST MEILISEARCH_URL/indexes/books/documents/fetch \
-H 'Content-Type: application/json' \
--data-binary '{
"filter": "(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English",
"fields": ["title", "genres", "rating", "language"],
"limit": 3
}'
```
```js
client.index('books').getDocuments({
filter: '(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English',
fields: ['title', 'genres', 'rating', 'language'],
limit: 3
})
```
```py
client.index('books').get_documents({
'limit':3,
'fields': ['title', 'genres', 'rating', 'language'],
'filter': '(rating > 3 AND (genres=Adventure OR genres=Fiction)) AND language=English'
})
```
```php
$client->index('books')->getDocuments(
(new DocumentsQuery())
->setFilter('(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English')
->setLimit(3)
->setFields(['title', 'genres', 'rating', 'language'])
);
```
```java
DocumentsQuery query = new DocumentsQuery()
.setFilter(new String[] {"(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English"})
.setFields(new String[] {"title", "genres", "rating", "language"})
.setLimit(3);
client.index("books").getDocuments(query, TargetClassName.class);
```
```ruby
client.index('books').get_documents(
filter: '(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English',
limit: 3,
fields: ['title', 'genres', 'rating', 'language']
)
```
```go
var result meilisearch.DocumentsResult
client.Index("books").GetDocuments(&meilisearch.DocumentsQuery{
Fields: []string{"title", "genres", "rating", "language"},
Filter: "(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English",
}, &result)
```
```csharp
await client.Index("movies").GetDocumentsAsync(new DocumentsQuery() {
Limit = 3,
Fields = new List { "title", "genres", "rating", "language"},
Filter = "(rating > 3 AND (genres=Adventure OR genres=Fiction)) AND language=English"
});
```
```rust
let index = client.index("books");
let documents: DocumentsResults = DocumentsQuery::new(&index)
.with_filter("(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English")
.with_fields(["title", "genres", "rating", "language"])
.with_limit(2)
.execute::()
.await
.unwrap();
```
```dart
await client.index('movies').getDocuments(
params: DocumentsQuery(
filterExpression: Meili.and([
'language'.toMeiliAttribute().eq('English'.toMeiliValue()),
Meili.and([
'rating'.toMeiliAttribute().gt(3.toMeiliValue()),
Meili.or([
'genres'.toMeiliAttribute().eq('Adventure'.toMeiliValue()),
'genres'.toMeiliAttribute().eq('Fiction'.toMeiliValue()),
]),
]),
]),
fields: ['title', 'genres', 'rating', 'language'],
limit: 3,
),
);
```
##### Response: `200 Ok`
```json
{
"results": [
{
"title": "The Travels of Ibn Battuta",
"genres": [
"Travel",
"Adventure"
],
"language": "English",
"rating": 4.5
},
{
"title": "Pride and Prejudice",
"genres": [
"Classics",
"Fiction",
"Romance",
"Literature"
],
"language": "English",
"rating": 4
},
…
],
"offset": 0,
"limit": 3,
"total": 5
}
```
### Get documents with GET
This endpoint will be deprecated in the near future. Consider using POST `/indexes/{index_uid}/documents/fetch` instead.
Get a set of documents.
Using the query parameters `offset` and `limit`, you can browse through all your documents.`filter` must be a string. To create [filter expressions](/learn/filtering_and_sorting/filter_expression_reference) use `AND` or `OR`.
`filter` will not work without first explicitly adding attributes to the [`filterableAttributes` list](/reference/api/settings#update-filterable-attributes). [Learn more about filters in our dedicated guide.](/learn/filtering_and_sorting/filter_search_results)
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Query parameters
| Query Parameter | Default Value | Description |
| :-------------- | :------------ | :-------------------------------------------------------------------- |
| **`offset`** | `0` | Number of documents to skip |
| **`limit`** | `20` | Number of documents to return |
| **`fields`** | `*` | Document attributes to show (case-sensitive, comma-separated) |
| **`filter`** | N/A | Refine results based on attributes in the `filterableAttributes` list |
| **`retrieveVectors`** | `false` | Return document vector data with search result |
| **`ids`** | `null` | Return documents based on their primary keys |
#### Response
| Name | Type | Description |
| :------------ | :------ | :------------------------------------- |
| **`results`** | Array | An array of documents |
| **`offset`** | Integer | Number of documents skipped |
| **`limit`** | Integer | Number of documents returned |
| **`total`** | Integer | Total number of documents in the index |
`/indexes/{index_uid}/documents/fetch` and `/indexes/{index_uid}/documents` responses do not return documents following the order of their primary keys.
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/documents?limit=2&filter=genres=action'
```
```js
client.index('movies').getDocuments({
limit: 2,
filter: 'genres = action'
})
```
```py
client.index('movies').get_documents({'limit':2, 'filter': 'genres=action'})
```
```php
$client->index('movies')->getDocuments((new DocumentsQuery())->setFilter('genres = action')->setLimit(2));
```
```java
DocumentsQuery query = new DocumentsQuery().setLimit(2).setFilter(new String[] {"genres = action"});
client.index("movies").getDocuments(query, TargetClassName.class);
```
```ruby
client.index('movies').get_documents(limit: 2, filter: 'genres = action')
```
```go
var result meilisearch.DocumentsResult
client.Index("movies").GetDocuments(&meilisearch.DocumentsQuery{
Limit: 2,
Filter: "genres = action",
}, &result)
```
```csharp
await client.Index("movies").GetDocumentsAsync(new DocumentsQuery() { Limit = 2, Filter = "genres = action" });
```
```rust
let index = client.index("movies");
let documents: DocumentsResults = DocumentsQuery::new(&index)
.with_filter("genres = action")
.with_limit(2)
.execute::()
.await
.unwrap();
```
```swift
client.index("movies").getDocuments(params: DocumentsQuery(limit: 2)) { (result: Result, Swift.Error>) in
switch result {
case .success(let movies):
print(movies)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').getDocuments(
params: DocumentsQuery(
limit: 2,
filter: Meili.attr('genres').eq('action'.toMeiliValue()),
),
);
```
##### Response: `200 Ok`
```json
{
"results": [
{
"id": 364,
"title": "Batman Returns",
"overview": "While Batman deals with a deformed man calling himself the Penguin, an employee of a corrupt businessman transforms into the Catwoman.",
"genres": [
"Action",
"Fantasy"
],
"poster": "https://image.tmdb.org/t/p/w500/jKBjeXM7iBBV9UkUcOXx3m7FSHY.jpg",
"release_date": 708912000
},
{
"id": 13851,
"title": " Batman: Gotham Knight",
"overview": "A collection of key events mark Bruce Wayne's life as he journeys from beginner to Dark Knight.",
"genres": [
"Animation",
"Action",
"Adventure"
],
"poster": "https://image.tmdb.org/t/p/w500/f3xUrqo7yEiU0guk2Ua3Znqiw6S.jpg",
"release_date": 1215475200
}
],
"offset": 0,
"limit": 2,
"total": 5403
}
```
### Get one document
Get one document using its unique id.
#### Path parameters
| Name | Type | Description |
| :------------------ | :------------- | :------------------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
| **`document_id`** * | String/Integer | [Document id](/learn/getting_started/primary_key#document-id) of the requested document |
#### Query parameters
| Query Parameter | Default Value | Description |
| :-------------- | :------------ | :------------------------------------------------------------ |
| **`fields`** | `*` | Document attributes to show (case-sensitive, comma-separated) |
| **`retrieveVectors`** | `false` | Return document vector data with search result |
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/documents/25684?fields=id,title,poster,release_date'
```
```js
client
.index('movies')
.getDocument(25684, { fields: ['id', 'title', 'poster', 'release_date'] })
```
```py
client.index('movies').get_document(25684, {
'fields': ['id', 'title', 'poster', 'release_date']
})
```
```php
$client->index('movies')->getDocument(25684, ['id', 'title', 'poster', 'release_date']);
```
```java
DocumentQuery query = new DocumentQuery().setFields(new String[] {"id", "title", "poster", "release_date"});
client.index("movies").getDocument("25684", query);
```
```ruby
client.index('movies').document(25684, fields: ['id', 'title', 'poster', 'release_date'])
```
```go
var a interface{}
client.Index("movies").GetDocument("25684",&meilisearch.DocumentQuery{
Fields: []string{"id", "title", "poster", "release_date"},
}, &a)
```
```csharp
await client.Index("movies").GetDocumentAsync(25684, new List { "id", "title", "poster", "release_date" });
```
```rust
let index = client
.index("movies");
let document = DocumentQuery::new(&index)
.with_fields(["id", "title", "poster", "release_date"])
.execute::("25684")
.await
.unwrap();
```
```swift
client.index("movies").getDocument(25684) { (result: Result) in
switch result {
case .success(let movie):
print(movie)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').getDocument(25684,
fields: ['id', 'title', 'poster', 'release_date']);
```
##### Response: `200 Ok`
```json
{
"id": 25684,
"title": "American Ninja 5",
"poster": "https://image.tmdb.org/t/p/w1280/iuAQVI4mvjI83wnirpD8GVNRVuY.jpg",
"release_date": "1993-01-01"
}
```
### Add or replace documents
Add an array of documents or replace them if they already exist. If the provided index does not exist, it will be created.
If you send an already existing document (same [document id](/learn/getting_started/primary_key#document-id)) the **whole existing document** will be overwritten by the new document. Fields that are no longer present in the new document are removed. For a partial update of the document see the [add or update documents](/reference/api/documents#add-or-update-documents) endpoint.
This endpoint accepts the following content types:
- `application/json`
- `application/x-ndjson`
- `text/csv`
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Query parameters
| Query Parameter | Default Value | Description |
| :----------------- | :------------ | :-------------------------------------------------------------------------------------------------------------------------------------- |
| **`primaryKey`** | `null` | [Primary key](/learn/getting_started/primary_key#primary-field) of the index |
| **`csvDelimiter`** | `","` | Configure the character separating CSV fields. Must be a string containing [one ASCII character](https://www.rfc-editor.org/rfc/rfc20). |
Configuring `csvDelimiter` and sending data with a content type other than CSV will cause Meilisearch to throw an error.
If you want to [set the primary key of your index on document addition](/learn/getting_started/primary_key#setting-the-primary-key-on-document-addition), it can only be done **the first time you add documents** to the index. After this, the `primaryKey` parameter will be ignored if given.
#### Body
An array of documents. Each document is represented as a JSON object.
```json
[
{
"id": 287947,
"title": "Shazam",
"poster": "https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg",
"overview": "A boy is given the ability to become an adult superhero in times of need with a single magic word.",
"release_date": "2019-03-23"
}
]
```
##### `_vectors`
`_vectors` is a special document attribute containing an object with vector embeddings for AI-powered search.
Each key of the `_vectors` object must be the name of a configured embedder and correspond to a nested object with two fields, `embeddings` and `regenerate`:
```json
[
{
"id": 452,
"title": "Female Trouble",
"overview": "Delinquent high school student Dawn Davenport runs away from home and embarks upon a life of crime.",
"_vectors": {
"default": {
"embeddings": [0.1, 0.2, 0.3],
"regenerate": false
},
"ollama": {
"embeddings": [0.4, 0.12, 0.6],
"regenerate": true
}
}
}
]
```
`embeddings` is an optional field. It must be an array of numbers representing a single embedding for that document. It may also be an array of arrays of numbers representing multiple embeddings for that document. `embeddings` defaults to `null`.
`regenerate` is a mandatory field. It must be a Boolean value. If `regenerate` is `true`, Meilisearch automatically generates embeddings for that document immediately and every time the document is updated. If `regenerate` is `false`, Meilisearch keeps the last value of the embeddings on document updates.
You may also use an array shorthand to add embeddings to a document:
```json
"_vectors": {
"default": [0.003, 0.1, 0.75]
}
```
Vector embeddings added with the shorthand are not replaced when Meilisearch generates new embeddings. The above example is equivalent to:
```json
"default": {
"embeddings": [0.003, 0.1, 0.75],
"regenerate": false
}
```
If the key for an embedder inside `_vectors` is empty or `null`, Meilisearch treats the document as not having any embeddings for that embedder. This document is then returned last during AI-powered searches.
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/documents' \
-H 'Content-Type: application/json' \
--data-binary '[
{
"id": 287947,
"title": "Shazam",
"poster": "https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg",
"overview": "A boy is given the ability to become an adult superhero in times of need with a single magic word.",
"release_date": "2019-03-23"
}
]'
```
```js
client.index('movies').addDocuments([{
id: 287947,
title: 'Shazam',
poster: 'https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg',
overview: 'A boy is given the ability to become an adult superhero in times of need with a single magic word.',
release_date: '2019-03-23'
}])
```
```py
client.index('movies').add_documents([{
'id': 287947,
'title': 'Shazam',
'poster': 'https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg',
'overview': 'A boy is given the ability to become an adult superhero in times of need with a single magic word.',
'release_date': '2019-03-23'
}])
```
```php
$client->index('movies')->addDocuments([
[
'id' => 287947,
'title' => 'Shazam',
'poster' => 'https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg',
'overview' => 'A boy is given the ability to become an adult superhero in times of need with a single magic word.',
'release_date' => '2019-03-23'
]
]);
```
```java
client.index("movies").addDocuments("[{"
+ "\"id\": 287947,"
+ "\"title\": \"Shazam\","
+ "\"poster\": \"https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg\","
+ "\"overview\": \"A boy is given the ability to become an adult superhero in times of need with a single magic word.\","
+ "\"release_date\": \"2019-03-23\""
+ "}]"
);
```
```ruby
client.index('movies').add_documents([
{
id: 287947,
title: 'Shazam',
poster: 'https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg',
overview: 'A boy is given the ability to become an adult superhero in times of need with a single magic word.',
release_date: '2019-03-23'
}
])
```
```go
documents := []map[string]interface{}{
{
"id": 287947,
"title": "Shazam",
"poster": "https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg",
"overview": "A boy is given the ability to become an adult superhero in times of need with a single magic word.",
"release_date": "2019-03-23",
},
}
client.Index("movies").AddDocuments(documents)
```
```csharp
var movie = new[]
{
new Movie
{
Id = "287947",
Title = "Shazam",
Poster = "https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg",
Overview = "A boy is given the ability to become an adult superhero in times of need with a single magic word.",
ReleaseDate = "2019-03-23"
}
};
await index.AddDocumentsAsync(movie);
```
```rust
let task: TaskInfo = client
.index("movies")
.add_or_replace(&[
Movie {
id: 287947,
title: "Shazam".to_string(),
poster: "https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg".to_string(),
overview: "A boy is given the ability to become an adult superhero in times of need with a single magic word.".to_string(),
release_date: "2019-03-23".to_string(),
}
], None)
.await
.unwrap();
```
```swift
let documentJsonString = """
[
{
"reference_number": 287947,
"title": "Shazam",
"poster": "https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg",
"overview": "A boy is given the ability to become an adult superhero in times of need with a single magic word.",
"release_date": "2019-03-23"
}
]
"""
let documents: Data = documentJsonString.data(using: .utf8)!
client.index("movies").addDocuments(documents: documents) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').addDocuments([
{
'id': 287947,
'title': 'Shazam',
'poster':
'https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg',
'overview':
'A boy is given the ability to become an adult superhero in times of need with a single magic word.',
'release_date': '2019-03-23'
}
]);
```
##### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "documentAdditionOrUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Add or update documents
Add a list of documents or update them if they already exist. If the provided index does not exist, it will be created.
If you send an already existing document (same [document id](/learn/getting_started/primary_key#document-id)) the old document will be only partially updated according to the fields of the new document. Thus, any fields not present in the new document are kept and remain unchanged.
To completely overwrite a document, check out the [add or replace documents route](/reference/api/documents#add-or-replace-documents).
If you want to set the [**primary key** of your index](/learn/getting_started/primary_key#setting-the-primary-key-on-document-addition) through this route, you may only do so **the first time you add documents** to the index. If you try to set the primary key after having added documents to the index, the task will return an error.
This endpoint accepts the following content types:
- `application/json`
- `application/x-ndjson`
- `text/csv`
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Query parameters
| Query Parameter | Default Value | Description |
| :----------------- | :------------ | :-------------------------------------------------------------------------------------------------------------------------------------- |
| **`primaryKey`** | `null` | [Primary key](/learn/getting_started/primary_key#primary-field) of the documents |
| **`csvDelimiter`** | `","` | Configure the character separating CSV fields. Must be a string containing [one ASCII character](https://www.rfc-editor.org/rfc/rfc20). |
Configuring `csvDelimiter` and sending data with a content type other than CSV will cause Meilisearch to throw an error.
#### Body
An array of documents. Each document is represented as a JSON object.
```json
[
{
"id": 287947,
"title": "Shazam ⚡️"
}
]
```
#### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/movies/documents' \
-H 'Content-Type: application/json' \
--data-binary '[
{
"id": 287947,
"title": "Shazam ⚡️",
"genres": "comedy"
}
]'
```
```js
client.index('movies').updateDocuments([{
id: 287947,
title: 'Shazam ⚡️',
genres: 'comedy'
}])
```
```py
client.index('movies').update_documents([{
'id': 287947,
'title': 'Shazam ⚡️',
'genres': 'comedy'
}])
```
```php
$client->index('movies')->updateDocuments([
[
'id' => 287947,
'title' => 'Shazam ⚡️',
'genres' => 'comedy'
]
]);
```
```java
client.index("movies").updateDocuments("[{
+ "\"id\": 287947,"
+ "\"title\": \"Shazam ⚡️\","
+ "\"genres\": \"comedy\""
+ "}]"
);
```
```ruby
client.index('movies').update_documents([
{
id: 287947,
title: 'Shazam ⚡️',
genres: 'comedy'
}
])
```
```go
documents := []map[string]interface{}{
{
"id": 287947,
"title": "Shazam ⚡️",
"genres": "comedy",
},
}
client.Index("movies").UpdateDocuments(documents)
```
```csharp
var movie = new[]
{
new Movie { Id = "287947", Title = "Shazam ⚡️", Genres = "comedy" }
};
await index.UpdateDocumentsAsync(movie);
```
```rust
// Define the type of our documents
#[derive(Serialize, Deserialize)]
struct IncompleteMovie {
id: usize,
title: String
}
let task: TaskInfo = client
.index("movies")
.add_or_update(&[
IncompleteMovie {
id: 287947,
title: "Shazam ⚡️".to_string()
}
], None)
.await
.unwrap();
```
```swift
let documentJsonString = """
[
{
"reference_number": 287947,
"title": "Shazam ⚡️",
"genres": "comedy"
}
]
"""
let documents: Data = documentJsonString.data(using: .utf8)!
client.index("movies").updateDocuments(documents: documents) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').updateDocuments([
{
'id': 287947,
'title': 'Shazam ⚡️',
'genres': 'comedy',
}
]);
```
This document is an update of the document found in [add or replace document](/reference/api/documents#add-or-replace-documents).
The documents are matched because they have the same [primary key](/learn/getting_started/documents#primary-field) value `id: 287947`. This route will update the `title` field as it changed from `Shazam` to `Shazam ⚡️` and add the new `genres` field to that document. The rest of the document will remain unchanged.
##### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "documentAdditionOrUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Update documents with function
Use a [RHAI function](https://rhai.rs/book/engine/hello-world.html) to edit one or more documents directly in Meilisearch.
This is an experimental feature. Use the experimental features endpoint to activate it:
```sh
curl \
-X PATCH 'MEILISEARCH_URL/experimental-features/' \
-H 'Content-Type: application/json' \
--data-binary '{
"editDocumentsByFunction": true
}'
```
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Query parameters
| Query Parameter | Default Value | Description |
| :----------------- | :------------ | :-------------------------------------------------------------------------------------------------------------------------------------- |
| **`function`** | `null` | A string containing a RHAI function |
| **`filter`** | `null` | A string containing a filter expression |
| **`context`** | `null` | An object with data Meilisearch should make available for the editing function |
##### `function`
`function` must be a string with a RHAI function that Meilisearch will apply to all selected documents. By default this function has access to a single variable, `doc`, representing the document you are currently editing. This is a required field.
##### `filter`
`filter` must be a string containing a filter expression. Use `filter` when you want only to apply `function` to a subset of the documents in your database.
##### `context`
Use `context` to pass data to the `function` scope. By default a function only has access to the document it is editing.
#### Example
```sh
curl \
-X POST 'MEILISEARCH_URL/indexes/INDEX_NAME/documents/edit' \
-H 'Content-Type: application/json' \
--data-binary '{
"function": "doc.title = `${doc.title.to_upper()}`"
}'
```
### Delete all documents
Delete all documents in the specified index.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies/documents'
```
```js
client.index('movies').deleteAllDocuments()
```
```py
client.index('movies').delete_all_documents()
```
```php
$client->index('movies')->deleteAllDocuments();
```
```java
client.index("movies").deleteAllDocuments();
```
```ruby
client.index('movies').delete_all_documents
```
```go
client.Index("movies").DeleteAllDocuments()
```
```csharp
await client.Index("movies").DeleteAllDocumentsAsync();
```
```rust
let task: TaskInfo = client
.index("movies")
.delete_all_documents()
.await
.unwrap();
```
```swift
client.index("movies").deleteAllDocuments() { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').deleteAllDocuments();
```
##### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "documentDeletion",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Delete one document
Delete one document based on its unique id.
#### Path parameters
| Name | Type | Description |
| :------------------ | :------------- | :------------------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
| **`document_id`** * | String/Integer | [Document id](/learn/getting_started/primary_key#document-id) of the requested document |
#### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies/documents/25684'
```
```js
client.index('movies').deleteDocument(25684)
```
```py
client.index('movies').delete_document(25684)
```
```php
$client->index('movies')->deleteDocument(25684);
```
```java
client.index("movies").deleteDocument("25684");
```
```ruby
client.index('movies').delete_document(25684)
```
```go
client.Index("movies").DeleteDocument("25684")
```
```csharp
await client.Index("movies").DeleteOneDocumentAsync("25684");
```
```rust
let task: TaskInfo = client
.index("movies")
.delete_document(25684)
.await
.unwrap();
```
```swift
client.index("movies").deleteDocument("25684") { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').deleteDocument(25684);
```
##### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "documentDeletion",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Delete documents by filter
Delete a set of documents based on a filter.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Body
A filter expression written as a string or array of array of strings for the documents to be deleted.
`filter` will not work without first explicitly adding attributes to the [`filterableAttributes` list](/reference/api/settings#update-filterable-attributes). [Learn more about filters in our dedicated guide.](/learn/filtering_and_sorting/filter_search_results)
```
"filter": "rating > 3.5"
```
Sending an empty payload (`--data-binary '{}'`) will return a `bad_request` error.
#### Example
```bash
curl \
-X POST MEILISEARCH_URL/indexes/movies/documents/delete \
-H 'Content-Type: application/json' \
--data-binary '{
"filter": "genres = action OR genres = adventure"
}'
```
```js
client.index('movies').deleteDocuments({
filter: 'genres = action OR genres = adventure'
})
```
```py
client.index('movies').delete_documents(filter='genres=action OR genres=adventure')
```
```php
$client->index('movies')->deleteDocuments(['filter' => 'genres = action OR genres = adventure']);
```
```java
String filter = "genres = action OR genres = adventure";
client.index("movies").deleteDocumentsByFilter(filter);
```
```ruby
client.index('movies').delete_documents(filter: 'genres = action OR genres = adventure')
```
```go
client.Index("movies").DeleteDocumentsByFilter("genres=action OR genres=adventure")
```
```csharp
await client.Index("movies").DeleteDocumentsAsync(new DeleteDocumentsQuery() { Filter = "genres = action OR genres = adventure" });
```
```rust
let index = client.index("movies");
let task = DocumentDeletionQuery::new(&index)
.with_filter("genres = action OR genres = adventure")
.execute()
.await
.unwrap();
```
```dart
await client.index('movies').deleteDocuments(
DeleteDocumentsQuery(
filterExpression: Meili.or([
Meili.attr('genres').eq(Meili.value('action')),
Meili.attr('genres').eq(Meili.value('adventure')),
]),
),
);
```
##### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "documentDeletion",
"enqueuedAt": "2023-05-15T08:38:48.024551Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Delete documents by batch
This endpoint will be deprecated in the near future. Consider using POST `/indexes/{index_uid}/documents/delete` instead .
Delete a set of documents based on an array of document ids.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Body
An array of numbers containing the unique ids of the documents to be deleted.
```json
[23488, 153738, 437035, 363869]
```
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/documents/delete-batch' \
-H 'Content-Type: application/json' \
--data-binary '[
23488,
153738,
437035,
363869
]'
```
```js
client.index('movies').deleteDocuments([23488, 153738, 437035, 363869])
```
```py
client.index('movies').delete_documents([23488, 153738, 437035, 363869])
```
```php
$client->index('movies')->deleteDocuments([23488, 153738, 437035, 363869]);
```
```java
client.index("movies").deleteDocuments(Arrays.asList(new String[]
{
"23488",
"153738",
"437035",
"363869"
}));
```
```ruby
client.index('movies').delete_documents([23488, 153738, 437035, 363869])
```
```go
client.Index("movies").DeleteDocuments([]string{
"23488",
"153738",
"437035",
"363869",
})
```
```csharp
await client.Index("movies").DeleteDocumentsAsync(new[] { "23488", "153738", "437035", "363869" });
```
```rust
let task: TaskInfo = client
.index("movies")
.delete_documents(&[23488, 153738, 437035, 363869])
.await
.unwrap();
```
```dart
await client.index('movies').deleteDocuments(
DeleteDocumentsQuery(
ids: [23488, 153738, 437035, 363869],
),
);
```
##### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "documentDeletion",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
---
title: Search — Meilisearch API reference
description: The /search route allows you to search your indexed documents. This route includes a large number of parameters you can use to customize returned search results.
sidebarDepth: 3
---
## Search
Meilisearch exposes two routes to perform searches:
- A POST route: this is the preferred route when using API authentication, as it allows [preflight request](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) caching and better performance
- A GET route: the usage of this route is discouraged, unless you have good reason to do otherwise (specific caching abilities for example)
You may find exhaustive descriptions of the parameters accepted by the two routes [at the end of this article](#search-parameters).
### Search in an index with POST
Search for documents matching a specific query in the given index.
This is the preferred endpoint to perform search when an API key is required, as it allows for [preflight requests](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) to be cached. Caching preflight requests **considerably improves search speed**.
By default, [this endpoint returns a maximum of 1000 results](/learn/resources/known_limitations#maximum-number-of-results-per-search). If you want to scrape your database, use the [get documents endpoint](/reference/api/documents#get-documents-with-post) instead.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Body
| Search Parameter | Type | Default value | Description |
| :------------------------------------------------------ | :--------------- | :------------ | :-------------------------------------------------- |
| **[`q`](#query-q)** | String | `""` | Query string |
| **[`offset`](#offset)** | Integer | `0` | Number of documents to skip |
| **[`limit`](#limit)** | Integer | `20` | Maximum number of documents returned |
| **[`hitsPerPage`](#number-of-results-per-page)** | Integer | `1` | Maximum number of documents returned for a page |
| **[`page`](#page)** | Integer | `1` | Request a specific page of results |
| **[`filter`](#filter)** | String or array of strings | `null` | Filter queries by an attribute's value |
| **[`facets`](#facets)** | Array of strings | `null` | Display the count of matches per facet |
| **[`distinct`](#distinct-attributes-at-search-time)** | String | `null` | Restrict search to documents with unique values of specified attribute |
| **[`attributesToRetrieve`](#attributes-to-retrieve)** | Array of strings | `["*"]` | Attributes to display in the returned documents |
| **[`attributesToCrop`](#attributes-to-crop)** | Array of strings | `null` | Attributes whose values have to be cropped |
| **[`cropLength`](#crop-length)** | Integer | `10` | Maximum length of cropped value in words |
| **[`cropMarker`](#crop-marker)** | String | `"…"` | String marking crop boundaries |
| **[`attributesToHighlight`](#attributes-to-highlight)** | Array of strings | `null` | Highlight matching terms contained in an attribute |
| **[`highlightPreTag`](#highlight-tags)** | String | `""` | String inserted at the start of a highlighted term |
| **[`highlightPostTag`](#highlight-tags)** | String | `""` | String inserted at the end of a highlighted term |
| **[`showMatchesPosition`](#show-matches-position)** | Boolean | `false` | Return matching terms location |
| **[`sort`](#sort)** | Array of strings | `null` | Sort search results by an attribute's value |
| **[`matchingStrategy`](#matching-strategy)** | String | `last` | Strategy used to match query terms within documents |
| **[`showRankingScore`](#ranking-score)** | Boolean | `false` | Display the global ranking score of a document |
| **[`showRankingScoreDetails`](#ranking-score-details)** | Boolean | `false` | Adds a detailed global ranking score field |
| **[`rankingScoreThreshold`](#ranking-score-threshold)** | Number | `null` | Excludes results with low ranking scores |
| **[`attributesToSearchOn`](#customize-attributes-to-search-on-at-search-time)** | Array of strings | `["*"]` | Restrict search to the specified attributes |
| **[`hybrid`](#hybrid-search)** | Object | `null` | Return results based on query keywords and meaning |
| **[`vector`](#vector)** | Array of numbers | `null` | Search using a custom query vector |
| **[`retrieveVectors`](#display-_vectors-in-response)** | Boolean | `false` | Return document vector data |
| **[`locales`](#query-locales)** | Array of strings | `null` | Explicitly specify languages used in a query |
#### Response
| Name | Type | Description |
| :----------------------- | :--------------- | :---------------------------------------------------------- |
| **`hits`** | Array of objects | Results of the query |
| **`offset`** | Number | Number of documents skipped |
| **`limit`** | Number | Number of documents to take |
| **`estimatedTotalHits`** | Number | Estimated total number of matches |
| **`totalHits`** | Number | Exhaustive total number of matches |
| **`totalPages`** | Number | Exhaustive total number of search result pages |
| **`hitsPerPage`** | Number | Number of results on each page |
| **`page`** | Number | Current search results page |
| **`facetDistribution`** | Object | **[Distribution of the given facets](#facetdistribution)** |
| **`facetStats`** | Object | [The numeric `min` and `max` values per facet](#facetstats) |
| **`processingTimeMs`** | Number | Processing time of the query |
| **`query`** | String | Query originating the response |
##### Exhaustive and estimated total number of search results
By default, Meilisearch only returns an estimate of the total number of search results in a query: `estimatedTotalHits`. This happens because Meilisearch prioritizes relevancy and performance over providing an exhaustive number of search results. When working with `estimatedTotalHits`, use `offset` and `limit` to navigate between search results.
If you require the total number of search results, use the `hitsPerPage` and `page` search parameters in your query. The response to this query replaces `estimatedTotalHits` with `totalHits` and includes an extra field with number of search results pages based on your `hitsPerPage`: `totalPages`. Using `totalHits` and `totalPages` may result in slightly reduced performance, but is recommended when creating UI elements such as numbered page selectors.
Neither `estimatedTotalHits` nor `totalHits` can exceed the limit configured in [the `maxTotalHits` index setting](/reference/api/settings#pagination).
You can [read more about pagination in our dedicated guide](/guides/front_end/pagination).
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{ "q": "american ninja" }'
```
```js
client.index('movies').search('American ninja')
```
```py
client.index('movies').search('American ninja')
```
```php
$client->index('movies')->search('american ninja');
```
```java
client.index("movies").search("American ninja");
```
```ruby
client.index('movies').search('american ninja')
```
```go
client.Index("movies").Search("american ninja", &meilisearch.SearchRequest{})
```
```csharp
await client.Index("movies").SearchAsync("American ninja");
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("American ninja")
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(query: "American ninja")
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search('American ninja');
```
##### Response: `200 Ok`
```json
{
"hits": [
{
"id": 2770,
"title": "American Pie 2",
"poster": "https://image.tmdb.org/t/p/w1280/q4LNgUnRfltxzp3gf1MAGiK5LhV.jpg",
"overview": "The whole gang are back and as close as ever. They decide to get even closer by spending the summer together at a beach house. They decide to hold the biggest…",
"release_date": 997405200
},
{
"id": 190859,
"title": "American Sniper",
"poster": "https://image.tmdb.org/t/p/w1280/svPHnYE7N5NAGO49dBmRhq0vDQ3.jpg",
"overview": "U.S. Navy SEAL Chris Kyle takes his sole mission—protect his comrades—to heart and becomes one of the most lethal snipers in American history. His pinpoint accuracy not only saves countless lives but also makes him a prime…",
"release_date": 1418256000
},
…
],
"offset": 0,
"limit": 20,
"estimatedTotalHits": 976,
"processingTimeMs": 35,
"query": "american "
}
```
### Search in an index with GET
Search for documents matching a specific query in the given index.
This endpoint only accepts [string filter expressions](/learn/filtering_and_sorting/filter_expression_reference).
This endpoint should only be used when no API key is required. If an API key is required, use the [POST](/reference/api/search#search-in-an-index-with-post) route instead.
By default, [this endpoint returns a maximum of 1000 results](/learn/resources/known_limitations#maximum-number-of-results-per-search). If you want to scrape your database, use the [get documents endpoint](/reference/api/documents#get-documents-with-post) instead.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Query parameters
| Search Parameter | Type | Default value | Description |
| :------------------------------------------------------ | :--------------- | :------------ | :-------------------------------------------------- |
| Search Parameter | Type | Default value | Description |
| :------------------------------------------------------ | :--------------- | :------------ | :-------------------------------------------------- |
| **[`q`](#query-q)** | String | `""` | Query string |
| **[`offset`](#offset)** | Integer | `0` | Number of documents to skip |
| **[`limit`](#limit)** | Integer | `20` | Maximum number of documents returned |
| **[`hitsPerPage`](#number-of-results-per-page)** | Integer | `1` | Maximum number of documents returned for a page |
| **[`page`](#page)** | Integer | `1` | Request a specific page of results |
| **[`filter`](#filter)** | String or array of strings | `null` | Filter queries by an attribute's value |
| **[`facets`](#facets)** | Array of strings | `null` | Display the count of matches per facet |
| **[`distinct`](#distinct-attributes-at-search-time)** | String | `null` | Restrict search to documents with unique values of specified attribute |
| **[`attributesToRetrieve`](#attributes-to-retrieve)** | Array of strings | `["*"]` | Attributes to display in the returned documents |
| **[`attributesToCrop`](#attributes-to-crop)** | Array of strings | `null` | Attributes whose values have to be cropped |
| **[`cropLength`](#crop-length)** | Integer | `10` | Maximum length of cropped value in words |
| **[`cropMarker`](#crop-marker)** | String | `"…"` | String marking crop boundaries |
| **[`attributesToHighlight`](#attributes-to-highlight)** | Array of strings | `null` | Highlight matching terms contained in an attribute |
| **[`highlightPreTag`](#highlight-tags)** | String | `""` | String inserted at the start of a highlighted term |
| **[`highlightPostTag`](#highlight-tags)** | String | `""` | String inserted at the end of a highlighted term |
| **[`showMatchesPosition`](#show-matches-position)** | Boolean | `false` | Return matching terms location |
| **[`sort`](#sort)** | Array of strings | `null` | Sort search results by an attribute's value |
| **[`matchingStrategy`](#matching-strategy)** | String | `last` | Strategy used to match query terms within documents |
| **[`showRankingScore`](#ranking-score)** | Boolean | `false` | Display the global ranking score of a document |
| **[`showRankingScoreDetails`](#ranking-score-details)** | Boolean | `false` | Adds a detailed global ranking score field |
| **[`rankingScoreThreshold`](#ranking-score-threshold)** | Number | `null` | Excludes results with low ranking scores |
| **[`attributesToSearchOn`](#customize-attributes-to-search-on-at-search-time)** | Array of strings | `["*"]` | Restrict search to the specified attributes |
| **[`hybrid`](#hybrid-search)** | Object | `null` | Return results based on query keywords and meaning |
| **[`vector`](#vector)** | Array of numbers | `null` | Search using a custom query vector |
| **[`retrieveVectors`](#display-_vectors-in-response)** | Boolean | `false` | Return document vector data |
| **[`locales`](#query-locales)** | Array of strings | `null` | Explicitly specify languages used in a query |
#### Response
| Name | Type | Description |
| :----------------------- | :--------------- | :---------------------------------------------------------- |
| **`hits`** | Array of objects | Results of the query |
| **`offset`** | Number | Number of documents skipped |
| **`limit`** | Number | Number of documents to take |
| **`estimatedTotalHits`** | Number | Estimated total number of matches |
| **`totalHits`** | Number | Exhaustive total number of matches |
| **`totalPages`** | Number | Exhaustive total number of search result pages |
| **`hitsPerPage`** | Number | Number of results on each page |
| **`page`** | Number | Current search results page |
| **`facetDistribution`** | Object | **[Distribution of the given facets](#facetdistribution)** |
| **`facetStats`** | Object | [The numeric `min` and `max` values per facet](#facetstats) |
| **`processingTimeMs`** | Number | Processing time of the query |
| **`query`** | String | Query originating the response |
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/search?q=american%20ninja'
```
```js
client.index('movies').searchGet('American ninja')
```
```dart
await client.index('movies').search('American ninja');
```
##### Response: `200 Ok`
```json
{
"hits": [
{
"id": 2770,
"title": "American Pie 2",
"poster": "https://image.tmdb.org/t/p/w1280/q4LNgUnRfltxzp3gf1MAGiK5LhV.jpg",
"overview": "The whole gang are back and as close as ever. They decide to get even closer by spending the summer together at a beach house. They decide to hold the biggest…",
"release_date": 997405200
},
{
"id": 190859,
"title": "American Sniper",
"poster": "https://image.tmdb.org/t/p/w1280/svPHnYE7N5NAGO49dBmRhq0vDQ3.jpg",
"overview": "U.S. Navy SEAL Chris Kyle takes his sole mission—protect his comrades—to heart and becomes one of the most lethal snipers in American history. His pinpoint accuracy not only saves countless lives but also makes him a prime…",
"release_date": 1418256000
},
…
],
"offset": 0,
"limit": 20,
"estimatedTotalHits": 976,
"processingTimeMs": 35,
"query": "american "
}
```
### Search parameters
Here follows an exhaustive description of each search parameter currently available when using the search endpoint. Unless otherwise noted, all parameters are valid for the `GET /indexes/{index_uid}/search`, `POST /indexes/{index_uid}/search`, and `/multi-search` routes.
If [using the `GET` route to perform a search](/reference/api/search#search-in-an-index-with-get), all parameters must be **URL-encoded**.
This is not necessary when using the `POST` route or one of our [SDKs](/learn/resources/sdks).
#### Query (q)
**Parameter**: `q`
**Expected value**: Any string
**Default value**: `null`
Sets the search terms.
Meilisearch only considers the first ten words of any given search query. This is necessary in order to deliver a [fast search-as-you-type experience](/learn/resources/known_limitations#maximum-number-of-query-words).
##### Example
You can search for films mentioning `shifu` by setting the `q` parameter:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{ "q": "shifu" }'
```
```js
client.index('movies').search('shifu')
```
```py
client.index('movies').search('shifu')
```
```php
$client->index('movies')->search('shifu');
```
```java
client.index("movies").search("shifu");
```
```ruby
client.index('movies').search('shifu')
```
```go
resp, err := client.Index("movies").Search("shifu", &meilisearch.SearchRequest{})
```
```csharp
await client.Index("movies").SearchAsync("shifu");
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("shifu")
.execute()
.await
.unwrap();
```
```swift
client.index("movies").search(SearchParameters(query: "shifu")) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search('shifu');
```
This will give you a list of documents that contain your query terms in at least one attribute.
```json
{
"hits": [
{
"id": 50393,
"title": "Kung Fu Panda Holiday",
"poster": "https://image.tmdb.org/t/p/w500/rV77WxY35LuYLOuQvBeD1nyWMuI.jpg",
"overview": "The Winter Feast is Po's favorite holiday. Every year he and his father hang decorations, cook together, and serve noodle soup to the villagers. But this year Shifu informs Po that as Dragon Warrior, it is his duty to host the formal Winter Feast at the Jade Palace.",
"release_date": 1290729600,
"genres": [
"Animation",
"Family",
"TV Movie"
]
}
],
"query": "shifu"
}
```
##### Query term normalization
Query terms go through a normalization process that removes [non-spacing marks](https://www.compart.com/en/unicode/category/Mn). Because of this, Meilisearch effectively ignores accents and diacritics when returning results. For example, searching for `"sábia"` returns documents containing `"sábia"`, `"sabiá"`, and `"sabia"`.
Normalization also converts all letters to lowercase. Searching for `"Video"` returns the same results as searching for `"video"`, `"VIDEO"`, or `"viDEO"`.
##### Placeholder search
When `q` isn't specified, Meilisearch performs a **placeholder search**. A placeholder search returns all searchable documents in an index, modified by any search parameters used and sorted by that index's [custom ranking rules](/learn/relevancy/custom_ranking_rules). Since there is no query term, the [built-in ranking rules](/learn/relevancy/ranking_rules) **do not apply.**
If the index has no sort or custom ranking rules, the results are returned in the order of their internal database position.
Placeholder search is particularly useful when building a [faceted search interfaces](/learn/filtering_and_sorting/search_with_facet_filters), as it allows users to view the catalog and alter sorting rules without entering a query.
##### Phrase search
If you enclose search terms in double quotes (`"`), Meilisearch will only return documents containing those terms in the order they were given. This is called a **phrase search**.
Phrase searches are case-insensitive and ignore [soft separators such as `-`, `,`, and `:`](/learn/engine/datatypes). Using a hard separator within a phrase search effectively splits it into multiple separate phrase searches: `"Octavia.Butler"` will return the same results as `"Octavia" "Butler"`.
You can combine phrase search and normal queries in a single search request. In this case, Meilisearch will first fetch all documents with exact matches to the given phrase(s), and [then proceed with its default behavior](/learn/relevancy/relevancy).
###### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{ "q": "\"african american\" horror" }'
```
```js
client.index('movies')
.search('"african american" horror')
```
```py
client.index('movies').search('"african american" horror')
```
```php
$client->index('movies')->search('"african american" horror');
```
```java
client.index("movies").search("\"african american\" horror");
```
```ruby
client.index('movies').search('"african american" horror')
```
```go
resp, err := client.Index("movies").Search("\"african american\" horror", &meilisearch.SearchRequest{})
```
```csharp
await client.Index("movies").SearchAsync("\"african american\" horror");
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("\"african american\" horror")
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(
query: "\"african american\" horror")
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search('"african american" horror');
```
##### Negative search
Use the minus (`-`) operator in front of a word or phrase to exclude it from search results.
###### Example
The following query returns all documents that do not include the word "escape":
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{ "q": "-escape" }'
```
```js
client.index('movies').search('-escape')
```
```php
$client->index('movies')->search('-escape');
```
Negative search can be used together with phrase search:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{ "q": "-\"escape room\"" }'
```
```js
client.index('movies').search('-"escape"')
```
```php
$client->index('movies')->search('-"escape"');
```
#### Offset
**Parameter**: `offset`
**Expected value**: Any positive integer
**Default value**: `0`
Sets the starting point in the search results, effectively skipping over a given number of documents.
Queries using `offset` and `limit` only return an estimate of the total number of search results.
You can [paginate search results](/guides/front_end/pagination) by making queries combining both `offset` and `limit`.
Setting `offset` to a value greater than an [index's `maxTotalHits`](/reference/api/settings#update-pagination-settings) returns an empty array.
##### Example
If you want to skip the **first** result in a query, set `offset` to `1`:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "shifu",
"offset": 1
}'
```
```js
client.index('movies').search('shifu', {
offset: 1
})
```
```py
client.index('movies').search('shifu', {
'offset': 1
})
```
```php
$client->index('movies')->search('shifu', ['offset' => 1]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("shifu").offset(1).build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('shifu', {
offset: 1
})
```
```go
resp, err := client.Index("movies").Search("shifu", &meilisearch.SearchRequest{
Offset: 1,
})
```
```csharp
var sq = new SearchQuery
{
Offset = 1
};
var result = await client.Index("movies").SearchAsync("shifu", sq);
if(result is SearchResult pagedResults)
{
}
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("shifu")
.with_offset(1)
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(
query: "shifu",
offset: 1)
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search('shifu', SearchQuery(offset: 1));
```
#### Limit
**Parameter**: `limit`
**Expected value**: Any positive integer or zero
**Default value**: `20`
Sets the maximum number of documents returned by a single query.
You can [paginate search results](/guides/front_end/pagination) by making queries combining both `offset` and `limit`.
A search query cannot return more results than configured in [`maxTotalHits`](/reference/api/settings#pagination-object), even if the value of `limit` is greater than the value of `maxTotalHits`.
##### Example
If you want your query to return only **two** documents, set `limit` to `2`:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "shifu",
"limit": 2
}'
```
```js
client.index('movies').search('shifu', {
limit: 2
})
```
```py
client.index('movies').search('shifu', {
'limit': 2
})
```
```php
$client->index('movies')->search('shifu', ['limit' => 2]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("shifu").limit(2).build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('shifu', {
limit: 2
})
```
```go
resp, err := client.Index("movies").Search("shifu", &meilisearch.SearchRequest{
Limit: 2,
})
```
```csharp
var sq = new SearchQuery
{
Limit = 2
};
var result = await client.Index("movies").SearchAsync("shifu", sq);
if(result is SearchResult pagedResults)
{
}
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("shifu")
.with_limit(2)
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(
query: "shifu",
limit: 2)
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search('shifu', SearchQuery(limit: 2));
```
#### Number of results per page
**Parameter**: `hitsPerPage`
**Expected value**: Any positive integer
**Default value**: `20`
Sets the maximum number of documents returned for a single query. The value configured with this parameter dictates the number of total pages: if Meilisearch finds a total of `20` matches for a query and your `hitsPerPage` is set to `5`, `totalPages` is `4`.
Queries containing `hitsPerPage` are exhaustive and do not return an `estimatedTotalHits`. Instead, the response body will include `totalHits` and `totalPages`.
If you set `hitsPerPage` to `0`, Meilisearch processes your request, but does not return any documents. In this case, the response body will include the exhaustive value for `totalHits`. The response body will also include `totalPages`, but its value will be `0`.
You can use `hitsPerPage` and `page` to [paginate search results](/guides/front_end/pagination).
`hitsPerPage` and `page` take precedence over `offset` and `limit`. If a query contains either `hitsPerPage` or `page`, any values passed to `offset` and `limit` are ignored.
`hitsPerPage` and `page` are resource-intensive options and might negatively impact search performance. This is particularly likely if [`maxTotalHits`](/reference/api/settings#pagination) is set to a value higher than its default.
##### Example
The following example returns the first 15 results for a query:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "",
"hitsPerPage": 15
}'
```
```js
client.index('movies').search('', {
hitsPerPage: 15
})
```
```py
client.index('movies').search('', {'hitsPerPage': 15})
```
```php
$client->index('movies')->search('', ['hitsPerPage' => 15]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("").hitsPerPage(15).build();
SearchResultPaginated searchResult = client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('', hits_per_page: 15)
```
```go
client.Index("movies").Search("", &meilisearch.SearchRequest{
HitsPerPage: 15,
})
```
```csharp
var result = await client.Index("movies").SearchAsync("", new SearchQuery { HitsPerPage = 15 });
if(result is PaginatedSearchResult pagedResults)
{
}
```
```rust
client.index("movies").search().with_hits_per_page(15).execute().await?;
```
```swift
let searchParameters = SearchParameters(query: "", hitsPerPage: 15)
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client
.index('movies')
.search('', SearchQuery(hitsPerPage: 15))
.asPaginatedResult();
```
#### Page
**Parameter**: `page`
**Expected value**: Any positive integer
**Default value**: `1`
Requests a specific results page. Pages are calculated using the `hitsPerPage` search parameter.
Queries containing `page` are exhaustive and do not return an `estimatedTotalHits`. Instead, the response body will include two new fields: `totalHits` and `totalPages`.
If you set `page` to `0`, Meilisearch processes your request, but does not return any documents. In this case, the response body will include the exhaustive values for `facetDistribution`, `totalPages`, and `totalHits`.
You can use `hitsPerPage` and `page` to [paginate search results](/guides/front_end/pagination).
`hitsPerPage` and `page` take precedence over `offset` and `limit`. If a query contains either `hitsPerPage` or `page`, any values passed to `offset` and `limit` are ignored.
`hitsPerPage` and `page` are resource-intensive options and might negatively impact search performance. This is particularly likely if [`maxTotalHits`](/reference/api/settings#pagination) is set to a value higher than its default.
##### Example
The following example returns the second page of search results:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "",
"page": 2
}'
```
```js
client.index('movies').search('', {
page: 2
})
```
```py
client.index('movies').search('', {'page': 2})
```
```php
$client->index('movies')->search('', ['page' => 2]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("").page(15).build();
SearchResultPaginated searchResult = client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('', page: 2)
```
```go
client.Index("movies").Search("", &meilisearch.SearchRequest{
Page: 2,
})
```
```csharp
var result = await client.Index("movies").SearchAsync("", new SearchQuery { Page = 2 });
if(result is PaginatedSearchResult pagedResults)
{
}
```
```rust
client.index("movies").search().with_page(2).execute().await?;
```
```swift
let searchParameters = SearchParameters(query: "", page: 15)
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client
.index('movies')
.search('', SearchQuery(page: 2))
.asPaginatedResult();
```
#### Filter
**Parameter**: `filter`
**Expected value**: A filter expression written as a string or an array of strings
**Default value**: `[]`
Uses filter expressions to refine search results. Attributes used as filter criteria must be added to the [`filterableAttributes` list](/reference/api/settings#filterable-attributes).
For more information, [read our guide on how to use filters and build filter expressions](/learn/filtering_and_sorting/filter_search_results).
##### Example
You can write a filter expression in string syntax using logical connectives:
```
"(genres = horror OR genres = mystery) AND director = 'Jordan Peele'"
```
You can write the same filter as an array:
```
[["genres = horror", "genres = mystery"], "director = 'Jordan Peele'"]
```
You can then use the filter in a search query:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "thriller",
"filter": [
[
"genres = Horror",
"genres = Mystery"
],
"director = \"Jordan Peele\""
]
}'
```
```js
client.index('movies')
.search('thriller', {
filter: [['genres = Horror', 'genres = Mystery'], 'director = "Jordan Peele"']
})
```
```py
client.index('movies').search('thriller', {
'filter': [['genres = Horror', 'genres = Mystery'], 'director = "Jordan Peele"']
})
```
```php
$client->index('movies')->search('thriller', [
'filter' => [['genres = Horror', 'genres = Mystery'], 'director = "Jordan Peele"']
]);
```
```java
SearchRequest searchRequest =
SearchRequest.builder().q("thriller").filterArray(new String[][] {
new String[] {"genres = Horror", "genres = Mystery"},
new String[] {"director = \"Jordan Peele\""}}).build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('thriller', {
filter: [['genres = Horror', 'genres = Mystery'], 'director = "Jordan Peele"']
})
```
```go
resp, err := client.Index("movies").Search("thriller", &meilisearch.SearchRequest{
Filter: [][]string{
[]string{"genres = Horror", "genres = Mystery"},
[]string{"director = \"Jordan Peele\""},
},
})
```
```csharp
var sq = new SearchQuery
{
Filter = "(genre = 'Horror' AND genre = 'Mystery') OR director = 'Jordan Peele'"
};
await client.Index("movies").SearchAsync("thriller", sq);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("thriller")
.with_filter("(genres = Horror AND genres = Mystery) OR director = \"Jordan Peele\"")
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(
query: "thriller",
filter: [
[
"genres = Horror",
"genres = Mystery"
],
"director = \"Jordan Peele\""
])
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search(
'thriller',
SearchQuery(filter: [
['genres = Horror', 'genres = Mystery'],
'director = "Jordan Peele"'
]));
```
##### Filtering results with `_geoRadius` and `_geoBoundingBox`
If your documents contain `_geo` data, you can use the `_geoRadius` and `_geoBoundingBox` built-in filter rules to filter results according to their geographic position.
`_geoRadius` establishes a circular area based on a central point and a radius. This filter rule requires three parameters: `lat`, `lng` and `distance_in_meters`.
```json
_geoRadius(lat, lng, distance_in_meters)
```
`lat` and `lng` should be geographic coordinates expressed as floating point numbers. `distance_in_meters` indicates the radius of the area within which you want your results and should be an integer.
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
-H 'Content-type:application/json' \
--data-binary '{ "filter": "_geoRadius(45.472735, 9.184019, 2000)" }'
```
```js
client.index('restaurants').search('', {
filter: ['_geoRadius(45.472735, 9.184019, 2000)'],
})
```
```py
client.index('restaurants').search('', {
'filter': '_geoRadius(45.472735, 9.184019, 2000)'
})
```
```php
$client->index('restaurants')->search('', [
'filter' => '_geoRadius(45.472735, 9.184019, 2000)'
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("").filter(new String[] {"_geoRadius(45.472735, 9.184019, 2000)"}).build();
client.index("restaurants").search(searchRequest);
```
```ruby
client.index('restaurants').search('', { filter: '_geoRadius(45.472735, 9.184019, 2000)' })
```
```go
resp, err := client.Index("restaurants").Search("", &meilisearch.SearchRequest{
Filter: "_geoRadius(45.472735, 9.184019, 2000)",
})
```
```csharp
SearchQuery filters = new SearchQuery() { Filter = "_geoRadius(45.472735, 9.184019, 2000)" };
var restaurants = await client.Index("restaurants").SearchAsync("", filters);
```
```rust
let results: SearchResults = client
.index("restaurants")
.search()
.with_filter("_geoRadius(45.472735, 9.184019, 2000)")
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(
filter: "_geoRadius(45.472735, 9.184019, 2000)"
)
client.index("restaurants").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('restaurants').search(
'',
SearchQuery(
filterExpression: Meili.geoRadius(
(lat: 45.472735, lng: 9.184019),
2000,
),
),
);
```
`_geoBoundingBox` establishes a rectangular area based on the coordinates for its top right and bottom left corners. This filter rule requires two arrays of geographic coordinates:
```
_geoBoundingBox([{lat}, {lng}], [{lat}, {lng}])
```
`lat` and `lng` should be geographic coordinates expressed as floating point numbers. The first array indicates the top right corner and the second array indicates the bottom left corner of the bounding box.
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
-H 'Content-type:application/json' \
--data-binary '{ "filter": "_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])" }'
```
```js
client.index('restaurants').search('', {
filter: ['_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])'],
})
```
```py
client.index('restaurants').search('Batman', {
'filter': '_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])'
})
```
```php
$client->index('restaurants')->search('', [
'filter' => '_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])'
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q()("").filter(new String[] {
"_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])"
}).build();
client.index("restaurants").search(searchRequest);
```
```ruby
client.index('restaurants').search('', { filter: ['_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])'] })
```
```go
client.Index("restaurants").Search("", &meilisearch.SearchRequest{
Filter: "_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])",
})
```
```csharp
SearchQuery filters = new SearchQuery()
{
Filter = "_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])"
};
var restaurants = await client.Index("restaurants").SearchAsync("restaurants", filters);
```
```rust
let results: SearchResults = client
.index("restaurants")
.search()
.with_filter("_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])")
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(
filter: "_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])"
)
client.index("restaurants").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('restaurants').search(
'',
SearchQuery(
filter:
'_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])',
),
);
```
Meilisearch will throw an error if the top right corner is under the bottom left corner.
If any parameters are invalid or missing, Meilisearch returns an [`invalid_search_filter`](/reference/errors/error_codes#invalid_search_filter) error.
#### Facets
**Parameter**: `facets`
**Expected value**: An array of `attribute`s or `["*"]`
**Default value**: `null`
Returns the number of documents matching the current search query for each given facet. This parameter can take two values:
- An array of attributes: `facets=["attributeA", "attributeB", …]`
- An asterisk—this will return a count for all facets present in `filterableAttributes`
By default, `facets` returns a maximum of 100 facet values for each faceted field. You can change this value using the `maxValuesPerFacet` property of the [`faceting` index settings](/reference/api/settings#faceting).
When `facets` is set, the search results object includes the [`facetDistribution`](#facetdistribution) and [`facetStats`](#facetstats) fields.
If an attribute used on `facets` has not been added to the `filterableAttributes` list, it will be ignored.
##### `facetDistribution`
`facetDistribution` contains the number of matching documents distributed among the values of a given facet. Each facet is represented as an object:
```json
{
…
"facetDistribution": {
"FACET_A": {
"FACET_VALUE_X": 6,
"FACET_VALUE_Y": 1,
},
"FACET_B": {
"FACET_VALUE_Z": 3,
"FACET_VALUE_W": 9,
},
},
…
}
```
`facetDistribution` contains an object for every attribute passed to the `facets` parameter. Each object contains the returned values for that attribute and the count of matching documents with that value. Meilisearch does not return empty facets.
##### `facetStats`
`facetStats` contains the lowest (`min`) and highest (`max`) numerical values across all documents in each facet. Only numeric values are considered:
```json
{
…
"facetStats": {
"rating": {
"min": 2.5,
"max": 4.7
}
}
…
}
```
If none of the matching documents have a numeric value for a facet, that facet is not included in the `facetStats` object. `facetStats` ignores string values, even if the string contains a number.
##### Example
Given a movie ratings database, the following code sample returns the number of `Batman` movies per genre along with the minimum and maximum ratings:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movie_ratings/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "Batman",
"facets": ["genres", "rating"]
}'
```
```js
client.index('movie_ratings').search('Batman', { facets: ['genres', 'rating'] })
```
```py
client.index('movie_ratings').search('Batman', {
'facets': ['genres', 'rating']
})
```
```php
$client->index('movie_ratings')->search('Batman', [
'facets' => ['genres', 'rating']
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("Batman").facets(new String[]
{
"genres",
"rating"
}).build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movie_ratings').search('Batman', {
facets: ['genres', 'rating']
})
```
```go
client.Index("movie_ratings").Search("Batman", &meilisearch.SearchRequest{
Facets: []string{
"genres",
"rating",
},
})
```
```csharp
var sq = new SearchQuery
{
Facets = new string[] { "genres", "rating" }
};
await client.Index("movie_ratings").SearchAsync("Batman", sq);
```
```rust
let books = client.index("movie_ratings");
let results: SearchResults = SearchQuery::new(&books)
.with_query("Batman")
.with_facets(Selectors::Some(&["genres", "rating"))
.execute()
.await
.unwrap();
```
```dart
await client
.index('movie_ratings')
.search('Batman', SearchQuery(facets: ['genres', 'rating']));
```
The response shows the facet distribution for `genres` and `rating`. Since `rating` is a numeric field, you get its minimum and maximum values in `facetStats`.
```json
{
…
"estimatedTotalHits": 22,
"query": "Batman",
"facetDistribution": {
"genres": {
"Action": 20,
"Adventure": 7,
…
"Thriller": 3
},
"rating": {
"2": 1,
…
"9.8": 1
}
},
"facetStats": {
"rating": {
"min": 2.0,
"max": 9.8
}
}
}
```
[Learn more about facet distribution in the faceted search guide.](/learn/filtering_and_sorting/search_with_facet_filters)
#### Distinct attributes at search time
**Parameter**: `distinct`
**Expected value**: An `attribute` present in the `filterableAttributes` list
**Default value**: `null`
Defines one attribute in the `filterableAttributes` list as a distinct attribute. Distinct attributes indicate documents sharing the same value for the specified field are equivalent and only the most relevant one should be returned in search results.
This behavior is similar to the [`distinctAttribute` index setting](/reference/api/settings#distinct-attribute), but can be configured at search time. `distinctAttribute` acts as a default distinct attribute value you may override with `distinct`.
##### Examples
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/INDEX_NAME/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "QUERY TERMS",
"distinct": "ATTRIBUTE_A"
}'
```
```js
client.index('INDEX_NAME').search('QUERY TERMS', { distinct: 'ATTRIBUTE_A' })
```
```py
client.index('INDEX_NAME').search('QUERY_TERMS', { distinct: 'ATTRIBUTE_A' })
```
```php
$client->index('INDEX_NAME')->search('QUERY TERMS', [
'distinct' => 'ATTRIBUTE_A'
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("QUERY TERMS").distinct("ATTRIBUTE_A").build();
client.index("INDEX_NAME").search(searchRequest);
```
```ruby
client.index('INDEX_NAME').search('QUERY TERMS', {
distinct: 'ATTRIBUTE_A'
})
```
```go
client.Index("INDEX_NAME").Search("QUERY TERMS", &meilisearch.SearchRequest{
Distinct: "ATTRIBUTE_A",
})
```
```csharp
var params = new SearchQuery()
{
Distinct = "ATTRIBUTE_A"
};
await client.Index("INDEX_NAME").SearchAsync("QUERY TERMS", params);
```
```rust
let res = client
.index("INDEX_NAME")
.search()
.with_query("QUERY TERMS")
.with_distinct("ATTRIBUTE_A")
.execute()
.await
.unwrap();
```
#### Attributes to retrieve
**Parameter**: `attributesToRetrieve`
**Expected value**: An array of `attribute`s or `["*"]`
**Default value**: `["*"]`
Configures which attributes will be retrieved in the returned documents.
If no value is specified, `attributesToRetrieve` uses the [`displayedAttributes` list](/reference/api/settings#displayed-attributes), which by default contains all attributes found in the documents.
If an attribute has been removed from `displayedAttributes`, `attributesToRetrieve` will silently ignore it and the field will not appear in your returned documents.
##### Example
To get only the `overview` and `title` fields, set `attributesToRetrieve` to `["overview", "title"]`.
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "shifu",
"attributesToRetrieve": [
"overview",
"title"
]
}'
```
```js
client.index('movies').search('shifu', {
attributesToRetrieve: ['overview', 'title']
})
```
```py
client.index('movies').search('shifu', {
'attributesToRetrieve': ['overview', 'title']
})
```
```php
$client->index('movies')->search('shifu', [
'attributesToRetrieve' => ['overview', 'title']
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("a").attributesToRetrieve(new String[] {"overview", "title"}).build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('shifu', {
attributes_to_retrieve: ['overview', 'title']
})
```
```go
resp, err := client.Index("movies").Search("shifu", &meilisearch.SearchRequest{
AttributesToRetrieve: []string{"overview", "title"},
})
```
```csharp
var sq = new SearchQuery
{
AttributesToRetrieve = new[] {"overview", "title"}
};
await client.Index("movies").SearchAsync("shifu", sq);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("shifu")
.with_attributes_to_retrieve(Selectors::Some(&["overview", "title"]))
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(
query: "shifu",
attributesToRetrieve: ["overview", "title"])
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search(
'shifu', SearchQuery(attributesToRetrieve: ['overview', 'title']));
```
#### Attributes to crop
**Parameter**: `attributesToCrop`
**Expected value**: An array of attributes or `["*"]`
**Default value**: `null`
Crops the selected fields in the returned results to the length indicated by the [`cropLength`](#crop-length) parameter. When `attributesToCrop` is set, each returned document contains an extra field called `_formatted`. This object contains the cropped version of the selected attributes.
By default, crop boundaries are marked by the ellipsis character (`…`). You can change this by using the [`cropMarker`](#crop-marker) search parameter.
Optionally, you can indicate a custom crop length for any attributes given to `attributesToCrop`: `attributesToCrop=["attributeNameA:5", "attributeNameB:9"]`. If configured, these values have priority over `cropLength`.
Instead of supplying individual attributes, you can provide `["*"]` as a wildcard: `attributesToCrop=["*"]`. This causes `_formatted` to include the cropped values of all attributes present in [`attributesToRetrieve`](#attributes-to-retrieve).
##### Cropping algorithm
Suppose you have a field containing the following string: `Donatello is a skilled and smart turtle. Leonardo is the most skilled turtle. Raphael is the strongest turtle.`
Meilisearch tries to respect sentence boundaries when cropping. For example, if your search term is `Leonardo` and your `cropLength` is 6, Meilisearch will prioritize keeping the sentence together and return: `Leonardo is the most skilled turtle.`
If a query contains only a single search term, Meilisearch crops around the first occurrence of that term. If you search for `turtle` and your `cropLength` is 7, Meilisearch will return the first instance of that word: `Donatello is a skilled and smart turtle.`
If a query contains multiple search terms, Meilisearch centers the crop around the largest number of unique matches, giving priority to terms that are closer to each other and follow the original query order. If you search for `skilled turtle` with a `cropLength` of 6, Meilisearch will return `Leonardo is the most skilled turtle`.
If Meilisearch does not find any query terms in a field, cropping begins at the first word in that field. If you search for `Michelangelo` with a `cropLength` of 4 and this string is present in another field, Meilisearch will return `Donatello is a skilled …`.
##### Example
If you use `shifu` as a search query and set the value of the `cropLength` parameter to `5`:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "shifu",
"attributesToCrop": ["overview"],
"cropLength": 5
}'
```
```js
client.index('movies').search('shifu', {
attributesToCrop: ['overview'],
cropLength: 5
})
```
```py
client.index('movies').search('shifu', {
'attributesToCrop': ['overview'],
'cropLength': 5
})
```
```php
$client->index('movies')->search('shifu', [
'attributesToCrop' => ['overview'],
'cropLength' => 5
]);
```
```java
SearchRequest searchRequest =
SearchRequest.builder()
.q("shifu")
.attributesToCrop(new String[] {"overview"})
.cropLength(5)
.build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('shifu', {
attributes_to_crop: ['overview'],
crop_length: 5
})
```
```go
resp, err := client.Index("movies").Search("shifu", &meilisearch.SearchRequest{
AttributesToCrop: []string{"overview"},
CropLength: 5,
})
```
```csharp
var sq = new SearchQuery
{
AttributesToCrop = new[] {"overview"},
CropLength = 5
};
await client.Index("movies").SearchAsync("shifu", sq);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("shifu")
.with_attributes_to_crop(Selectors::Some(&[("overview", None)]))
.with_crop_length(5)
.execute()
.await
.unwrap();
// Get the formatted results
let formatted_results: Vec<&Movie> = results
.hits
.iter()
.map(|r| r.formatted_result.as_ref().unwrap())
.collect();
```
```swift
let searchParameters = SearchParameters(
query: "shifu",
attributesToCrop: ["overview"],
cropLength: 5)
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search(
'shifu', SearchQuery(attributesToCrop: ['overview'], cropLength: 5));
```
You will get the following response with the **cropped text in the `_formatted` object**:
```json
{
"id": 50393,
"title": "Kung Fu Panda Holiday",
"poster": "https://image.tmdb.org/t/p/w1280/gp18R42TbSUlw9VnXFqyecm52lq.jpg",
"overview": "The Winter Feast is Po's favorite holiday. Every year he and his father hang decorations, cook together, and serve noodle soup to the villagers. But this year Shifu informs Po that as Dragon Warrior, it is his duty to host the formal Winter Feast at the Jade Palace. Po is caught between his obligations as the Dragon Warrior and his family traditions: between Shifu and Mr. Ping.",
"release_date": 1290729600,
"_formatted": {
"id": 50393,
"title": "Kung Fu Panda Holiday",
"poster": "https://image.tmdb.org/t/p/w1280/gp18R42TbSUlw9VnXFqyecm52lq.jpg",
"overview": "…this year Shifu informs Po…",
"release_date": 1290729600
}
}
```
#### Crop length
**Parameter**: `cropLength`
**Expected value**: A positive integer
**Default value**: `10`
Configures the total number of words to appear in the cropped value when using [`attributesToCrop`](#attributes-to-crop). If `attributesToCrop` is not configured, `cropLength` has no effect on the returned results.
Query terms are counted as part of the cropped value length. If `cropLength` is set to `2` and you search for one term (for example, `shifu`), the cropped field will contain two words in total (for example, `"…Shifu informs…"`).
Stop words are also counted against this number. If `cropLength` is set to `2` and you search for one term (for example, `grinch`), the cropped result may contain a stop word (for example, `"…the Grinch…"`).
If `attributesToCrop` uses the `attributeName:number` syntax to specify a custom crop length for an attribute, that value has priority over `cropLength`.
#### Crop marker
**Parameter**: `cropMarker`
**Expected value**: A string
**Default value**: `"…"`
Sets a string to mark crop boundaries when using the [`attributesToCrop`](#attributes-to-crop) parameter. The crop marker will be inserted on both sides of the crop. If `attributesToCrop` is not configured, `cropMarker` has no effect on the returned search results.
If `cropMarker` is set to `null` or an empty string, no markers will be included in the returned results.
Crop markers are only added where content has been removed. For example, if the cropped text includes the first word of the field value, the crop marker will not be added to the beginning of the cropped result.
##### Example
When searching for `shifu`, you can use `cropMarker` to change the default `…`:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "shifu",
"cropMarker": "[…]",
"attributesToCrop": ["overview"]
}'
```
```js
client.index('movies').search('shifu', {
attributesToCrop: ['overview'],
cropMarker: '[…]'
})
```
```py
client.index('movies').search('shifu', {
'attributesToCrop': ['overview'],
'cropMarker': '[…]'
})
```
```php
$client->index('movies')->search('shifu', [
'attributesToCrop' => ['overview'],
'cropMarker' => '[…]'
]);
```
```java
SearchRequest searchRequest =
SearchRequest.builder()
.q("shifu")
.attributesToCrop(new String[] {"overview"})
.cropMarker("[…]")
.build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('shifu', {
attributes_to_crop: ['overview'],
crop_marker: '[…]'
})
```
```go
resp, err := client.Index("movies").Search("shifu", &meilisearch.SearchRequest{
AttributesToCrop: []string{"overview"},
CropMarker: "[…]",
})
```
```csharp
var sq = new SearchQuery
{
AttributesToCrop = new[] {"overview"},
CropMarker = "[...]"
};
await client.Index("movies").SearchAsync("shifu", sq);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("shifu")
.with_attributes_to_crop(Selectors::Some(&[("overview", None)]))
.with_crop_marker("[…]")
.execute()
.await
.unwrap();
// Get the formatted results
let formatted_results: Vec<&Movie> = results
.hits
.iter()
.map(|r| r.formatted_result.as_ref().unwrap())
.collect();
```
```swift
let searchParameters = SearchParameters(
query: "shifu",
attributesToCrop: ["overview"],
cropMarker: "[…]")
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search(
'shifu',
SearchQuery(
attributesToCrop: ['overview'],
cropMarker: '[…]',
),
);
```
```json
{
"id": 50393,
"title": "Kung Fu Panda Holiday",
"poster": "https://image.tmdb.org/t/p/w1280/gp18R42TbSUlw9VnXFqyecm52lq.jpg",
"overview": "The Winter Feast is Po's favorite holiday. Every year he and his father hang decorations, cook together, and serve noodle soup to the villagers. But this year Shifu informs Po that as Dragon Warrior, it is his duty to host the formal Winter Feast at the Jade Palace. Po is caught between his obligations as the Dragon Warrior and his family traditions: between Shifu and Mr. Ping.",
"release_date": 1290729600,
"_formatted": {
"id": 50393,
"title": "Kung Fu Panda Holiday",
"poster": "https://image.tmdb.org/t/p/w1280/gp18R42TbSUlw9VnXFqyecm52lq.jpg",
"overview": "[…]But this year Shifu informs Po that as Dragon Warrior,[…]",
"release_date": 1290729600
}
}
```
#### Attributes to highlight
**Parameter**: `attributesToHighlight`
**Expected value**: An array of attributes or `["*"]`
**Default value**: `null`
Highlights matching query terms in the specified attributes. `attributesToHighlight` only works on values of the following types: string, number, array, object.
When this parameter is set, returned documents include a `_formatted` object containing the highlighted terms.
Instead of a list of attributes, you can use `["*"]`: `attributesToHighlight=["*"]`. In this case, all the attributes present in [`attributesToRetrieve`](#attributes-to-retrieve) will be assigned to `attributesToHighlight`.
By default highlighted elements are enclosed in `` and `` tags. You may change this by using the [`highlightPreTag` and `highlightPostTag` search parameters](#highlight-tags).
`attributesToHighlight` also highlights terms configured as [synonyms](/reference/api/settings#synonyms) and [stop words](/reference/api/settings#stop-words).
`attributesToHighlight` will highlight matches within all attributes added to the `attributesToHighlight` array, even if those attributes are not set as [`searchableAttributes`](/learn/relevancy/displayed_searchable_attributes#searchable-fields).
##### Example
The following query highlights matches present in the `overview` attribute:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "winter feast",
"attributesToHighlight": ["overview"]
}'
```
```js
client.index('movies').search('winter feast', {
attributesToHighlight: ['overview']
})
```
```py
client.index('movies').search('winter feast', {
'attributesToHighlight': ['overview']
})
```
```php
$client->index('movies')->search('winter feast', [
'attributesToHighlight' => ['overview']
]);
```
```java
SearchRequest searchRequest =
new SearchRequest("winter feast").setAttributesToHighlight(new String[] {"overview"});
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('winter feast', {
attributes_to_highlight: ['overview']
})
```
```go
resp, err := client.Index("movies").Search("winter feast", &meilisearch.SearchRequest{
AttributesToHighlight: []string{"overview"},
})
```
```csharp
var sq = new SearchQuery
{
AttributesToHighlight = new[] {"overview"}
};
await client.Index("movies").SearchAsync("winter feast", sq);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("winter feast")
.with_attributes_to_highlight(Selectors::Some(&["overview"]))
.execute()
.await
.unwrap();
// Get the formatted results
let formatted_results: Vec<&Movie> = results
.hits
.iter()
.map(|r| r.formatted_result.as_ref().unwrap())
.collect();
```
```swift
let searchParameters = SearchParameters(
query: "winter feast",
attributesToHighlight: ["overview"])
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search(
'winter feast', SearchQuery(attributesToHighlight: ['overview']));
```
The highlighted version of the text would then be found in the `_formatted` object included in each returned document:
```json
{
"id": 50393,
"title": "Kung Fu Panda Holiday",
"poster": "https://image.tmdb.org/t/p/w1280/gp18R42TbSUlw9VnXFqyecm52lq.jpg",
"overview": "The Winter Feast is Po's favorite holiday. Every year he and his father hang decorations, cook together, and serve noodle soup to the villagers. But this year Shifu informs Po that as Dragon Warrior, it is his duty to host the formal Winter Feast at the Jade Palace. Po is caught between his obligations as the Dragon Warrior and his family traditions: between Shifu and Mr. Ping.",
"release_date": 1290729600,
"_formatted": {
"id": 50393,
"title": "Kung Fu Panda Holiday",
"poster": "https://image.tmdb.org/t/p/w1280/gp18R42TbSUlw9VnXFqyecm52lq.jpg",
"overview": "The Winter Feast is Po's favorite holiday. Every year he and his father hang decorations, cook together, and serve noodle soup to the villagers. But this year Shifu informs Po that as Dragon Warrior, it is his duty to host the formal Winter Feast at the Jade Palace. Po is caught between his obligations as the Dragon Warrior and his family traditions: between Shifu and Mr. Ping.",
"release_date": 1290729600
}
}
```
#### Highlight tags
**Parameters**: `highlightPreTag` and `highlightPostTag`
**Expected value**: A string
**Default value**: `""` and `""` respectively
`highlightPreTag` and `highlightPostTag` configure, respectively, the strings to be inserted before and after a word highlighted by `attributesToHighlight`. If `attributesToHighlight` has not been configured, `highlightPreTag` and `highlightPostTag` have no effect on the returned search results.
It is possible to use `highlightPreTag` and `highlightPostTag` to enclose terms between any string of text, not only HTML tags: `""`, `""`, `"*"`, and `"__"` are all equally supported values.
If `highlightPreTag` or `highlightPostTag` are set to `null` or an empty string, nothing will be inserted respectively at the beginning or the end of a highlighted term.
##### Example
The following query encloses highlighted matches in `` tags with a `class` attribute:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "winter feast",
"attributesToHighlight": ["overview"],
"highlightPreTag": "",
"highlightPostTag": ""
}'
```
```js
client.index('movies').search('winter feast', {
attributesToHighlight: ['overview'],
highlightPreTag: '',
highlightPostTag: ''
})
```
```py
client.index('movies').search('winter feast', {
'attributesToHighlight': ['overview'],
'highlightPreTag': '',
'highlightPostTag': ''
})
```
```php
$client->index('movies')->search('winter feast', [
'attributesToHighlight' => ['overview'],
'highlightPreTag' => '',
'highlightPostTag' => ''
]);
```
```java
SearchRequest searchRequest =
SearchRequest.builder()
.q("winter feast")
.attributesToHighlight(new String[] {"overview"})
.highlightPreTag("")
.highlightPostTag("")
.build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('winter feast', {
attributes_to_highlight: ['overview'],
highlight_pre_tag: '',
highlight_post_tag: ''
})
```
```go
resp, err := client.Index("movies").Search("winter feast", &meilisearch.SearchRequest{
AttributesToHighlight: []string{"overview"},
HighlightPreTag: "",
HighlightPostTag: "",
})
```
```csharp
var sq = new SearchQuery
{
AttributesToHighlight = new[] {"overview"},
HighlightPreTag = "",
HighlightPostTag = ""
};
await client.Index("movies").SearchAsync("winter feast", sq);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("winter feast")
.with_attributes_to_highlight(Selectors::Some(&["overview"]))
.with_highlight_pre_tag("")
.with_highlight_post_tag("")
.execute()
.await
.unwrap();
// Get the formatted results
let formatted_results: Vec<&Movie> = results
.hits
.iter()
.map(|r| r.formatted_result.as_ref().unwrap())
.collect();
```
```swift
let searchParameters = SearchParameters(
query: "winter feast",
attributesToHighlight: ["overview"],
highlightPreTag: "",
highlightPostTag: "")
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').search(
'winter feast',
SearchQuery(
attributesToHighlight: ['overview'],
highlightPreTag: '',
highlightPostTag: '',
),
);
```
You can find the highlighted query terms inside the `_formatted` property:
```json
{
"id": 50393,
"title": "Kung Fu Panda Holiday",
"poster": "https://image.tmdb.org/t/p/w1280/gp18R42TbSUlw9VnXFqyecm52lq.jpg",
"overview": "The Winter Feast is Po's favorite holiday. Every year he and his father hang decorations, cook together, and serve noodle soup to the villagers. But this year Shifu informs Po that as Dragon Warrior, it is his duty to host the formal Winter Feast at the Jade Palace. Po is caught between his obligations as the Dragon Warrior and his family traditions: between Shifu and Mr. Ping.",
"release_date": 1290729600,
"_formatted": {
"id": 50393,
"title": "Kung Fu Panda Holiday",
"poster": "https://image.tmdb.org/t/p/w1280/gp18R42TbSUlw9VnXFqyecm52lq.jpg",
"overview": "The Winter Feast is Po's favorite holiday. Every year he and his father hang decorations, cook together, and serve noodle soup to the villagers. But this year Shifu informs Po that as Dragon Warrior, it is his duty to host the formal Winter Feast at the Jade Palace. Po is caught between his obligations as the Dragon Warrior and his family traditions: between Shifu and Mr. Ping.",
"release_date": 1290729600
}
}
```
Though it is not necessary to use `highlightPreTag` and `highlightPostTag` in conjunction, be careful to ensure tags are correctly matched. In the above example, not setting `highlightPostTag` would result in malformed HTML: `Winter Feast`.
#### Show matches position
**Parameter**: `showMatchesPosition`
**Expected value**: `true` or `false`
**Default value**: `false`
Adds a `_matchesPosition` object to the search response that contains the location of each occurrence of queried terms across all fields. This is useful when you need more control than offered by our [built-in highlighting](#attributes-to-highlight). `showMatchesPosition` only works for strings, numbers, and arrays of strings and numbers.
`showMatchesPosition` returns the location of matched query terms within all attributes, even attributes that are not set as [`searchableAttributes`](/learn/relevancy/displayed_searchable_attributes#searchable-fields).
The beginning of a matching term within a field is indicated by `start`, and its length by `length`.
`start` and `length` are measured in bytes and not the number of characters. For example, `ü` represents two bytes but one character.
##### Example
If you set `showMatchesPosition` to `true` and search for `winter feast`:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "winter feast",
"showMatchesPosition": true
}'
```
```js
client.index('movies').search('winter feast', {
showMatchesPosition: true
})
```
```py
client.index('movies').search('winter feast', {
'showMatchesPosition': True
})
```
```php
$client->index('movies')->search('winter feast', [
'attributesToHighlight' => ['overview'],
'showMatchesPosition' => true
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("winter feast").showMatchesPosition(true).build();
SearchResultPaginated searchResult = client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('winter feast', {
show_matches_position: true
})
```
```go
resp, err := client.Index("movies").Search("winter feast", &meilisearch.SearchRequest{
ShowMatchesPosition: true,
})
```
```csharp
await client.Index("movies").SearchAsync(
"winter feast",
new SearchQuery
{
ShowMatchesPosition = True,
});
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("winter feast")
.with_show_matches_position(true)
.execute()
.await
.unwrap();
// Get the matches info
let matches_position: Vec<&HashMap>> = results
.hits
.iter()
.map(|r| r.matches_position.as_ref().unwrap())
.collect();
```
```swift
let searchParameters = SearchParameters(
query: "winter feast",
showMatchesPosition: true)
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client
.index('movies')
.search('winter feast', SearchQuery(showMatchesPosition: true));
```
You would get the following response with **information about the matches in the `_matchesPosition` object**. Note how Meilisearch searches for `winter` and `feast` separately because of the whitespace:
```json
{
"id": 50393,
"title": "Kung Fu Panda Holiday",
"poster": "https://image.tmdb.org/t/p/w500/rV77WxY35LuYLOuQvBeD1nyWMuI.jpg",
"overview": "The Winter Feast is Po's favorite holiday. Every year he and his father hang decorations, cook together, and serve noodle soup to the villagers. But this year Shifu informs Po that as Dragon Warrior, it is his duty to host the formal Winter Feast at the Jade Palace. Po is caught between his obligations as the Dragon Warrior and his family traditions: between Shifu and Mr. Ping.",
"release_date": 1290729600,
"_matchesPosition": {
"overview": [
{
"start": 4,
"length": 6
},
{
"start": 11,
"length": 5
},
{
"start": 234,
"length": 6
},
{
"start": 241,
"length": 5
}
]
}
}
```
#### Sort
**Parameter**: `sort`
**Expected value**: A list of attributes written as an array or as a comma-separated string
**Default value**: `null`
Sorts search results at query time according to the specified attributes and indicated order.
Each attribute in the list must be followed by a colon (`:`) and the preferred sorting order: either ascending (`asc`) or descending (`desc`).
Attribute order is meaningful. The first attributes in a list will be given precedence over those that come later.
For example, `sort="price:asc,author:desc` will prioritize `price` over `author` when sorting results.
When using the `POST` route, `sort` expects an array of strings.
When using the `GET` route, `sort` expects the list as a comma-separated string.
[Read more about sorting search results in our dedicated guide.](/learn/filtering_and_sorting/sort_search_results)
##### Example
You can search for science fiction books ordered from cheapest to most expensive:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/books/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "science fiction",
"sort": ["price:asc"]
}'
```
```js
client.index('books').search('science fiction', {
sort: ['price:asc'],
})
```
```py
client.index('books').search('science fiction', {
'sort': ['price:asc']
})
```
```php
$client->index('books')->search('science fiction', ['sort' => ['price:asc']]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("science fiction").sort(new String[] {"price:asc"}).build();
client.index("search_parameter_guide_sort_1").search(searchRequest);
```
```ruby
client.index('books').search('science fiction', { sort: ['price:asc'] })
```
```go
resp, err := client.Index("books").Search("science fiction", &meilisearch.SearchRequest{
Sort: []string{
"price:asc",
},
})
```
```csharp
var sq = new SearchQuery
{
Sort = new[] { "price:asc" },
};
await client.Index("books").SearchAsync("science fiction", sq);
```
```rust
let results: SearchResults = client
.index("books")
.search()
.with_query("science fiction")
.with_sort(&["price:asc"])
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(
query: "science fiction",
sort: ["price:asc"]
)
client.index("books").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client
.index('books')
.search('science fiction', SearchQuery(sort: ['price:asc']));
```
##### Sorting results with `_geoPoint`
When dealing with documents containing geolocation data, you can use `_geoPoint` to sort results based on their distance from a specific geographic location.
`_geoPoint` is a sorting function that requires two floating point numbers indicating a location's latitude and longitude. You must also specify whether the sort should be ascending (`asc`) or descending (`desc`):
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/restaurants/search' \
-H 'Content-type:application/json' \
--data-binary '{ "sort": ["_geoPoint(48.8561446,2.2978204):asc"] }'
```
```js
client.index('restaurants').search('', {
sort: ['_geoPoint(48.8561446, 2.2978204):asc'],
})
```
```py
client.index('restaurants').search('', {
'sort': ['_geoPoint(48.8561446,2.2978204):asc']
})
```
```php
$client->index('restaurants')->search('', [
'sort' => ['_geoPoint(48.8561446,2.2978204):asc']
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("").sort(new String[] {"_geoPoint(48.8561446,2.2978204):asc"}).build();
client.index("restaurants").search(searchRequest);
```
```ruby
client.index('restaurants').search('', { sort: ['_geoPoint(48.8561446, 2.2978204):asc'] })
```
```go
resp, err := client.Index("restaurants").Search("", &meilisearch.SearchRequest{
Sort: []string{
"_geoPoint(48.8561446,2.2978204):asc",
},
})
```
```csharp
SearchQuery filters = new SearchQuery()
{
Sort = new string[] { "_geoPoint(48.8561446,2.2978204):asc" }
};
var restaurants = await client.Index("restaurants").SearchAsync("", filters);
```
```rust
let results: SearchResults = client
.index("restaurants")
.search()
.with_sort(&["_geoPoint(48.8561446, 2.2978204):asc"])
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(
query: "",
sort: ["_geoPoint(48.8561446, 2.2978204):asc"]
)
client.index("restaurants").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('restaurants').search(
'', SearchQuery(sort: ['_geoPoint(48.8561446, 2.2978204):asc']));
```
Queries using `_geoPoint` will always include a `geoDistance` field containing the distance in meters between the document location and the `_geoPoint`:
```json
[
{
"id": 1,
"name": "Nàpiz' Milano",
"_geo": {
"lat": 45.4777599,
"lng": 9.1967508
},
"_geoDistance": 1532
}
]
```
[You can read more about location-based sorting in our dedicated guide.](/learn/filtering_and_sorting/geosearch#sorting-results-with-_geopoint)
#### Matching strategy
**Parameter**: `matchingStrategy`
**Expected value**: `last`, `all`, or `frequency`
**Default value**: `last`
Defines the strategy used to match query terms in documents.
##### `last`
`last` returns documents containing all the query terms first. If there are not enough results containing all query terms to meet the requested `limit`, Meilisearch will remove one query term at a time, starting from the end of the query.
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "big fat liar",
"matchingStrategy": "last"
}'
```
```js
client.index('movies').search('big fat liar', {
matchingStrategy: 'last'
})
```
```py
client.index('movies').search('big fat liar', {
'matchingStrategy': 'last'
})
```
```php
$client->index('movies')->search('big fat liar', ['matchingStrategy' => 'last']);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("big fat liar").matchingStrategy(MatchingStrategy.LAST).build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('big fat liar', {
matching_strategy: 'last'
})
```
```go
resp, err := client.Index("movies").Search("big fat liar", &meilisearch.SearchRequest{
MatchingStrategy: Last,
})
```
```csharp
SearchQuery params = new SearchQuery() { MatchingStrategy = "last" };
await client.Index("movies").SearchAsync("big fat liar", params);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("big fat liar")
.with_matching_strategy(MatchingStrategies::LAST)
.execute()
.await
.unwrap();
```
```dart
await client.index('movies').search(
'big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.last));
```
With the above code sample, Meilisearch will first return documents that contain all three words. If the results don't meet the requested `limit`, it will also return documents containing only the first two terms, `big fat`, followed by documents containing only `big`.
##### `all`
`all` only returns documents that contain all query terms. Meilisearch will not match any more documents even if there aren't enough to meet the requested `limit`.
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "big fat liar",
"matchingStrategy": "all"
}'
```
```js
client.index('movies').search('big fat liar', {
matchingStrategy: 'all'
})
```
```py
client.index('movies').search('big fat liar', {
'matchingStrategy': 'all'
})
```
```php
$client->index('movies')->search('big fat liar', ['matchingStrategy' => 'all']);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("big fat liar").matchingStrategy(MatchingStrategy.ALL).build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('big fat liar', {
matching_strategy: 'all'
})
```
```go
resp, err := client.Index("movies").Search("big fat liar", &meilisearch.SearchRequest{
MatchingStrategy: All,
})
```
```csharp
SearchQuery params = new SearchQuery() { MatchingStrategy = "all" };
await client.Index("movies").SearchAsync("big fat liar", params);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("big fat liar")
.with_matching_strategy(MatchingStrategies::ALL)
.execute()
.await
.unwrap();
```
```dart
await client.index('movies').search(
'big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.all));
```
The above code sample would only return documents containing all three words.
##### `frequency`
`frequency` returns documents containing all the query terms first. If there are not enough results containing all query terms to meet the requested limit, Meilisearch will remove one query term at a time, starting with the word that is the most frequent in the dataset. `frequency` effectively gives more weight to terms that appear less frequently in a set of results.
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "white shirt",
"matchingStrategy": "frequency"
}'
```
```js
client.index('movies').search('white shirt', {
matchingStrategy: 'frequency'
})
```
```py
client.index('movies').search('big fat liar', {
'matchingStrategy': 'frequency'
})
```
```php
$client->index('movies')->search('white shirt', ['matchingStrategy' => 'frequency']);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("white shirt").matchingStrategy(MatchingStrategy.FREQUENCY).build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('white shirt', {
matching_strategy: 'frequency'
})
```
```go
client.Index("movies").Search("white shirt", &meilisearch.SearchRequest{
MatchingStrategy: Frequency,
})
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("white shirt")
.with_matching_strategy(MatchingStrategies::FREQUENCY)
.execute()
.await
.unwrap();
```
In a dataset where many documents contain the term `"shirt"`, the above code sample would prioritize documents containing `"white"`.
#### Ranking score
**Parameter**: `showRankingScore`
**Expected value**: `true` or `false`
**Default value**: `false`
Adds a global ranking score field, `_rankingScore`, to each document. The `_rankingScore` is a numeric value between `0.0` and `1.0`. The higher the `_rankingScore`, the more relevant the document.
The `sort` ranking rule does not influence the `_rankingScore`. Instead, the document order is determined by the value of the field they are sorted on.
A document's ranking score does not change based on the scores of other documents in the same index.
For example, if a document A has a score of `0.5` for a query term, this value remains constant no matter the score of documents B, C, or D.
##### Example
The code sample below returns the `_rankingScore` when searching for `dragon` in `movies`:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "dragon",
"showRankingScore": true
}'
```
```js
client.index('movies').search('dragon', {
showRankingScore: true
})
```
```py
client.index('movies').search('dragon', {
'showRankingScore': True
})
```
```php
$client->index('movies')->search('dragon', [
'showRankingScore' => true
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("dragon").showRankingScore(true).build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('dragon', {
show_ranking_score: true
})
```
```go
resp, err := client.Index("movies").Search("dragon", &meilisearch.SearchRequest{
showRankingScore: true,
})
```
```csharp
var params = new SearchQuery()
{
ShowRankingScore = true
};
await client.Index("movies").SearchAsync("dragon", params);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("dragon")
.with_show_ranking_score(true)
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(query: "dragon", showRankingScore: true)
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult.rankingScore)
case .failure(let error):
print(error)
}
}
```
```dart
await client
.index('movies')
.search('dragon', SearchQuery(showRankingScore: true));
```
```json
{
"hits": [
{
"id": 31072,
"title": "Dragon",
"overview": "In a desperate attempt to save her kingdom…",
…
"_rankingScore": 0.92
},
{
"id": 70057,
"title": "Dragon",
"overview": "A sinful martial arts expert wants…",
…
"_rankingScore": 0.91
},
…
],
…
}
```
#### Ranking score details
**Parameter**: `showRankingScoreDetails`
**Expected value**: `true` or `false`
**Default value**: `false`
Adds a detailed global ranking score field, `_rankingScoreDetails`, to each document. `_rankingScoreDetails` is an object containing a nested object for each active ranking rule.
##### Ranking score details object
Each ranking rule details its score in its own object. Fields vary per ranking rule.
###### `words`
- `order`: order in which this ranking rule was applied
- `score`: ranking score for this rule
- `matchingWords`: number of words in the query that match in the document
- `maxMatchingWords`: maximum number of words in the query that can match in the document
###### `typo`
- `order`: order in which this specific ranking rule was applied
- `score`: ranking score for this rule
- `typoCount`: number of typos corrected so that the document matches the query term
- `maxTypoCount`: maximum number of typos accepted
###### `proximity`
- `order`: order in which this ranking rule was applied
- `score`: ranking score for this rule
###### `attribute`
- `order`: order in which this ranking rule was applied
- `score`: ranking score for this rule
- `attributeRankingOrderScore`: score computed from the maximum attribute ranking order for the matching attributes
- `queryWordDistanceScore`: score computed from the distance between the position words in the query and the position of words in matched attributes
###### `exactness`
- `order`: order in which this ranking rule was applied
- `score`: ranking score for this rule
- `matchType`: either `exactMatch`, `matchesStart`, or `noExactMatch`:
- `exactMatch`: document contains an attribute matching all query terms with no other words between them and in the order they were given
- `matchesStart`: document contains an attribute with all query terms in the same order as the original query
- `noExactMatch`: document contains an attribute with at least one query term matching the original query
- `matchingWords`: the number of exact matches in an attribute when `matchType` is `noExactMatch`
- `maxMatchingWords`: the maximum number of exact matches in an attribute when `matchType` is `noExactMatch`
###### `field_name:direction`
The `sort` ranking rule does not appear as a single field in the score details object. Instead, each sorted attribute appears as its own field, followed by a colon (`:`) and the sorting direction: `attribute:direction`.
- `order`: order in which this ranking rule was applied
- `value`: value of the field used for sorting
###### `_geoPoint(lat:lng):direction`
- `order`: order in which this ranking rule was applied
- `value`: value of the field used for sorting
- `distance`: same as [_geoDistance](/learn/filtering_and_sorting/geosearch#finding-the-distance-between-a-document-and-a-_geopoint)
###### `vectorSort(target_vector)`
- `order`: order in which this specific ranking rule was applied
- `value`: vector used for sorting the document
- `similarity`: similarity score between the target vector and the value vector. 1.0 means a perfect similarity, 0.0 a perfect dissimilarity
##### Example
The code sample below returns the `_rankingScoreDetail` when searching for `dragon` in `movies`:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "dragon",
"showRankingScoreDetails": true
}'
```
```js
client.index('movies').search('dragon', { showRankingScoreDetails: true })
```
```py
client.index('movies').search('dragon', {
'showRankingScoreDetails': True
})
```
```php
$client->index('movies')->search('dragon', [
'showRankingScoreDetails' => true
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("dragon").showRankingScoreDetails(true).build();
client.index("movies").search(searchRequest);
```
```ruby
client.index('movies').search('dragon', {
show_ranking_score_details: true
})
```
```go
resp, err := client.Index("movies").Search("dragon", &meilisearch.SearchRequest{
showRankingScoreDetails: true,
})
```
```csharp
var params = new SearchQuery()
{
ShowRankingScoreDetails = true
};
await client.Index("movies").SearchAsync("dragon", params);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("dragon")
.with_show_ranking_score_details(true)
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(query: "dragon", showRankingScoreDetails: true)
let movies: Searchable = try await client.index("movies").search(searchParameters)
```
```json
{
"hits": [
{
"id": 31072,
"title": "Dragon",
"overview": "In a desperate attempt to save her kingdom…",
…
"_rankingScoreDetails": {
"words": {
"order": 0,
"matchingWords": 4,
"maxMatchingWords": 4,
"score": 1.0
},
"typo": {
"order": 2,
"typoCount": 1,
"maxTypoCount": 4,
"score": 0.75
},
"name:asc": {
"order": 1,
"value": "Dragon"
}
}
},
…
],
…
}
```
#### Ranking score threshold
**Parameter**: `rankingScoreThreshold`
**Expected value**: A number between `0.0` and `1.0`
**Default value**: `null`
Excludes results below the specified ranking score.
Excluded results do not count towards `estimatedTotalHits`, `totalHits`, and facet distribution.
For performance reasons, if the number of documents above `rankingScoreThreshold` is higher than `limit`, Meilisearch does not evaluate the ranking score of the remaining documents. Results ranking below the threshold are not immediately removed from the set of candidates. In this case, Meilisearch may overestimate the count of `estimatedTotalHits`, `totalHits` and facet distribution.
##### Example
The following query only returns results with a ranking score bigger than `0.2`:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/INDEX_NAME/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "badman",
"rankingScoreThreshold": 0.2
}'
```
```js
client.index('INDEX_NAME').search('badman', { rankingScoreThreshold: 0.2 })
```
```py
client.index('INDEX_NAME').search('badman', { 'rankingScoreThreshold': 0.2 })
```
```php
$client->index('INDEX_NAME')->search('badman', [
'rankingScoreThreshold' => 0.2
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("badman").rankingScoreThreshold(0.2).build();
client.index("INDEX_NAME").search(searchRequest);
```
```ruby
client.index('INDEX_NAME').search('badman', {
rankingScoreThreshold: 0.2
})
```
```go
client.Index("INDEX_NAME").Search("badman", &meilisearch.SearchRequest{
RankingScoreThreshold: 0.2,
})
```
```rust
let res = client
.index("INDEX_NAME")
.search()
.with_query("badman")
.with_ranking_score_threshold(0.2)
.execute()
.await
.unwrap();
```
#### Customize attributes to search on at search time
**Parameter**: `attributesToSearchOn`
**Expected value**: A list of searchable attributes written as an array
**Default value**: `["*"]`
Configures a query to only look for terms in the specified attributes.
Instead of a list of attributes, you can pass a wildcard value (`["*"]`) and `null` to `attributesToSearchOn`. In both cases, Meilisearch will search for matches in all searchable attributes.
Attributes passed to `attributesToSearchOn` must also be present in the `searchableAttributes` list.
The order of attributes in `attributesToSearchOn` does not affect relevancy.
##### Example
The following query returns documents whose `overview` includes `"adventure"`:
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/movies/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "adventure",
"attributesToSearchOn": ["overview"]
}'
```
```js
client.index('movies').search('adventure', {
attributesToSearchOn: ['overview']
})
```
```py
client.index('movies').search('adventure', {
'attributesToSearchOn': ['overview']
})
```
```php
$client->index('movies')->search('adventure', [
'attributesToSearchOn' => ['overview']
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("adventure").attributesToSearchOn(new String[] {"overview"});
client.index("movies").searchRequest(searchRequest);
```
```ruby
client.index('movies').search('adventure', {
attributes_to_search_on: ['overview']
})
```
```go
resp, err := client.Index("movies").Search("adventure", &meilisearch.SearchRequest{
AttributesToSearchOn: []string{"overview"},
})
```
```csharp
var searchQuery = new SearchQuery
{
AttributesToSearchOn = new[] { "overview" }
};
await client.Index("movies").SearchAsync("adventure", searchQuery);
```
```rust
let results: SearchResults = client
.index("movies")
.search()
.with_query("adventure")
.with_attributes_to_search_on(&["overview"])
.execute()
.await
.unwrap();
```
```swift
let searchParameters = SearchParameters(query: "adventure", attributesToSearchOn: ["overview"])
client.index("movies").search(searchParameters) { (result: Result, Swift.Error>) in
switch result {
case .success(let searchResult):
print(searchResult)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('books').facetSearch(
FacetSearchQuery(
facetQuery: 'c',
facetName: 'genres',
),
);
```
Results would not include documents containing `"adventure"` in other fields such as `title` or `genre`, even if these fields were present in the `searchableAttributes` list.
#### Hybrid search
**Parameter**: `hybrid`
**Expected value**: An object with two fields: `embedder` and `semanticRatio`
**Default value**: `null`
Configures Meilisearch to return search results based on a query's meaning and context.
`hybrid` must be an object. It accepts two fields: `embedder` and `semanticRatio`.
`embedder` must be a string indicating an embedder configured with the `/settings` endpoint. It is mandatory to specify a valid embedder when performing AI-powered searches.
`semanticRatio` must be a number between `0.0` and `1.0` indicating the proportion between keyword and semantic search results. `0.0` causes Meilisearch to only return keyword results. `1.0` causes Meilisearch to only return meaning-based results. Defaults to `0.5`.
##### Example
```bash
curl -X POST 'MEILISEARCH_URL/indexes/INDEX_NAME/search' \
-H 'content-type: application/json' \
--data-binary '{
"q": "kitchen utensils",
"hybrid": {
"semanticRatio": 0.9,
"embedder": "EMBEDDER_NAME"
}
}'
```
```js
client.index('INDEX_NAME').search('kitchen utensils', {
hybrid: {
semanticRatio: 0.9,
embedder: 'EMBEDDER_NAME'
}
})
```
```php
$client->index('INDEX_NAME')->search('kitchen utensils', [
'hybrid' => [
'semanticRatio' => 0.9,
'embedder' => 'EMBEDDER_NAME'
]
]);
```
#### Vector
**Parameter**: `vector`
**Expected value**: an array of numbers
**Default value**: `null`
Use a custom vector to perform a search query. Must be an array of numbers corresponding to the dimensions of the custom vector.
`vector` is mandatory when performing searches with `userProvided` embedders. You may also use `vector` to override an embedder's automatic vector generation.
`vector` dimensions must match the dimensions of the embedder.
If a query does not specify `q`, but contains both `vector` and `hybrid.semanticRatio` bigger than `0`, Meilisearch performs a pure semantic search.
If `q` is missing and `semanticRatio` is explicitly set to `0`, Meilisearch performs a placeholder search without any vector search results.
##### Example
```bash
curl -X POST 'MEILISEARCH_URL/indexes/INDEX_NAME/search' \
-H 'content-type: application/json' \
--data-binary '{
"vector": [0, 1, 2],
"hybrid": {
"embedder": "EMBEDDER_NAME"
}
}'
```
#### Display `_vectors` in response
**Parameter**: `retrieveVectors`
**Expected value**: `true` or `false`
**Default value**: `false`
Return document embedding data with search results. If `true`, Meilisearch will display vector data in each [document's `_vectors` field](/reference/api/documents#_vectors).
##### Example
```bash
curl -X POST 'MEILISEARCH_URL/indexes/INDEX_NAME/search' \
-H 'content-type: application/json' \
--data-binary '{
"q": "kitchen utensils",
"retrieveVectors": true,
"hybrid": {
"embedder": "EMBEDDER_NAME"
}
}'
```
```js
client.index('INDEX_NAME').search('kitchen utensils', {
retrieveVectors: true,
hybrid: {
embedder: 'EMBEDDER_NAME'
}
})
```
```php
$client->index('INDEX_NAME')->search('kitchen utensils', [
'retrieveVectors' => true,
'hybrid' => [
'embedder': 'EMBEDDER_NAME'
]
]);
```
```json
{
"hits": [
{
"id": 0,
"title": "DOCUMENT NAME",
"_vectors": {
"default": {
"embeddings": [0.1, 0.2, 0.3],
"regenerate": true
}
}
…
},
…
],
…
}
```
#### Query locales
**Parameter**: `locales`
**Expected value**: array of [supported ISO-639 locales](/reference/api/settings#localized-attributes-object)
**Default value**: `[]`
By default, Meilisearch auto-detects the language of a query. Use this parameter to explicitly state the language of a query.
In case of a mismatch between `locales` and the [localized attributes index setting](/reference/api/settings#localized-attributes), this parameter takes precedence.
`locales` and [`localizedAttributes`](/reference/api/settings#localized-attributes) have the same goal: explicitly state the language used in a search when Meilisearch's language auto-detection is not working as expected.
If you believe Meilisearch is detecting incorrect languages because of the query text, explicitly set the search language with `locales`.
If you believe Meilisearch is detecting incorrect languages because of document, explicitly set the document language at the index level with `localizedAttributes`.
For full control over the way Meilisearch detects languages during indexing and at search time, set both `locales` and `localizedAttributes`.
##### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/INDEX_NAME/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "QUERY TEXT IN JAPANESE",
"locales": ["jpn"]
}'
```
```js
client.index('INDEX_NAME').search('QUERY TEXT IN JAPANESE', { locales: ['jpn'] })
```
```py
client.index('INDEX_NAME').search('進撃の巨人', { 'locales': ['jpn'] })
```
```php
$client->index('INDEX_NAME')->search('QUERY TEXT IN JAPANESE', [
'locales' => ['jpn']
]);
```
```java
SearchRequest searchRequest = SearchRequest.builder().q("QUERY TEXT IN JAPANESE").locales(new String[]{"jpn"}).build();
client.index("INDEX_NAME").search(searchRequest);
```
```ruby
client.index('INDEX_NAME').search('進撃の巨人', { locales: ['jpn'] })
```
```go
client.index("INDEX_NAME").Search("QUERY TEXT IN JAPANESE", &meilisearch.SearchRequest{
Locates: []string{"jpn"}
})
```
```rust
let res = client
.index("books")
.search()
.with_query("進撃の巨人")
.with_locales(&["jpn"])
.execute()
.await
.unwrap();
```
```json
{
"hits": [
{
"id": 0,
"title": "DOCUMENT NAME",
"overview_jp": "OVERVIEW TEXT IN JAPANESE"
}
…
],
…
}
```
---
title: Multi-search — Meilisearch API reference
description: The /multi-search route allows you to perform multiple search queries on one or more indexes.
---
## Multi-search
The `/multi-search` route allows you to perform multiple search queries on one or more indexes by bundling them into a single HTTP request. Multi-search is also known as federated search.
### Perform a multi-search
Bundle multiple search queries in a single API request. Use this endpoint to search through multiple indexes at once.
#### Body
| Name | Type | Description |
| :------------ | :--------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`federation`** | Object | If present and not `null`, returns a single list merging all search results across all specified queries |
| **`queries`** | Array of objects | Contains the list of search queries to perform. The [`indexUid`](/learn/getting_started/indexes#index-uid) search parameter is required, all other parameters are optional |
If Meilisearch encounters an error when handling any of the queries in a multi-search request, it immediately stops processing the request and returns an error message. The returned message will only address the first error encountered.
##### `federation`
Use `federation` to receive a single list with all search results from all specified queries, in descending ranking score order. This is called federated search.
`federation` may optionally contain the following parameters:
| Parameter | Type | Default value | Description |
| :--------------------------------------------------------------------------- | :--------------- | :------------ | :-------------------------------------------------- |
| **[`offset`](/reference/api/search#offset)** | Integer | `0` | Number of documents to skip |
| **[`limit`](/reference/api/search#limit)** | Integer | `20` | Maximum number of documents returned |
| **[`facetsByIndex`](#facetsbyindex)** | Object of arrays | `null` | Display facet information for the specified indexes |
| **[`mergeFacets`](#mergefacets)** | Object | `null` | Display facet information for the specified indexes |
If `federation` is missing or `null`, Meilisearch returns a list of multiple search result objects, with each item from the list corresponding to a search query in the request.
###### `facetsByIndex`
`facetsByIndex` must be an object. Its keys must correspond to indexes in your Meilisearch project. Each key must be associated with an array of attributes in the filterable attributes list of that index:
```json
"facetsByIndex": {
"INDEX_A": ["ATTRIBUTE_X", "ATTRIBUTE_Y"],
"INDEX_B": ["ATTRIBUTE_Z"]
}
```
When you specify `facetsByIndex`, multi-search responses include an extra `facetsByIndex` field. The response's `facetsByIndex` is an object with one field for each queried index:
```json
{
"hits" [ … ],
…
"facetsByIndex": {
"INDEX_A": {
"distribution": {
"ATTRIBUTE_X": {
"KEY": ,
"KEY": ,
…
},
"ATTRIBUTE_Y": {
"KEY": ,
…
}
},
"stats": {
"KEY": {
"min": ,
"max":
}
}
},
"INDEX_B": {
…
}
}
}
```
###### `mergeFacets`
`mergeFacets` must be an object and may contain the following fields:
- `maxValuesPerFacet`: must be an integer. When specified, indicates the maximum number of returned values for a single facet. Defaults to the value assigned to [the `maxValuesPerFacet` index setting](/reference/api/settings#faceting)
When both `facetsByIndex` and `mergeFacets` are present and not null, facet information included in multi-search responses is merged across all queried indexes. Instead of `facetsByIndex`, the response includes two extra fields: `facetDistribution` and `facetStats`:
```json
{
"hits": [ … ],
…
"facetDistribution": {
"ATTRIBUTE": {
"VALUE": ,
"VALUE":
}
},
"facetStats": {
"ATTRIBUTE": {
"min": ,
"max":
}
}
}
```
###### Merge algorithm for federated searches
Federated search's merged results are returned in decreasing ranking score. To obtain the final list of results, Meilisearch compares with the following procedure:
1. Detailed ranking scores are normalized in the following way for both hits:
1. Consecutive relevancy scores (related to the rules `words`, `typo`, `attribute`, `exactness` or `vector`) are grouped in a single score for each hit
2. `sort` and `geosort` score details remain unchanged
2. Normalized detailed ranking scores are compared lexicographically for both hits:
1. If both hits have a relevancy score, then the bigger score wins. If it is a tie, move to next step
2. If one result has a relevancy score or a (geo)sort score, Meilisearch picks it
3. If both results have a sort or geosort score in the same sorting direction, then Meilisearch compares the values according to the common sort direction. The result with the value that must come first according to the common sort direction wins. If it is a tie, go to the next step
4. Compare the global ranking scores of both hits to determine which comes first, ignoring any sorting or geosorting
5. In the case of a perfect tie, documents from the query with the lowest rank in the `queries` array are preferred.
Meilisearch considers two documents the same if:
1. They come from the same index
2. And their primary key is the same
There is no way to specify that two documents should be treated as the same across multiple indexes.
##### `queries`
`queries` must be an array of objects. Each object may contain the following search parameters:
| Search parameter | Type | Default value | Description |
| :--------------------------------------------------------------------------- | :--------------- | :------------ | :-------------------------------------------------- |
| **[`federationOptions`](#federationoptions)** | Object | `null` | Configure federation settings for a specific query |
| **[`indexUid`](/learn/getting_started/indexes#index-uid)** | String | N/A | `uid` of the requested index |
| **[`q`](/reference/api/search#query-q)** | String | `""` | Query string |
| **[`offset`](/reference/api/search#offset)** | Integer | `0` | Number of documents to skip |
| **[`limit`](/reference/api/search#limit)** | Integer | `20` | Maximum number of documents returned |
| **[`hitsPerPage`](/reference/api/search#number-of-results-per-page)** | Integer | `1` | Maximum number of documents returned for a page |
| **[`page`](/reference/api/search#page)** | Integer | `1` | Request a specific page of results |
| **[`filter`](/reference/api/search#filter)** | String | `null` | Filter queries by an attribute's value |
| **[`facets`](/reference/api/search#facets)** | Array of strings | `null` | Display the count of matches per facet |
| **[`distinct`](/reference/api/search#distinct-attributes-at-search-time)** | String | `null` | Restrict search to documents with unique values of specified attribute |
| **[`attributesToRetrieve`](/reference/api/search#attributes-to-retrieve)** | Array of strings | `["*"]` | Attributes to display in the returned documents |
| **[`attributesToCrop`](/reference/api/search#attributes-to-crop)** | Array of strings | `null` | Attributes whose values have to be cropped |
| **[`cropLength`](/reference/api/search#crop-length)** | Integer | `10` | Maximum length of cropped value in words |
| **[`cropMarker`](/reference/api/search#crop-marker)** | String | `"…"` | String marking crop boundaries |
| **[`attributesToHighlight`](/reference/api/search#attributes-to-highlight)** | Array of strings | `null` | Highlight matching terms contained in an attribute |
| **[`highlightPreTag`](/reference/api/search#highlight-tags)** | String | `""` | String inserted at the start of a highlighted term |
| **[`highlightPostTag`](/reference/api/search#highlight-tags)** | String | `""` | String inserted at the end of a highlighted term |
| **[`showMatchesPosition`](/reference/api/search#show-matches-position)** | Boolean | `false` | Return matching terms location |
| **[`sort`](/reference/api/search#sort)** | Array of strings | `null` | Sort search results by an attribute's value |
| **[`matchingStrategy`](/reference/api/search#matching-strategy)** | String | `last` | Strategy used to match query terms within documents |
| **[`showRankingScore`](/reference/api/search#ranking-score)** | Boolean | `false` | Display the global ranking score of a document |
| **[`showRankingScoreDetails`](/reference/api/search#ranking-score-details)** | Boolean | `false` | Adds a detailed global ranking score field |
| **[`rankingScoreThreshold`](/reference/api/search#ranking-score-threshold)** | Number | `null` | Excludes results with low ranking scores |
| **[`attributesToSearchOn`](/reference/api/search#customize-attributes-to-search-on-at-search-time)** | Array of strings | `["*"]` | Restrict search to the specified attributes |
| **[`hybrid`](/reference/api/search#hybrid-search)** | Object | `null` | Return results based on query keywords and meaning |
| **[`vector`](/reference/api/search#vector)** | Array of numbers | `null` | Search using a custom query vector |
| **[`retrieveVectors`](/reference/api/search#display-_vectors-in-response)** | Boolean | `false` | Return document vector data |
| **[`locales`](/reference/api/search#query-locales)** | Array of strings | `null` | Explicitly specify languages used in a query |
Unless otherwise noted, search parameters for multi-search queries function exactly like [search parameters for the `/search` endpoint](/reference/api/search#search-parameters).
###### `limit`, `offset`, `hitsPerPage` and `page`
These options are not compatible with federated searches.
###### `federationOptions`
`federationOptions` must be an object. It accepts the following parameters:
- `weight`: serves as a multiplicative factor to ranking scores of search results in this specific query. If < `1.0`, the hits from this query are less likely to appear in the final results list. If > `1.0`, the hits from this query are more likely to appear in the final results list. Must be a positive floating-point number. Defaults to `1.0`
- `remote` : indicates the remote instance where Meilisearch will perform the query. Must be a string corresponding to a [remote object](/reference/api/network). Defaults to `null`
#### Response
The response to `/multi-search` queries may take different shapes depending on the type of query you're making.
##### Non-federated multi-search requests
| Name | Type | Description |
| :------------ | :--------------- | :--------------------------------------------------------------------- |
| **`results`** | Array of objects | Results of the search queries in the same order they were requested in |
Each search result object is composed of the following fields:
| Name | Type | Description |
| :----------------------- | :--------------- | :------------------------------------------------------------------------------- |
| **`indexUid`** | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
| **`hits`** | Array of objects | Results of the query |
| **`offset`** | Number | Number of documents skipped |
| **`limit`** | Number | Number of documents to take |
| **`estimatedTotalHits`** | Number | Estimated total number of matches |
| **`totalHits`** | Number | Exhaustive total number of matches |
| **`totalPages`** | Number | Exhaustive total number of search result pages |
| **`hitsPerPage`** | Number | Number of results on each page |
| **`page`** | Number | Current search results page |
| **`facetDistribution`** | Object | **[Distribution of the given facets](/reference/api/search#facetdistribution)** |
| **`facetStats`** | Object | [The numeric `min` and `max` values per facet](/reference/api/search#facetstats) |
| **`processingTimeMs`** | Number | Processing time of the query |
| **`query`** | String | Query originating the response |
##### Federated multi-search requests
Federated search requests return a single object and the following fields:
| Name | Type | Description |
| :----------------------- | :--------------- | :------------------------------------------------------------------------------- |
| **`hits`** | Array of objects | Results of the query |
| **`offset`** | Number | Number of documents skipped |
| **`limit`** | Number | Number of documents to take |
| **`estimatedTotalHits`** | Number | Estimated total number of matches |
| **`processingTimeMs`** | Number | Processing time of the query |
| **`facetsByIndex`** | Object | [Data for facets present in the search results](#facetsbyindex) |
| **`facetDistribution`** | Object | [Distribution of the given facets](#mergefacets) |
| **`facetStats`** | Object | [The numeric `min` and `max` values per facet](#mergefacets) |
| **`remoteErrors`** | Object | Indicates which remote requests failed and why |
Each result in the `hits` array contains an additional `_federation` field with the following fields:
| Name | Type | Description |
| :-------------------------- | :--------------- | :--------------------------------------------------------------------------------- |
| **`indexUid`** | String | Index of origin for this document |
| **`queriesPosition`** | Number | Array index number of the query in the request's `queries` array |
| **`remote`** | String | Remote instance of origin for this document
| **`weightedRankingScore`** | Number | The product of the _rankingScore of the hit and the weight of the query of origin. |
#### Example
##### Non-federated multi-search
```bash
curl \
-X POST 'MEILISEARCH_URL/multi-search' \
-H 'Content-Type: application/json' \
--data-binary '{
"queries": [
{
"indexUid": "movies",
"q": "pooh",
"limit": 5
},
{
"indexUid": "movies",
"q": "nemo",
"limit": 5
},
{
"indexUid": "movie_ratings",
"q": "us"
}
]
}'
```
```js
client.multiSearch({ queries: [
{
indexUid: 'movies',
q: 'pooh',
limit: 5,
},
{
indexUid: 'movies',
q: 'nemo',
limit: 5,
},
{
indexUid: 'movie_ratings',
q: 'us',
},
]})
```
```py
client.multi_search(
[
{'indexUid': 'movies', 'q': 'pooh', 'limit': 5},
{'indexUid': 'movies', 'q': 'nemo', 'limit': 5},
{'indexUid': 'movie_ratings', 'q': 'us'}
]
)
```
```php
$client->multiSearch([
(new SearchQuery())
->setIndexUid('movies')
->setQuery('pooh')
->setLimit(5),
(new SearchQuery())
->setIndexUid('movies')
->setQuery('nemo')
->setLimit(5),
(new SearchQuery())
->setIndexUid('movie_ratings')
->setQuery('us')
]);
```
```java
MultiSearchRequest multiSearchRequest = new MultiSearchRequest();
multiIndexSearch.addQuery(new IndexSearchRequest("movies").setQuery("pooh").setLimit(5));
multiIndexSearch.addQuery(new IndexSearchRequest("movies").setQuery("nemo").setLimit(5));
multiIndexSearch.addQuery(new IndexSearchRequest("movie_ratings").setQuery("us"));
client.multiSearch(multiSearchRequest);
```
```ruby
client.multi_search([
{ index_uid: 'books', q: 'prince' },
{ index_uid: 'movies', q: 'pooh', limit: 5 }
{ index_uid: 'movies', q: 'nemo', limit: 5 }
{ index_uid: 'movie_ratings', q: 'us' }
])
```
```go
client.MultiSearch(&MultiSearchRequest{
Queries: []SearchRequest{
{
IndexUID: "movies",
Query: "pooh",
Limit: 5,
},
{
IndexUID: "movies",
Query: "nemo",
Limit: 5,
},
{
IndexUID: "movie_ratings",
Query: "us",
},
},
})
```
```csharp
await client.MultiSearchAsync(new MultiSearchQuery()
{
Queries = new System.Collections.Generic.List()
{
new SearchQuery() {
IndexUid = "movies",
Q = "booh",
Limit = 5
},
new SearchQuery() {
IndexUid = "movies",
Q = "nemo",
Limit = 5
},
new SearchQuery() {
IndexUid = "movie_ratings",
Q = "us",
},
}
});
```
```rust
let movie = client.index("movie");
let movie_ratings = client.index("movie_ratings");
let search_query_1 = SearchQuery::new(&movie)
.with_query("pooh")
.with_limit(5)
.build();
let search_query_2 = SearchQuery::new(&movie)
.with_query("nemo")
.with_limit(5)
.build();
let search_query_3 = SearchQuery::new(&movie_ratings)
.with_query("us")
.build();
let response = client
.multi_search()
.with_search_query(search_query_1)
.with_search_query(search_query_2)
.with_search_query(search_query_3)
.execute::()
.await
.unwrap();
```
```dart
await client.multiSearch(MultiSearchQuery(queries: [
IndexSearchQuery(query: 'pooh', indexUid: 'movies', limit: 5),
IndexSearchQuery(query: 'nemo', indexUid: 'movies', limit: 5),
IndexSearchQuery(query: 'us', indexUid: 'movies_ratings'),
]));
```
###### Response: `200 Ok`
```json
{
"results": [
{
"indexUid": "movies",
"hits": [
{
"id": 13682,
"title": "Pooh's Heffalump Movie",
…
},
…
],
"query": "pooh",
"processingTimeMs": 26,
"limit": 5,
"offset": 0,
"estimatedTotalHits": 22
},
{
"indexUid": "movies",
"hits": [
{
"id": 12,
"title": "Finding Nemo",
…
},
…
],
"query": "nemo",
"processingTimeMs": 5,
"limit": 5,
"offset": 0,
"estimatedTotalHits": 11
},
{
"indexUid": "movie_ratings",
"hits": [
{
"id": "Us",
"director": "Jordan Peele",
…
}
],
"query": "Us",
"processingTimeMs": 0,
"limit": 20,
"offset": 0,
"estimatedTotalHits": 1
}
]
}
```
##### Federated multi-search
```bash
curl \
-X POST 'MEILISEARCH_URL/multi-search' \
-H 'Content-Type: application/json' \
--data-binary '{
"federation": {},
"queries": [
{
"indexUid": "movies",
"q": "batman"
},
{
"indexUid": "comics",
"q": "batman"
}
]
}'
```
```js
client.multiSearch({
federation: {},
queries: [
{
indexUid: 'movies',
q: 'batman',
},
{
indexUid: 'comics',
q: 'batman',
},
]
})
```
```py
client.multi_search(
[{"indexUid": "movies", "q": "batman"}, {"indexUid": "comics", "q": "batman"}],
{}
)
```
```php
$client->multiSearch([
(new SearchQuery())
->setIndexUid('movies'))
->setQuery('batman'),
(new SearchQuery())
->setIndexUid('comics')
->setQuery('batman'),
],
(new MultiSearchFederation())
);
```
```ruby
client.multi_search(
queries: [{ index_uid: 'movies', q: 'batman' }, { index_uid: 'comics', q: 'batman' }],
federation: {}
)
```
###### Response: `200 Ok`
```json
{
"hits": [
{
"id": 42,
"title": "Batman returns",
"overview": …,
"_federation": {
"indexUid": "movies",
"queriesPosition": 0
}
},
{
"comicsId": "batman-killing-joke",
"description": …,
"title": "Batman: the killing joke",
"_federation": {
"indexUid": "comics",
"queriesPosition": 1
}
},
…
],
"processingTimeMs": 0,
"limit": 20,
"offset": 0,
"estimatedTotalHits": 2,
"semanticHitCount": 0
}
```
##### Remote federated multi-search
```bash
curl \
-X POST 'MEILISEARCH_URL/multi-search' \
-H 'Content-Type: application/json' \
--data-binary '{
"federation": {},
"queries": [
{
"indexUid": "movies",
"q": "batman",
"federationOptions": {
"remote": "ms-00"
}
},
{
"indexUid": "movies",
"q": "batman",
"federationOptions": {
"remote": "ms-01"
}
}
]
}'
```
###### Response: `200 Ok`
```json
{
"hits": [
{
"id": 42,
"title": "Batman returns",
"overview": …,
"_federation": {
"indexUid": "movies",
"queriesPosition": 0,
"weightedRankingScore": 1.0,
"remote": "ms-01"
}
},
{
"id": 87,
"description": …,
"title": "Batman: the killing joke",
"_federation": {
"indexUid": "movies",
"queriesPosition": 1,
"weightedRankingScore": 0.9848484848484849,
"remote": "ms-00"
}
},
…
],
"processingTimeMs": 35,
"limit": 5,
"offset": 0,
"estimatedTotalHits": 111,
"remoteErrors": {
"ms-02": {
"message": "error sending request",
"code": "proxy_could_not_send_request",
"type": "system",
"link": "https://docs.meilisearch.com/errors#proxy_could_not_make_request"
}
}
}
```
---
title: Network — Meilisearch API reference
description: Use the `/network` route to create a network of Meilisearch instances.
---
## Network
Use the `/network` route to create a network of Meilisearch instances. This is particularly useful when used together with federated search to implement horizontal database partition strategies such as sharding.
This is an experimental feature. Use the Meilisearch Cloud UI or the experimental features endpoint to activate it:
```sh
curl \
-X PATCH 'MEILISEARCH_URL/experimental-features/' \
-H 'Content-Type: application/json' \
--data-binary '{
"network": true
}'
```
If an attribute is both:
- not on the `displayedAttributes` list
- present on the `sortableAttributes`
It is possible its value becomes publicly accessible via the `/network` endpoint.
Do not enable the `network` feature if you rely on the value of attributes not present in `displayedAttributes` to remain hidden at all times.
### The network object
```json
{
"self": "ms-00",
"remotes": {
"ms-00": {
"url": "http://ms-1235.example.meilisearch.io",
"searchApiKey": "Ecd1SDDi4pqdJD6qYLxD3y7VZAEb4d9j6LJgt4d6xas"
},
"ms-01": {
"url": "http://ms-4242.example.meilisearch.io",
"searchApiKey": "hrVu-OMcjPGElK7692K7bwriBoGyHXTMvB5NmZkMKqQ"
}
}
}
```
#### `self`
**Type**: String
**Default value**: `null`
**Description**: A string indicating the name of the current instance
#### `remotes`
**Type**: Object
**Default value**: `{}`
**Description**: An object containing [remote objects](#the-remote-object). The key of each remote object indicates the name of the remote instance
##### The remote object
```json
"ms-00": {
"url": "http://ms-1235.example.meilisearch.io",
"searchApiKey": "Ecd1SDDi4pqdJD6qYLxD3y7VZAEb4d9j6LJgt4d6xas"
}
```
###### `url`
**Type**: String
**Default value**: `null`
**Description**: URL indicating the address of a Meilisearch instance. This URL does not need to be public, but must be accessible to all instances in the network. Required
###### `searchApiKey`
**Type**: String
**Default value**: `null`
**Description**: An API key with search permissions
### Get the network object
Returns the current value of the instance's network object.
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/network'
```
##### Response: `200 Ok`
```json
{
"self": "ms-00",
"remotes": {
"ms-00": {
"url": "http://ms-1235.example.meilisearch.io",
"searchApiKey": "Ecd1SDDi4pqdJD6qYLxD3y7VZAEb4d9j6LJgt4d6xas"
},
"ms-01": {
"url": "http://ms-4242.example.meilisearch.io",
"searchApiKey": "hrVu-OMcjPGElK7692K7bwriBoGyHXTMvB5NmZkMKqQ"
}
}
}
```
### Update the network object
Update the `self` and `remotes` fields of the network object.
Updates to the network object are **partial**. Only provide the fields you intend to update. Fields not present in the payload will remain unchanged.
To reset `self` and `remotes` to their original value, set them to `null`. To remove a single `remote` from your network, set the value of its name to `null`.
#### Body
| Name | Type | Default value | Description |
| :-------------------------------- | :----- | :------------ | :---------------------------------- |
| **[`self`](#self)** | String | `null` | The name of the current instance |
| **[`remotes`](#remotes)** | String | `null` | A list of remote objects describing accessible Meilisearch instances |
#### Example
```bash
curl \
-X PATCH 'MEILISEARCH_URL/network' \
-H 'Content-Type: application/json' \
--data-binary '{
"self": "ms-00",
"remotes": {
"ms-00": {
"url": "http://INSTANCE_URL",
"searchApiKey": "INSTANCE_API_KEY"
},
"ms-01": {
"url": "http://ANOTHER_INSTANCE_URL",
"searchApiKey": "ANOTHER_INSTANCE_API_KEY"
}
}
}'
```
##### Response: `200 Ok`
```json
{
"self": "ms-00",
"remotes": {
"ms-00": {
"url": "http://INSTANCE_URL",
"searchApiKey": "INSTANCE_API_KEY"
},
"ms-01": {
"url": "http://ANOTHER_INSTANCE_URL",
"searchApiKey": "ANOTHER_INSTANCE_API_KEY"
}
}
}
```
---
title: Similar documents — Meilisearch API reference
description: The /similar route accepts one search result and uses AI-powered search to return a number of similar documents.
---
## Similar documents
The `/similar` route uses AI-powered search to return a number of documents similar to a target document.
Meilisearch exposes two routes for retrieving similar documents: `POST` and `GET`. In the majority of cases, `POST` will offer better performance and ease of use.
### Get similar documents with `POST`
Retrieve documents similar to a specific search result.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Body
| Parameter | Type | Default value | Description |
| ---------------------------------------------------------------------------- | ---------------- | ------------- | ---------------------------------------------- |
| **`id`** | String or number | `null` | Identifier of the target document (mandatory) |
| **[`embedder`](/reference/api/search#hybrid-search)** | String | `null` | Embedder to use when computing recommendations. Mandatory |
| **[`attributesToRetrieve`](/reference/api/search#attributes-to-retrieve)** | Array of strings | `["*"]` | Attributes to display in the returned documents|
| **[`offset`](/reference/api/search#offset)** | Integer | `0` | Number of documents to skip |
| **[`limit`](/reference/api/search#limit)** | Integer | `20` | Maximum number of documents returned |
| **[`filter`](/reference/api/search#filter)** | String | `null` | Filter queries by an attribute's value |
| **[`showRankingScore`](/reference/api/search#ranking-score)** | Boolean | `false` | Display the global ranking score of a document |
| **[`showRankingScoreDetails`](/reference/api/search#ranking-score-details)** | Boolean | `false` | Display detailed ranking score information |
| **[`rankingScoreThreshold`](/reference/api/search#ranking-score-threshold)** | Number | `null` | Exclude results with low ranking scores |
| **[`retrieveVectors`](/reference/api/search#display-_vectors-in-response)** | Boolean | `false` | Return document vector data |
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/INDEX_NAME/similar' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer DEFAULT_SEARCH_API_KEY' \
--data-binary '{
"id": TARGET_DOCUMENT_ID,
"embedder": "EMBEDDER_NAME"
}'
```
```js
client.index('INDEX_NAME').searchSimilarDocuments({ id: 'TARGET_DOCUMENT_ID', embedder: 'default' })
```
```py
client.index("INDEX_NAME").get_similar_documents({"id": "TARGET_DOCUMENT_ID", "embedder": "default"})
```
```php
$similarQuery = new SimilarDocumentsQuery('TARGET_DOCUMENT_ID', 'default');
$client->index('INDEX_NAME')->searchSimilarDocuments($similarQuery);
```
```java
SimilarDocumentRequest query = new SimilarDocumentRequest() .setId("143") .setEmbedder("manual"); client.index("movies").searchSimilarDocuments(query)
```
```ruby
client.index('INDEX_NAME').search_similar_documents('TARGET_DOCUMENT_ID', embedder: 'default')
```
```go
resp := new(meilisearch.SimilarDocumentResult)
client.Index("INDEX_NAME").SearchSimilarDocuments(&meilisearch.SimilarDocumentQuery{
Id: "TARGET_DOCUMENT_ID",
Embedder: "default",
}, resp)
```
##### Response: `200 OK`
```json
{
"hits": [
{
"id": "299537",
"title": "Captain Marvel"
},
{
"id": "166428",
"title": "How to Train Your Dragon: The Hidden World"
}
{
"id": "287947",
"title": "Shazam!"
}
],
"id": "23",
"processingTimeMs": 0,
"limit": 20,
"offset": 0,
"estimatedTotalHits": 3
}
```
### Get similar documents with `GET`
Retrieve documents similar to a specific search result.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Query parameters
| Parameter | Type | Default value | Description |
| ---------------------------------------------------------------------------- | ---------------- | ------------- | ---------------------------------------------- |
| **`id`** | String or number | `null` | Identifier of the target document (mandatory) |
| **[`embedder`](/reference/api/search#hybrid-search)** | String | `"default"` | Embedder to use when computing recommendations. Mandatory |
| **[`attributesToRetrieve`](/reference/api/search#attributes-to-retrieve)** | Array of strings | `["*"]` | Attributes to display in the returned documents|
| **[`offset`](/reference/api/search#offset)** | Integer | `0` | Number of documents to skip |
| **[`limit`](/reference/api/search#limit)** | Integer | `20` | Maximum number of documents returned |
| **[`filter`](/reference/api/search#filter)** | String | `null` | Filter queries by an attribute's value |
| **[`showRankingScore`](/reference/api/search#ranking-score)** | Boolean | `false` | Display the global ranking score of a document |
| **[`showRankingScoreDetails`](/reference/api/search#ranking-score-details)** | Boolean | `false` | Display detailed ranking score information |
| **[`rankingScoreThreshold`](/reference/api/search#ranking-score-threshold)** | Number | `null` | Exclude results with low ranking scores |
| **[`retrieveVectors`](/reference/api/search#display-_vectors-in-response)** | Boolean | `false` | Return document vector data |
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/INDEX_NAME/similar?id=TARGET_DOCUMENT_ID&embedder=EMBEDDER_NAME'
```
##### Response: `200 OK`
```json
{
"hits": [
{
"id": "299537",
"title": "Captain Marvel"
},
{
"id": "166428",
"title": "How to Train Your Dragon: The Hidden World"
}
{
"id": "287947",
"title": "Shazam!"
}
],
"id": "23",
"processingTimeMs": 0,
"limit": 20,
"offset": 0,
"estimatedTotalHits": 3
}
```
---
title: Facet search — Meilisearch API reference
description: The /facet-search route allows you to search for facet values.
---
## Facet search
The `/facet-search` route allows you to search for facet values. Facet search supports [prefix search](/learn/engine/prefix) and [typo tolerance](/learn/relevancy/typo_tolerance_settings). The returned hits are sorted lexicographically in ascending order. You can configure how facets are sorted using the [`sortFacetValuesBy`](/reference/api/settings#faceting-object) property of the `faceting` index settings.
Meilisearch does not support facet search on numbers. Convert numeric facets to strings to make them searchable.
Internally, Meilisearch represents numbers as [`float64`](https://en.wikipedia.org/wiki/Double-precision_floating-point_format). This means they lack precision and can be represented in different ways, making it difficult to search facet values effectively.
### Perform a facet search
Search for a facet value within a given facet.
This endpoint will not work without first explicitly adding attributes to the [`filterableAttributes`](/reference/api/settings#update-filterable-attributes) list. [Learn more about facets in our dedicated guide.](/learn/filtering_and_sorting/search_with_facet_filters)
Meilisearch's facet search does not support multi-word facets and only considers the first term in the`facetQuery`.
For example, searching for `Jane` will return `Jane Austen`, but searching for `Austen` will not return `Jane Austen`.
#### Body
| Name | Type | Default value | Description |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`facetName`** * | String | `null` | Facet name to search values on |
| **`facetQuery`** | String | `null` | Search query for a given facet value. If `facetQuery` isn't specified, Meilisearch returns all facet values for the searched facet, limited to 100 |
| **[`q`](/reference/api/search#query-q)** | String | `""` | Query string |
| **[`filter`](/reference/api/search#filter)** | [String*](/learn/filtering_and_sorting/filter_expression_reference) | `null` | Filter queries by an attribute's value |
| **[`matchingStrategy`](/reference/api/search#matching-strategy)** | String | `"last"` | Strategy used to match query terms within documents |
| **[`attributesToSearchOn`](/reference/api/search##customize-attributes-to-search-on-at-search-time)** | Array of strings | `null` | Restrict search to the specified attributes |
| **`exhaustiveFacetCount`** | Boolean | `false` | Return an exhaustive count of facets, up to the limit defined by [`maxTotalHits`](/reference/api/settings#pagination) |
#### Response
| Name | Type | Description |
| :--------------------- | :------ | :------------------------------------------------------ |
| **`facetHits.value`** | String | Facet value matching the `facetQuery` |
| **`facetHits.count`** | Integer | Number of documents with a facet value matching `value` |
| **`facetQuery`** | String | The original `facetQuery` |
| **`processingTimeMs`** | Number | Processing time of the query |
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/indexes/books/facet-search' \
-H 'Content-Type: application/json' \
--data-binary '{
"facetQuery": "fiction",
"facetName": "genres",
"filter": "rating > 3"
}'
```
```js
client.index('books').searchForFacetValues({
facetQuery: 'fiction',
facetName: 'genres'
filter: 'rating > 3'
})
```
```py
client.index('books').facet_search('genres', 'fiction', {
'filter': 'rating > 3'
})
```
```php
$client->index('books')->facetSearch(
(new FacetSearchQuery())
->setFacetQuery('fiction')
->setFacetName('genres')
->setFilter(['rating > 3'])
);
```
```java
FacetSearchRequest fsr = FacetSearchRequest.builder().facetName("genres").facetQuery("fiction").filter(new String[]{"rating > 3"}).build();
client.index("books").facetSearch(fsr);
```
```ruby
client.index('books').facet_search('genres', 'fiction', filter: 'rating > 3')
```
```go
client.Index("books").FacetSearch(&meilisearch.FacetSearchRequest{
FacetQuery: "fiction",
FacetName: "genres",
Filter: "rating > 3",
})
```
```csharp
var query = new SearchFacetsQuery()
{
FacetQuery = "fiction",
Filter = "rating > 3"
};
await client.Index("books").FacetSearchAsync("genres", query);
```
```dart
await client.index('books').facetSearch(
FacetSearchQuery(
facetQuery: 'fiction',
facetName: 'genres',
filter: 'rating > 3',
),
);
```
##### Response: `200 Ok`
```json
{
"facetHits": [
{
"value": "fiction",
"count": 7
}
],
"facetQuery": "fiction",
"processingTimeMs": 0
}
```
---
title: Tasks — Meilisearch API reference
description: The /tasks route allows you to manage and monitor Meilisearch's asynchronous operations.
---
## Tasks
The `/tasks` route gives information about the progress of [asynchronous operations](/learn/async/asynchronous_operations).
### Task object
```json
{
"uid": 4,
"batchUids": 0,
"indexUid": "movie",
"status": "failed",
"type": "indexDeletion",
"canceledBy": null,
"details": {
"deletedDocuments": 0
},
"error": {
"message": "Index `movie` not found.",
"code": "index_not_found",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#index_not_found"
},
"duration": "PT0.001192S",
"enqueuedAt": "2022-08-04T12:28:15.159167Z",
"startedAt": "2022-08-04T12:28:15.161996Z",
"finishedAt": "2022-08-04T12:28:15.163188Z"
}
```
#### `uid`
**Type**: Integer
**Description**: Unique sequential identifier of the task.
The task `uid` is incremented across all indexes in an instance.
#### `batchUid`
**Type**: Integer
**Description**: Unique sequential identifier of the batch this task belongs to.
The batch `uid` is incremented across all indexes in an instance.
#### `indexUid`
**Type**: String
**Description**: Unique identifier of the targeted index
This value is always `null` for [global tasks](/learn/async/asynchronous_operations#global-tasks).
#### `status`
**Type**: String
**Description**: Status of the task. Possible values are `enqueued`, `processing`, `succeeded`, `failed`, and `canceled`
#### `type`
**Type**: String
**Description**: Type of operation performed by the task. Possible values are `indexCreation`, `indexUpdate`, `indexDeletion`, `indexSwap`, `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `dumpCreation`, `taskCancelation`, `taskDeletion`, and `snapshotCreation`
#### `canceledBy`
**Type**: Integer
**Description**: If the task was canceled, `canceledBy` contains the `uid` of a `taskCancelation` task. If the task was not canceled, `canceledBy` is always `null`
#### `details`
**Type**: Object
**Description**: Detailed information on the task payload. This object's contents depend on the task's `type`
##### `documentAdditionOrUpdate`
| Name | Description |
| :---------------------- | :-------------------------------------------------------------------------------------- |
| **`receivedDocuments`** | Number of documents received |
| **`indexedDocuments`** | Number of documents indexed. `null` while the task status is `enqueued` or `processing` |
##### `documentDeletion`
| Name | Description |
| :--------------------- | :-------------------------------------------------------------------------------------- |
| **`providedIds`** | Number of documents queued for deletion |
| **`originalFilter`** | The filter used to delete documents. `null` if it was not specified |
| **`deletedDocuments`** | Number of documents deleted. `null` while the task status is `enqueued` or `processing` |
##### `indexCreation`
| Name | Description |
| :--------------- | :--------------------------------------------------------------------------------------------- |
| **`primaryKey`** | Value of the `primaryKey` field supplied during index creation. `null` if it was not specified |
##### `indexUpdate`
| Name | Description |
| :--------------- | :------------------------------------------------------------------------------------------- |
| **`primaryKey`** | Value of the `primaryKey` field supplied during index update. `null` if it was not specified |
##### `indexDeletion`
| Name | Description |
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`deletedDocuments`** | Number of deleted documents. This should equal the total number of documents in the deleted index. `null` while the task status is `enqueued` or `processing` |
##### `indexSwap`
| Name | Description |
| :---------- | :----------------------------------------------------- |
| **`swaps`** | Object containing the payload for the `indexSwap` task |
##### `settingsUpdate`
| Name | Description |
| :------------------------- | :---------------------------- |
| **`rankingRules`** | List of ranking rules |
| **`filterableAttributes`** | List of filterable attributes |
| **`distinctAttribute`** | The distinct attribute |
| **`searchableAttributes`** | List of searchable attributes |
| **`displayedAttributes`** | List of displayed attributes |
| **`sortableAttributes`** | List of sortable attributes |
| **`stopWords`** | List of stop words |
| **`synonyms`** | List of synonyms |
| **`typoTolerance`** | The `typoTolerance` object |
| **`pagination`** | The `pagination` object |
| **`faceting`** | The `faceting` object |
##### `dumpCreation`
| Name | Description |
| :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`dumpUid`** | The generated `uid` of the dump. This is also the name of the generated dump file. `null` when the task status is `enqueued`, `processing`, `canceled`, or `failed` |
##### `taskCancelation`
| Name | Description |
| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`matchedTasks`** | The number of matched tasks. If the API key used for the request doesn’t have access to an index, tasks relating to that index will not be included in `matchedTasks` |
| **`canceledTasks`** | The number of tasks successfully canceled. If the task cancellation fails, this will be `0`. `null` when the task status is `enqueued` or `processing` |
| **`originalFilter`** | The filter used in the [cancel task](#cancel-tasks) request |
Task cancellation can be successful and still have `canceledTasks: 0`. This happens when `matchedTasks` matches finished tasks (`succeeded`, `failed`, or `canceled`).
##### `taskDeletion`
| Name | Description |
| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`matchedTasks`** | The number of matched tasks. If the API key used for the request doesn’t have access to an index, tasks relating to that index will not be included in `matchedTasks` |
| **`deletedTasks`** | The number of tasks successfully deleted. If the task deletion fails, this will be `0`. `null` when the task status is `enqueued` or `processing` |
| **`originalFilter`** | The filter used in the [delete task](#delete-tasks) request |
Task deletion can be successful and still have `deletedTasks: 0`. This happens when `matchedTasks` matches `enqueued` or `processing` tasks.
##### `snapshotCreation`
The `details` object is set to `null` for `snapshotCreation` tasks.
#### `error`
**Type**: Object
**Description**: If the task has the `failed` [status](#status), then this object contains the error definition. Otherwise, set to `null`
| Name | Description |
| :------------ | :-------------------------------------------------- |
| **`message`** | A human-readable description of the error |
| **`code`** | The [error code](/reference/errors/error_codes) |
| **`type`** | The [error type](/reference/errors/overview#errors) |
| **`link`** | A link to the relevant section of the documentation |
#### `duration`
**Type**: String
**Description**: The total elapsed time the task spent in the `processing` state, in [ISO 8601](https://www.ionos.com/digitalguide/websites/web-development/iso-8601/) format
#### `enqueuedAt`
**Type**: String
**Description**: The date and time when the task was first `enqueued`, in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format
#### `startedAt`
**Type**: String
**Description**: The date and time when the task began `processing`, in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format
#### `finishedAt`
**Type**: String
**Description**: The date and time when the task finished `processing`, whether `failed`, `succeeded`, or `canceled`, in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format
#### Summarized task object
When an API request triggers an asynchronous process, Meilisearch returns a summarized task object. This object contains the following fields:
| Field | Type | Description |
| :--------------- | :------ | :---------------------------------------------------------------------------------------------------------------------------- |
| **`taskUid`** | Integer | Unique sequential identifier |
| **`indexUid`** | String | Unique index identifier (always `null` for [global tasks](/learn/async/asynchronous_operations#global-tasks)) |
| **`status`** | String | Status of the task. Value is `enqueued` |
| **`type`** | String | Type of task |
| **`enqueuedAt`** | String | Represents the date and time in the [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format when the task has been `enqueued` |
You can use this `taskUid` to get more details on [the status of the task](#get-one-task).
### Get tasks
List all tasks globally, regardless of index. The `task` objects are contained in the `results` array.
Tasks are always returned in descending order of `uid`. This means that by default, **the most recently created `task` objects appear first**.
Task results are [paginated](/learn/async/paginating_tasks) and can be [filtered](/learn/async/filtering_tasks).
#### Query parameters
| Query Parameter | Default Value | Description |
| :--------------------- | :----------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`uids`** | `*` (all uids) | [Filter tasks](/learn/async/filtering_tasks) by their `uid`. Separate multiple task `uids` with a comma (`,`) |
| **`batchUids`** | `*` (all batch uids) | [Filter tasks](/learn/async/filtering_tasks) by their `batchUid`. Separate multiple `batchUids` with a comma (`,`) |
| **`statuses`** | `*` (all statuses) | [Filter tasks](/learn/async/filtering_tasks) by their `status`. Separate multiple task `statuses` with a comma (`,`) |
| **`types`** | `*` (all types) | [Filter tasks](/learn/async/filtering_tasks) by their `type`. Separate multiple task `types` with a comma (`,`) |
| **`indexUids`** | `*` (all indexes) | [Filter tasks](/learn/async/filtering_tasks) by their `indexUid`. Separate multiple task `indexUids` with a comma (`,`). Case-sensitive |
| **`limit`** | `20` | Number of tasks to return |
| **`from`** | `uid` of the last created task | `uid` of the first task returned |
| **`reverse`** | `false` | If `true`, returns results in the reverse order, from oldest to most recent |
| **`canceledBy`** | N/A | [Filter tasks](/learn/async/filtering_tasks) by their `canceledBy` field. Separate multiple task `uids` with a comma (`,`) |
| **`beforeEnqueuedAt`** | `*` (all tasks) | [Filter tasks](/learn/async/filtering_tasks) by their `enqueuedAt` field |
| **`beforeStartedAt`** | `*` (all tasks) | [Filter tasks](/learn/async/filtering_tasks) by their `startedAt` field |
| **`beforeFinishedAt`** | `*` (all tasks) | [Filter tasks](/learn/async/filtering_tasks) by their `finishedAt` field |
| **`afterEnqueuedAt`** | `*` (all tasks) | [Filter tasks](/learn/async/filtering_tasks) by their `enqueuedAt` field |
| **`afterStartedAt`** | `*` (all tasks) | [Filter tasks](/learn/async/filtering_tasks) by their `startedAt` field |
| **`afterFinishedAt`** | `*` (all tasks) | [Filter tasks](/learn/async/filtering_tasks) by their `finishedAt` field |
#### Response
| Name | Type | Description |
| :------------ | :------ | :----------------------------------------------------------------------------------------------------------------------------- |
| **`results`** | Array | An array of [task objects](#task-object) |
| **`total`** | Integer | Total number of tasks matching the filter or query |
| **`limit`** | Integer | Number of tasks returned |
| **`from`** | Integer | `uid` of the first task returned |
| **`next`** | Integer | Value passed to `from` to view the next "page" of results. When the value of `next` is `null`, there are no more tasks to view |
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/tasks'
```
```js
client.getTasks()
```
```py
client.get_tasks()
```
```php
$client->getTasks();
```
```java
client.getTasks();
```
```ruby
client.tasks
```
```go
client.GetTasks(nil);
```
```csharp
ResourceResults taskResult = await client.GetTasksAsync();
```
```rust
let tasks: TasksResults = client
.get_tasks()
.await
.unwrap();
```
```swift
client.getTasks { (result) in
switch result {
case .success(let tasks):
print(tasks)
case .failure(let error):
print(error)
}
}
```
```dart
await client.getTasks();
```
##### Response: `200 Ok`
```json
{
"results": [
{
"uid": 1,
"indexUid": "movies_reviews",
"status": "failed",
"type": "documentAdditionOrUpdate",
"canceledBy": null,
"details": {
"receivedDocuments": 100,
"indexedDocuments": 0
},
"error": null,
"duration": null,
"enqueuedAt": "2021-08-12T10:00:00.000000Z",
"startedAt": null,
"finishedAt": null
},
{
"uid": 0,
"indexUid": "movies",
"status": "succeeded",
"type": "documentAdditionOrUpdate",
"canceledBy": null,
"details": {
"receivedDocuments": 100,
"indexedDocuments": 100
},
"error": null,
"duration": "PT16S",
"enqueuedAt": "2021-08-11T09:25:53.000000Z",
"startedAt": "2021-08-11T10:03:00.000000Z",
"finishedAt": "2021-08-11T10:03:16.000000Z"
}
],
"total": 50,
"limit": 20,
"from": 1,
"next": null
}
```
### Get one task
Get a single task.
If you try retrieving a deleted task, Meilisearch will return a [`task_not_found`](/reference/errors/error_codes#task_not_found) error.
#### Path parameters
| Name | Type | Description |
| :--------------- | :----- | :---------------------------------- |
| **`task_uid`** * | String | [`uid`](#uid) of the requested task |
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/tasks/1'
```
```js
client.getTask(1)
```
```py
client.get_task(1)
```
```php
$client->getTask(1);
```
```java
client.getTask(1);
```
```ruby
client.task(1)
```
```go
client.GetTask(1);
```
```csharp
TaskInfo task = await client.GetTaskAsync(1);
```
```rust
let task: Task = client
.get_task(1)
.await
.unwrap();
```
```swift
client.getTask(taskUid: 1) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.getTask(1);
```
##### Response: `200 Ok`
```json
{
"uid": 1,
"indexUid": "movies",
"status": "succeeded",
"type": "settingsUpdate",
"canceledBy": null,
"details": {
"rankingRules": [
"typo",
"ranking:desc",
"words",
"proximity",
"attribute",
"exactness"
]
},
"error": null,
"duration": "PT1S",
"enqueuedAt": "2021-08-10T14:29:17.000000Z",
"startedAt": "2021-08-10T14:29:18.000000Z",
"finishedAt": "2021-08-10T14:29:19.000000Z"
}
```
### Cancel tasks
Cancel any number of `enqueued` or `processing` tasks based on their `uid`, `status`, `type`, `indexUid`, or the date at which they were enqueued (`enqueuedAt`) or processed (`startedAt`).
Task cancellation is an atomic transaction: **either all tasks are successfully canceled or none are**.
To prevent users from accidentally canceling all enqueued and processing tasks, Meilisearch throws the [`missing_task_filters`](/reference/errors/error_codes#missing_task_filters) error if this route is used without any filters (`POST /tasks/cancel`).
You can also cancel `taskCancelation` type tasks as long as they are in the `enqueued` or `processing` state. This is possible because `taskCancelation` type tasks are processed in reverse order, such that the last one you enqueue will be processed first.
#### Query parameters
A valid `uids`, `statuses`, `types`, `indexUids`, or date(`beforeXAt` or `afterXAt`) parameter is required.
| Query Parameter | Description |
| :--------------------- | :----------------------------------------------------------------------------------------------------------------------------------- |
| **`uids`** | Cancel tasks based on `uid`. Separate multiple `uids` with a comma (`,`). Use `uids=*` for all `uids` |
| **`statuses`** | Cancel tasks based on `status`. Separate multiple `statuses` with a comma (`,`). Use `statuses=*` for all `statuses` |
| **`types`** | Cancel tasks based on `type`. Separate multiple `types` with a comma (`,`). Use `types=*` for all `types` |
| **`indexUids`** | Cancel tasks based on `indexUid`. Separate multiple `uids` with a comma (`,`). Use `indexUids=*` for all `indexUids`. Case-sensitive |
| **`beforeEnqueuedAt`** | Cancel tasks **before** a specified `enqueuedAt` date. Use `beforeEnqueuedAt=*` to cancel all tasks |
| **`beforeStartedAt`** | Cancel tasks **before** a specified `startedAt` date. Use `beforeStartedAt=*` to cancel all tasks |
| **`afterEnqueuedAt`** | Cancel tasks **after** a specified `enqueuedAt` date. Use `afterEnqueuedAt=*` to cancel all tasks |
| **`afterStartedAt`** | Cancel tasks **after** a specified `startedAt` date. Use `afterStartedAt=*` to cancel all tasks |
Date filters are equivalent to `<` or `>` operations. At this time, there is no way to perform a `≤` or `≥` operations with a date filter.
[To learn more about filtering tasks, refer to our dedicated guide.](/learn/async/filtering_tasks)
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/tasks/cancel?uids=1,2'
```
```js
client.cancelTasks({ uids: [1, 2] })
```
```py
client.cancel_tasks({'uids': ['1', '2']})
```
```php
$client->cancelTasks((new CancelTasksQuery())->setUids([1, 2]));
```
```java
CancelTasksQuery query = new CancelTasksQuery().setUids(new int[] {1, 2})
client.cancelTasks(query);
```
```ruby
client.cancel_tasks(uids: [1, 2])
```
```go
client.CancelTasks(&meilisearch.CancelTasksQuery{
UIDS: []int64{1, 2},
});
```
```csharp
await client.CancelTasksAsync(new CancelTasksQuery { Uids = new List { 1, 2 } });
```
```rust
let mut query = tasks::TasksCancelQuery::new(&client);
query.with_uids([1, 2]);
let res = client.cancel_task_with(&query).await.unwrap();
```
```swift
client.cancelTasks(filter: CancelTasksQuery(uids: [1, 2])) { (result) in
switch result {
case .success(let taskInfo):
print(taskInfo)
case .failure(let error):
print(error)
}
}
```
```dart
await client.cancelTasks(params: CancelTasksQuery(uids: [1, 2]));
```
##### Response: `200 Ok`
```json
{
"taskUid": 3,
"indexUid": null,
"status": "enqueued",
"type": "taskCancelation",
"enqueuedAt": "2021-08-12T10:00:00.000000Z"
}
```
Since `taskCancelation` is a [global task](/learn/async/asynchronous_operations#global-tasks), its `indexUid` is always `null`.
You can use this `taskUid` to get more details on the [status of the task](#get-one-task).
#### Cancel all tasks
You can cancel all `processing` and `enqueued` tasks using the following filter:
The API key used must have access to all indexes (`"indexes": [*]`) and the [`task.cancel`](/reference/api/keys#actions) action.
### Delete tasks
Delete a finished (`succeeded`, `failed`, or `canceled`) task based on `uid`, `status`, `type`, `indexUid`, `canceledBy`, or date. Task deletion is an atomic transaction: **either all tasks are successfully deleted, or none are**.
To prevent users from accidentally deleting the entire task history, Meilisearch throws the [`missing_task_filters`](/reference/errors/error_codes#missing_task_filters) error if this route is used without any filters (DELETE `/tasks`).
#### Query parameters
A valid `uids`, `statuses`, `types`, `indexUids`, `canceledBy`, or date(`beforeXAt` or `afterXAt`) parameter is required.
| Query Parameter | Description |
| :--------------------- | :----------------------------------------------------------------------------------------------------------------------------------- |
| **`uids`** | Delete tasks based on `uid`. Separate multiple `uids` with a comma (`,`). Use `uids=*` for all `uids` |
| **`statuses`** | Delete tasks based on `status`. Separate multiple `statuses` with a comma (`,`). Use `statuses=*` for all `statuses` |
| **`types`** | Delete tasks based on `type`. Separate multiple `types` with a comma (`,`). Use `types=*` for all `types` |
| **`indexUids`** | Delete tasks based on `indexUid`. Separate multiple `uids` with a comma (`,`). Use `indexUids=*` for all `indexUids`. Case-sensitive |
| **`canceledBy`** | Delete tasks based on the `canceledBy` field |
| **`beforeEnqueuedAt`** | Delete tasks **before** a specified `enqueuedAt` date. Use `beforeEnqueuedAt=*` to delete all tasks |
| **`beforeStartedAt`** | Delete tasks **before** a specified `startedAt` date. Use `beforeStartedAt=*` to delete all tasks |
| **`beforeFinishedAt`** | Delete tasks **before** a specified `finishedAt` date. Use `beforeFinishedAt=*` to delete all tasks |
| **`afterEnqueuedAt`** | Delete tasks **after** a specified `enqueuedAt` date. Use `afterEnqueuedAt=*` to delete all tasks |
| **`afterStartedAt`** | Delete tasks **after** a specified `startedAt` date. Use `afterStartedAt=*` to delete all tasks |
| **`afterFinishedAt`** | Delete tasks **after** a specified `finishedAt` date. Use `afterFinishedAt=*` to delete all tasks |
Date filters are equivalent to `<` or `>` operations. At this time, there is no way to perform a `≤` or `≥` operations with a date filter.
[To learn more about filtering tasks, refer to our dedicated guide.](/learn/async/filtering_tasks)
#### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/tasks?uids=1,2'
```
```js
client.deleteTasks({ uids: [1, 2] })
```
```py
client.delete_tasks({'uids': ['1', '2']})
```
```php
$client->deleteTasks((new DeleteTasksQuery())->setUids([1, 2]));
```
```java
DeleteTasksQuery query = new DeleteTasksQuery().setUids(new int[] {1, 2})
client.deleteTasks(query);
```
```ruby
client.delete_tasks(uids: [1, 2])
```
```go
client.DeleteTaks(&meilisearch.DeleteTasksQuery{
UIDS: []int64{1, 2},
});
```
```csharp
await client.DeleteTasksAsync(new DeleteTasksQuery { Uids = new List { 1, 2 } });
```
```rust
let mut query = tasks::TasksDeleteQuery::new(&client);
query.with_uids([1, 2]);
let res = client.delete_tasks_with(&query).await.unwrap();
```
```swift
client.deleteTasks(filter: DeleteTasksQuery(uids: [1, 2])) { (result) in
switch result {
case .success(let taskInfo):
print(taskInfo)
case .failure(let error):
print(error)
}
}
```
```dart
await client.deleteTasks(params: DeleteTasksQuery(uids: [1, 2]));
```
##### Response: `200 Ok`
```json
{
"taskUid": 3,
"indexUid": null,
"status": "enqueued",
"type": "taskDeletion",
"enqueuedAt": "2021-08-12T10:00:00.000000Z"
}
```
Since `taskDeletion` is a [global task](/learn/async/asynchronous_operations#global-tasks), its `indexUid` is always `null`.
You can use this `taskUid` to get more details on the [status of the task](#get-one-task).
#### Delete all tasks
You can delete all finished tasks by using the following filter:
The API key used must have access to all indexes (`"indexes": [*]`) and the [`task.delete`](/reference/api/keys#actions) action.
---
title: Batches — Meilisearch API reference
description: The /batches route allows you to monitor how Meilisearch is grouping and processing asynchronous operations.
---
## Batches
The `/batches` route gives information about the progress of batches of [asynchronous operations](/learn/async/asynchronous_operations).
### Batch object
```json
{
"uid": 0,
"details": {
"receivedDocuments": 6,
"indexedDocuments": 6
},
"stats": {
"totalNbTasks": 1,
"status": {
"succeeded": 1
},
"types": {
"documentAdditionOrUpdate": 1
},
"indexUids": {
"INDEX_NAME": 1
},
"progressTrace": { … },
"writeChannelCongestion": { … },
"internalDatabaseSizes": { … }
},
"duration": "PT0.250518S",
"startedAt": "2024-12-10T15:20:30.18182Z",
"finishedAt": "2024-12-10T15:20:30.432338Z",
"progress": {
"steps": [
{
"currentStep": "extracting words",
"finished": 2,
"total": 9,
},
{
"currentStep": "document",
"finished": 30546,
"total": 31944,
}
],
"percentage": 32.8471
}
}
```
#### `uid`
**Type**: Integer
**Description**: Unique sequential identifier of the batch. Starts at `0` and increases by one for every new batch.
#### `details`
**Type**: Object
**Description**: Basic information on the types tasks in a batch. Consult the [task object reference](/reference/api/tasks#details) for an exhaustive list of possible values.
#### `progress`
**Type**: Object
**Description**: Object containing two fields: `steps` and `percentage`. Once Meilisearch has fully processed a batch, its `progress` is set to `null`.
##### `steps`
Information about the current operations Meilisearch is performing in this batch. A step may consist of multiple substeps.
| Name | Description |
| :-----------------| :------------------------------------------------- |
| **`currentStep`** | A string describing the operation |
| **`total`** | The total number of operations in the step |
| **`finished`** | The number of operations Meilisearch has completed |
If Meilisearch is taking longer than expected to process a batch, monitor the `steps` array. If the `finished` field of the last item in the `steps` array does not update, Meilisearch may be stuck.
##### `percentage`
The percentage of completed operations, calculated from all current steps and substeps. This value is a rough estimate and may not always reflect the current state of the batch due to how different steps are processed more quickly than others.
#### `stats`
**Type**: Object
**Description**: Detailed information on the payload of all tasks in a batch.
##### `totalNbTasks`
Number of tasks in the batch.
##### `status`
Object listing the [status of each task](/reference/api/tasks#status) in the batch. Contains five keys whose values correspond to the number of tasks with that status.
##### `types`
List with the `types` of tasks contained in the batch.
##### `indexUids`
List of the number of tasks in the batch separated by the indexes they affect.
##### `progressTrace`
List with full paths for each operation performed in the batch, together with the processing time in human-readable format.
##### `writeChannelCongestion`
Object containing information on write operations computed during indexing. Can be useful when diagnosing performance issues associated with write speeds.
##### `internalDatabaseSizes`
Size of each internal database, including by how much it changed after a batch was processed.
#### `duration`
**Type**: String
**Description**: The total elapsed time the batch spent in the `processing` state, in [ISO 8601](https://www.ionos.com/digitalguide/websites/web-development/iso-8601/) format. Set to `null` while the batch is processing tasks
#### `startedAt`
**Type**: String
**Description**: The date and time when the batch began `processing`, in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format
#### `finishedAt`
**Type**: String
**Description**: The date and time when the tasks finished `processing`, whether `failed`, `succeeded`, or `canceled`, in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format
### Get batches
List all batches, regardless of index. The batch objects are contained in the `results` array.
Batches are always returned in descending order of `uid`. This means that by default, **the most recently created batch objects appear first**.
Batch results are [paginated](/learn/async/paginating_tasks) and can be [filtered](/learn/async/filtering_tasks) with query parameters.
Some query parameters for `/batches`, such as `uids` and `statuses`, target tasks instead of batches.
For example, `?uids=0` returns a batch containing the task with a `taskUid` equal to `0`, instead of a batch with a `batchUid` equal to `0`.
#### Query parameters
| Query Parameter | Default Value | Description |
| ---------------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **`uids`** | `*` (all task uids) | Select batches containing the tasks with the specified `uid`s. Separate multiple task `uids` with a comma (`,`) |
| **`batchUids`** | `*` (all batch uids) | Filter batches by their `uid`. Separate multiple batch `uids` with a comma (`,`) |
| **`indexUids`** | `*` (all indexes) | Select batches containing tasks affecting the specified indexes. Separate multiple `indexUids` with a comma (`,`) |
| **`statuses`** | `*` (all statuses) | Select batches containing tasks with the specified `status`. Separate multiple task `statuses` with a comma (`,`) |
| **`types`** | `*` (all types) | Select batches containing tasks with the specified `type`. Separate multiple task `types` with a comma (`,`) |
| **`limit`** | `20` | Number of batches to return |
| **`from`** | `uid` of the last created batch | `uid` of the first batch returned |
| **`reverse`** | `false` | If `true`, returns results in the reverse order, from oldest to most recent |
| **`beforeEnqueuedAt`** | `*` (all tasks) | Select batches containing tasks with the specified `enqueuedAt` field |
| **`beforeStartedAt`** | `*` (all tasks) | Select batches containing tasks with the specified `startedAt` field |
| **`beforeFinishedAt`** | `*` (all tasks) | Select batches containing tasks with the specified `finishedAt` field |
| **`afterEnqueuedAt`** | `*` (all tasks) | Select batches containing tasks with the specified `enqueuedAt` field |
| **`afterStartedAt`** | `*` (all tasks) | Select batches containing tasks with the specified `startedAt` field |
| **`afterFinishedAt`** | `*` (all tasks) | Select batches containing tasks with the specified `finishedAt` field |
#### Response
| Name | Type | Description |
| :------------ | :------ | :----------------------------------------------------------------------------------------------------------------------------- |
| **`results`** | Array | An array of [batch objects](#batch-object) |
| **`total`** | Integer | Total number of batches matching the filter or query |
| **`limit`** | Integer | Number of batches returned |
| **`from`** | Integer | `uid` of the first batch returned |
| **`next`** | Integer | Value passed to `from` to view the next "page" of results. When the value of `next` is `null`, there are no more tasks to view |
#### Example
```bash
curl \
-X GET 'http://MEILISEARCH_URL/batches'
```
```js
client.getBatches();
```
```py
client.get_batches()
```
```php
$client->getBatches();
```
```ruby
client.batches
```
##### Response: `200 Ok`
```json
{
"results": [
{
"uid": 2,
"details": {
"stopWords": [
"of",
"the"
]
},
"progress": null,
"stats": {
"totalNbTasks": 1,
"status": {
"succeeded": 1
},
"types": {
"settingsUpdate": 1
},
"indexUids": {
"INDEX_NAME": 1
},
"progressTrace": { … },
"writeChannelCongestion": { … },
"internalDatabaseSizes": { … }
},
"duration": "PT0.110083S",
"startedAt": "2024-12-10T15:49:04.995321Z",
"finishedAt": "2024-12-10T15:49:05.105404Z"
}
],
"total": 3,
"limit": 1,
"from": 2,
"next": 1
}
```
### Get one batch
Get a single batch.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :----------------------------------- |
| **`batch_uid`** * | String | [`uid`](#uid) of the requested batch |
#### Example
```bash
curl \
-X GET 'http://MEILISEARCH_URL/batches/BATCH_UID'
```
```js
client.getBatch(BATCH_UID);
```
```py
client.get_batch(BATCH_UID)
```
```php
$client->getBatch(BATCH_UID);
```
```ruby
client.batch(BATCH_UID)
```
##### Response: `200 Ok`
```json
{
"uid": 1,
"details": {
"receivedDocuments": 1,
"indexedDocuments": 1
},
"progress": null,
"stats": {
"totalNbTasks": 1,
"status": {
"succeeded": 1
},
"types": {
"documentAdditionOrUpdate": 1
},
"indexUids": {
"INDEX_NAME": 1
}
},
"duration": "PT0.364788S",
"startedAt": "2024-12-10T15:48:49.672141Z",
"finishedAt": "2024-12-10T15:48:50.036929Z"
}
```
---
title: Keys — Meilisearch API reference
description: The /keys route allows you to create, manage, and delete API keys.
---
## Keys
The `/keys` route allows you to create, manage, and delete API keys. To use these endpoints, you must first [set the master key](/learn/security/basic_security). Once a master key is set, you can access these endpoints by supplying it in the header of the request, or using API keys that have access to the `keys.get`, `keys.create`, `keys.update`, or `keys.delete` actions.
Accessing the `/keys` route without setting a master key will throw a [`missing_master_key`](/reference/errors/error_codes#missing_master_key) error.
### Key object
```json
{
"name": "Default Search API Key",
"description": "Use it to search from the frontend code",
"key": "0a6e572506c52ab0bd6195921575d23092b7f0c284ab4ac86d12346c33057f99",
"uid": "74c9c733-3368-4738-bbe5-1d18a5fecb37",
"actions": [
"search"
],
"indexes": [
"*"
],
"expiresAt": null,
"createdAt": "2021-08-11T10:00:00Z",
"updatedAt": "2021-08-11T10:00:00Z"
}
```
#### `name`
**Type**: String
**Default value**: `null`
**Description**: A human-readable name for the key
#### `description`
**Type**: String
**Default value**: `null`
**Description**: A description for the key. You can add any important information about the key here
#### `uid`
**Type**: String
**Default value**: N/A
**Description**: A [uuid v4](https://www.sohamkamani.com/uuid-versions-explained) to identify the API key. If not specified, it is automatically generated by Meilisearch
#### `key`
**Type**: String
**Default value**: N/A
**Description**: An alphanumeric key value generated by Meilisearch by hashing the `uid` and the master key on API key creation. Used for authorization when [making calls to a protected Meilisearch instance](/learn/security/basic_security#sending-secure-api-requests-to-meilisearch)
This value is also used as the `{key}` path variable to [update](#update-a-key), [delete](#delete-a-key), or [get](#get-one-key) a specific key.
If the master key changes, all `key` values are automatically changed.
Custom API keys are deterministic: `key` is a SHA256 hash of the `uid` and master key. To reuse custom API keys, launch the new instance with the same master key and recreate your API keys with the same `uid`.
**You cannot reuse default API keys between instances.** Meilisearch automatically generates their `uid`s the first time you launch an instance.
#### `actions`
**Type**: Array
**Default value**: N/A
**Description**: An array of API actions permitted for the key, represented as strings. API actions are only possible on authorized [`indexes`](#indexes). `["*"]` for all actions.
You can use `*` as a wildcard to access all endpoints for the `documents`, `indexes`, `tasks`, `settings`, `stats` and `dumps` actions. For example, `documents.*` gives access to all document actions.
For security reasons, we do not recommend creating keys that can perform all actions.
| Name | Description |
| :--------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`search`** | Provides access to both [`POST`](/reference/api/search#search-in-an-index-with-post) and [`GET`](/reference/api/search#search-in-an-index-with-get) search endpoints |
| **`documents.add`** | Provides access to the [add documents](/reference/api/documents#add-or-replace-documents) and [update documents](/reference/api/documents#add-or-update-documents) endpoints |
| **`documents.get`** | Provides access to the [get one document](/reference/api/documents#get-one-document), [get documents with POST](/reference/api/documents#get-documents-with-post), and [get documents with GET](/reference/api/documents#get-documents-with-get) endpoints |
| **`documents.delete`** | Provides access to the [delete one document](/reference/api/documents#delete-one-document), [delete all documents](/reference/api/documents#delete-all-documents), [batch delete](/reference/api/documents#delete-documents-by-batch), and [delete by filter](/reference/api/documents#delete-documents-by-filter) endpoints |
| **`indexes.create`** | Provides access to the [create index](/reference/api/indexes#create-an-index) endpoint |
| **`indexes.get`** | Provides access to the [get one index](/reference/api/indexes#get-one-index) and [list all indexes](/reference/api/indexes#list-all-indexes) endpoints. **Non-authorized `indexes` will be omitted from the response** |
| **`indexes.update`** | Provides access to the [update index](/reference/api/indexes#update-an-index) endpoint |
| **`indexes.delete`** | Provides access to the [delete index](/reference/api/indexes#delete-an-index) endpoint |
| **`indexes.swap`** | Provides access to the swap indexes endpoint. **Non-authorized `indexes` will not be swapped** |
| **`tasks.get`** | Provides access to the [get one task](/reference/api/tasks#get-one-task) and [get tasks](/reference/api/tasks#get-tasks) endpoints. **Tasks from non-authorized `indexes` will be omitted from the response** |
| **`tasks.cancel`** | Provides access to the [cancel tasks](/reference/api/tasks#cancel-tasks) endpoint. **Tasks from non-authorized `indexes` will not be canceled** |
| **`tasks.delete`** | Provides access to the [delete tasks](/reference/api/tasks#delete-tasks) endpoint. **Tasks from non-authorized `indexes` will not be deleted** |
| **`settings.get`** | Provides access to the [get settings](/reference/api/settings#get-settings) endpoint and equivalents for all subroutes |
| **`settings.update`** | Provides access to the [update settings](/reference/api/settings#update-settings) and [reset settings](/reference/api/settings#reset-settings) endpoints and equivalents for all subroutes |
| **`stats.get`** | Provides access to the [get stats of an index](/reference/api/stats#get-stats-of-an-index) endpoint and the [get stats of all indexes](/reference/api/stats#get-stats-of-all-indexes) endpoint. For the latter, **non-authorized `indexes` are omitted from the response** |
| **`dumps.create`** | Provides access to the [create dump](/reference/api/dump#create-a-dump) endpoint. **Not restricted by `indexes`** |
| **`snapshots.create`** | Provides access to the [create snapshot](/reference/api/snapshots#create-a-snapshot) endpoint. **Not restricted by `indexes`** |
| **`version`** | Provides access to the [get Meilisearch version](/reference/api/version#get-version-of-meilisearch) endpoint |
| **`keys.get`** | Provides access to the [get all keys](#get-all-keys) endpoint |
| **`keys.create`** | Provides access to the [create key](#create-a-key) endpoint |
| **`keys.update`** | Provides access to the [update key](#update-a-key) endpoint |
| **`keys.delete`** | Provides access to the [delete key](#delete-a-key) endpoint |
| **`network.get`** | Provides access to the [get the network object](/reference/api/network#get-the-network-object) endpoint |
| **`network.update`** | Provides access to the [update the network object](/reference/api/network#update-the-network-object) endpoint |
#### `indexes`
**Type**: Array
**Default value**: N/A
**Description**: An array of indexes the key is authorized to act on. Use`["*"]` for all indexes. Only the key's [permitted actions](#actions) can be used on these indexes.
You can also use the `*` character as a wildcard by adding it at the end of a string. This allows an API key access to all index names starting with that string. For example, using `"indexes": ["movie*"]` will give the API key access to the `movies` and `movie_ratings` indexes.
#### `expiresAt`
**Type**: String
**Default value**: N/A
**Description**: Date and time when the key will expire, represented in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. `null` if the key never expires
Once a key is past its `expiresAt` date, using it for API authorization will return an error.
#### `createdAt`
**Type**: String
**Default value**: `null`
**Description**: Date and time when the key was created, represented in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format
#### `updatedAt`
**Type**: String
**Default value**: `null`
**Description**: Date and time when the key was last updated, represented in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format
### Get all keys
Returns the 20 most recently created keys in a `results` array. **Expired keys are included in the response**, but deleted keys are not.
#### Query parameters
Results can be paginated using the `offset` and `limit` query parameters.
| Query Parameter | Default Value | Description |
| :-------------- | :------------ | :----------------------- |
| **`offset`** | `0` | Number of keys to skip |
| **`limit`** | `20` | Number of keys to return |
#### Response
| Name | Type | Description |
| :------------ | :------ | :------------------------------------- |
| **`results`** | Array | An array of [key objects](#key-object) |
| **`offset`** | Integer | Number of keys skipped |
| **`limit`** | Integer | Number of keys returned |
| **`total`** | Integer | Total number of API keys |
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/keys?limit=3' \
-H 'Authorization: Bearer MASTER_KEY'
```
```js
client.getKeys({ limit: 3 })
```
```py
client.get_keys({'limit': 3})
```
```php
$client->getKeys((new KeysQuery())->setLimit(3));
```
```java
KeysQuery query = new KeysQuery().setLimit(3);
client.getKeys(query);
```
```ruby
client.keys(limit: 3)
```
```go
client.GetKeys(&meilisearch.KeysQuery{
Limit: 3
});
```
```csharp
ResourceResults keyResult = await client.GetKeysAsync(new KeysQuery { Limit = 3 });
```
```rust
let mut query = KeysQuery::new()
.with_limit(3)
.execute(&client)
.await
.unwrap();
```
```swift
client.getKeys(params: KeysQuery(limit: 3)) { result in
switch result {
case .success(let keys):
print(keys)
case .failure(let error):
print(error)
}
}
```
```dart
await client.getKeys(params: KeysQuery(limit: 3));
```
##### Response: `200 Ok`
```json
{
"results": [
{
"name": null,
"description": "Manage documents: Products/Reviews API key",
"key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4",
"uid": "6062abda-a5aa-4414-ac91-ecd7944c0f8d",
"actions": [
"documents.add",
"documents.delete"
],
"indexes": [
"prod*",
"reviews"
],
"expiresAt": "2021-12-31T23:59:59Z",
"createdAt": "2021-10-12T00:00:00Z",
"updatedAt": "2021-10-13T15:00:00Z"
},
{
"name": "Default Search API Key",
"description": "Use it to search from the frontend code",
"key": "0a6e572506c52ab0bd6195921575d23092b7f0c284ab4ac86d12346c33057f99",
"uid": "74c9c733-3368-4738-bbe5-1d18a5fecb37",
"actions": [
"search"
],
"indexes": [
"*"
],
"expiresAt": null,
"createdAt": "2021-08-11T10:00:00Z",
"updatedAt": "2021-08-11T10:00:00Z"
},
{
"name": "Default Admin API Key",
"description": "Use it for anything that is not a search operation. Caution! Do not expose it on a public frontend",
"key": "380689dd379232519a54d15935750cc7625620a2ea2fc06907cb40ba5b421b6f",
"uid": "20f7e4c4-612c-4dd1-b783-7934cc038213",
"actions": [
"*"
],
"indexes": [
"*"
],
"expiresAt": null,
"createdAt": "2021-08-11T10:00:00Z",
"updatedAt": "2021-08-11T10:00:00Z"
}
],
"offset": 0,
"limit": 3,
"total": 7
}
```
API keys are displayed in descending order based on their `createdAt` date. This means that the most recently created keys appear first.
### Get one key
Get information on the specified key. Attempting to use this endpoint with a non-existent or deleted key will result in [an error](/reference/errors/error_codes#api_key_not_found).
#### Path parameters
A valid API `key` or `uid` is required.
| Name | Type | Description |
| :---------- | :----- | :------------------------------------------- |
| **`key`** * | String | [`key`](#key) value of the requested API key |
| **`uid`** * | String | [`uid`](#uid) of the requested API key |
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/keys/6062abda-a5aa-4414-ac91-ecd7944c0f8d' \
-H 'Authorization: Bearer MASTER_KEY'
```
```js
client.getKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d')
```
```py
client.get_key('6062abda-a5aa-4414-ac91-ecd7944c0f8d')
```
```php
$client->getKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d');
```
```java
client.getKey("6062abda-a5aa-4414-ac91-ecd7944c0f8d");
```
```ruby
client.key('6062abda-a5aa-4414-ac91-ecd7944c0f8d')
```
```go
client.GetKey("6062abda-a5aa-4414-ac91-ecd7944c0f8d")
```
```csharp
Key key = await client.GetKeyAsync("d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4");
```
```rust
let key = client
.get_key("6062abda-a5aa-4414-ac91-ecd7944c0f8d")
.await
.unwrap();
```
```swift
client.getKey(keyOrUid: "6062abda-a5aa-4414-ac91-ecd7944c0f8d") { result in
switch result {
case .success(let key):
print(key)
case .failure(let error):
print(error)
}
}
```
```dart
await client.getKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d');
```
##### Response: `200 Ok`
```json
{
"name": null,
"description": "Add documents: Products API key",
"key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4",
"uid": "6062abda-a5aa-4414-ac91-ecd7944c0f8d",
"actions": [
"documents.add"
],
"indexes": [
"products"
],
"expiresAt": "2021-11-13T00:00:00Z",
"createdAt": "2021-11-12T10:00:00Z",
"updatedAt": "2021-11-12T10:00:00Z"
}
```
For an explanation of these fields, see the [key object](#key-object).
### Create a key
Create an API key with the provided description, permissions, and expiration date.
#### Body
| Name | Type | Default value | Description |
| :-------------------------------- | :----- | :------------ | :---------------------------------------------------------------------------------------------------------------------------------------------- |
| **[`actions`](#actions)** * | Array | N/A | A list of API actions permitted for the key. `["*"]` for all actions |
| **[`indexes`](#indexes)** * | Array | N/A | An array of indexes the key is authorized to act on. `["*"]` for all indexes |
| **[`expiresAt`](#expiresat)** * | String | N/A | Date and time when the key will expire, represented in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. `null` if the key never expires |
| **[`name`](#name)** | String | `null` | A human-readable name for the key |
| **[`uid`](#uid)** | String | N/A | A [uuid v4](https://www.sohamkamani.com/uuid-versions-explained) to identify the API key. If not specified, it is generated by Meilisearch |
| **[`description`](#description)** | String | `null` | An optional description for the key |
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/keys' \
-H 'Authorization: Bearer MASTER_KEY' \
-H 'Content-Type: application/json' \
--data-binary '{
"description": "Add documents: Products API key",
"actions": ["documents.add"],
"indexes": ["products"],
"expiresAt": "2042-04-02T00:42:42Z"
}'
```
```js
client.createKey({
description: 'Add documents: Products API key',
actions: ['documents.add'],
indexes: ['products'],
expiresAt: '2021-11-13T00:00:00Z'
})
```
```py
client.create_key(options={
'description': 'Add documents: Products API key',
'actions': ['documents.add'],
'indexes': ['products'],
'expiresAt': '2042-04-02T00:42:42Z'
})
```
```php
$client->createKey([
'description' => 'Add documents: Products API key',
'actions' => ['documents.add'],
'indexes' => ['products'],
'expiresAt' => '2042-04-02T00:42:42Z',
]);
```
```java
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Date dateParsed = format.parse("2042-04-02T00:42:42Z");
Key keyInfo = new Key();
keyInfo.setDescription("Add documents: Products API key");
keyInfo.setActions(new String[] {"documents.add"});
keyInfo.setIndexes(new String[] {"products"});
keyInfo.setExpiresAt(dateParsed);
client.createKey(keyInfo);
```
```ruby
client.create_key(
description: 'Add documents: Products API key',
actions: ['documents.add'],
indexes: ['products'],
expires_at: '2042-04-02T00:42:42Z'
)
```
```go
client.CreateKey(&meilisearch.Key{
Description: "Add documents: Products API key",
Actions: []string{"documents.add"},
Indexes: []string{"products"},
ExpiresAt: time.Date(2042, time.April, 02, 0, 42, 42, 0, time.UTC),
})
```
```csharp
Key keyOptions = new Key
{
Description = "Add documents: Products API key",
Actions = new KeyAction[] { KeyAction.DocumentsAdd },
Indexes = new string[] { "products" },
ExpiresAt = DateTime.Parse("2042-04-02T00:42:42Z")
};
Key createdKey = await this.client.CreateKeyAsync(keyOptions);
```
```rust
let mut key_options = KeyBuilder::new();
key_options
.with_name("Add documents: Products API key")
.with_action(Action::DocumentsAdd)
.with_expires_at(time::macros::datetime!(2042 - 04 - 02 00:42:42 UTC))
.with_index("products");
let new_key = client
.create_key(key_options)
.await
.unwrap();
```
```swift
let keyParams = KeyParams(
description: "Add documents: Products API key",
actions: ["documents.add"],
indexes: ["products"],
expiresAt: "2042-04-02T00:42:42Z"
)
client.createKey(keyParams) { result in
switch result {
case .success(let key):
print(key)
case .failure(let error):
print(error)
}
}
```
```dart
await client.createKey(
description: 'Add documents: Products API key',
actions: ['documents.add'],
indexes: ['products'],
expiresAt: DateTime(2042, 04, 02));
```
##### Response: `201 Created`
```json
{
"name": null,
"description": "Manage documents: Products/Reviews API key",
"key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4",
"uid": "6062abda-a5aa-4414-ac91-ecd7944c0f8d",
"actions": [
"documents.add"
],
"indexes": [
"products"
],
"expiresAt": "2021-11-13T00:00:00Z",
"createdAt": "2021-11-12T10:00:00Z",
"updatedAt": "2021-11-12T10:00:00Z"
}
```
### Update a key
Update the `name` and `description` of an API key.
Updates to keys are **partial**. This means you should provide only the fields you intend to update, as any fields not present in the payload will remain unchanged.
#### Path parameters
A valid API `key` or `uid` is required.
| Name | Type | Description |
| :---------- | :----- | :------------------------------------------- |
| **`key`** * | String | [`key`](#key) value of the requested API key |
| **`uid`** * | String | [`uid`](#uid) of the requested API key |
#### Body
| Name | Type | Default value | Description |
| :-------------------------------- | :----- | :------------ | :---------------------------------- |
| **[`name`](#name)** | String | `null` | A human-readable name for the key |
| **[`description`](#description)** | String | `null` | An optional description for the key |
#### Example
```bash
curl \
-X PATCH 'MEILISEARCH_URL/keys/6062abda-a5aa-4414-ac91-ecd7944c0f8d' \
-H 'Authorization: Bearer MASTER_KEY' \
-H 'Content-Type: application/json' \
--data-binary '{
"name": "Products/Reviews API key",
"description": "Manage documents: Products/Reviews API key"
}'
```
```js
client.updateKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d', {
name: 'Products/Reviews API key',
description: 'Manage documents: Products/Reviews API key',
})
```
```py
client.update_key(key_or_uid='6062abda-a5aa-4414-ac91-ecd7944c0f8d',
options={
'name': 'Products/Reviews API key',
'description': 'Manage documents: Products/Reviews API key'
})
```
```php
$client->updateKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d',
[
'name' => 'Products/Reviews API key',
'description' => 'Manage documents: Products/Reviews API key'
]);
```
```java
KeyUpdate keyChanges = new KeyUpdate();
keyChanges.setName("Products/Reviews API key");
keyChanges.setDescription("Manage documents: Products/Reviews API key");
client.updateKey("6062abda-a5aa-4414-ac91-ecd7944c0f8d", keyChanges);
```
```ruby
client.update_key(
'6062abda-a5aa-4414-ac91-ecd7944c0f8d',
{
description: 'Manage documents: Products/Reviews API key',
name: 'Products/Reviews API key'
}
)
```
```go
client.UpdateKey("6062abda-a5aa-4414-ac91-ecd7944c0f8d", &meilisearch.Key{
Description: "Manage documents: Products/Reviews API key",
Actions: []string{"documents.add", "document.delete"},
Indexes: []string{"products", "reviews"},
ExpiresAt: time.Date(2042, time.April, 02, 0, 42, 42, 0, time.UTC),
})
```
```csharp
await client.UpdateKeyAsync(
"6062abda-a5aa-4414-ac91-ecd7944c0f8d",
description: "Manage documents: Products/Reviews API key",
name: "Products/Reviews API key"
)
```
```rust
let mut key = client
.get_key("6062abda-a5aa-4414-ac91-ecd7944c0f8d")
.await
.unwrap();
key
.with_description("Manage documents: Products/Reviews API key".to_string())
.with_name("Products/Reviews API key".to_string())
.update(&client)
.await
.unwrap();
```
```swift
let keyParams = KeyUpdateParams(
description: "Manage documents: Products/Reviews API key",
name: "Products/Reviews API key"
)
client.updateKey(keyOrUid: "6062abda-a5aa-4414-ac91-ecd7944c0f8d", keyParams: keyParams) { result in
switch result {
case .success(let key):
print(key)
case .failure(let error):
print(error)
}
}
```
```dart
await client.updateKey(
'6062abda-a5aa-4414-ac91-ecd7944c0f8d',
description: 'Manage documents: Products/Reviews API key',
name: 'Products/Reviews API key',
);
```
##### Response: `200 Ok`
```json
{
"name": "Products/Reviews API key",
"description": "Manage documents: Products/Reviews API key",
"key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4",
"uid": "6062abda-a5aa-4414-ac91-ecd7944c0f8d",
"actions": [
"documents.add",
"documents.delete"
],
"indexes": [
"products",
"reviews"
],
"expiresAt": "2021-12-31T23:59:59Z",
"createdAt": "2021-10-12T00:00:00Z",
"updatedAt": "2021-10-13T15:00:00Z"
}
```
### Delete a key
Delete the specified API key.
#### Path parameters
A valid API `key` or `uid` is required.
| Name | Type | Description |
| :---------- | :----- | :------------------------------------------- |
| **`key`** * | String | [`key`](#key) value of the requested API key |
| **`uid`** * | String | [`uid`](#uid) of the requested API key |
#### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/keys/6062abda-a5aa-4414-ac91-ecd7944c0f8d' \
-H 'Authorization: Bearer MASTER_KEY'
```
```js
client.deleteKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d')
```
```py
client.delete_key('6062abda-a5aa-4414-ac91-ecd7944c0f8d')
```
```php
$client->deleteKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d');
```
```java
client.deleteKey("6062abda-a5aa-4414-ac91-ecd7944c0f8d")
```
```ruby
client.delete_key('6062abda-a5aa-4414-ac91-ecd7944c0f8d')
```
```go
client.DeleteKey("6062abda-a5aa-4414-ac91-ecd7944c0f8d")
```
```csharp
client.DeleteKeyAsync("6062abda-a5aa-4414-ac91-ecd7944c0f8d")
```
```rust
let key = client
.get_key("6062abda-a5aa-4414-ac91-ecd7944c0f8d")
.await
.unwrap();
client
.delete_key(&key)
.await?;
```
```swift
client.deleteKey(keyOrUid: "6062abda-a5aa-4414-ac91-ecd7944c0f8d") { result in
switch result {
case .success:
print("success")
case .failure(let error):
print(error)
}
}
```
```dart
await client.deleteKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d');
```
##### Response: `204 No Content`
---
title: Settings — Meilisearch API reference
description: The /settings route allows you to customize search settings for the given index.
sidebarDepth: 3
---
## Settings
Use the `/settings` route to customize search settings for a given index. You can either modify all index settings at once using the [update settings endpoint](#update-settings), or use a child route to configure a single setting.
For a conceptual overview of index settings, refer to the [indexes explanation](/learn/getting_started/indexes#index-settings). To learn more about the basics of index configuration, refer to the [index configuration tutorial](/learn/configuration/configuring_index_settings_api).
### Settings interface
[Meilisearch Cloud](https://meilisearch.com/cloud) offers a [user-friendly graphical interface for managing index settings](/learn/configuration/configuring_index_settings) in addition to the `/settings` route. The Cloud interface offers more immediate and visible feedback, and is helpful for tweaking relevancy when used in conjunction with the [search preview](/learn/getting_started/search_preview).
### Settings object
By default, the settings object looks like this. All fields are modifiable.
```json
{
"displayedAttributes": [
"*"
],
"searchableAttributes": [
"*"
],
"filterableAttributes": [],
"sortableAttributes": [],
"rankingRules":
[
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness"
],
"stopWords": [],
"nonSeparatorTokens": [],
"separatorTokens": [],
"dictionary": [],
"synonyms": {},
"distinctAttribute": null,
"typoTolerance": {
"enabled": true,
"minWordSizeForTypos": {
"oneTypo": 5,
"twoTypos": 9
},
"disableOnWords": [],
"disableOnAttributes": []
},
"faceting": {
"maxValuesPerFacet": 100
},
"pagination": {
"maxTotalHits": 1000
},
"proximityPrecision": "byWord",
"facetSearch": true,
"prefixSearch": "indexingTime",
"searchCutoffMs": null,
"embedders": {}
}
```
### All settings
This route allows you to retrieve, configure, or reset all of an index's settings at once.
#### Get settings
Get the settings of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/settings'
```
```js
client.index('movies').getSettings()
```
```py
client.index('movies').get_settings()
```
```php
$client->index('movies')->getSettings();
```
```java
client.index("movies").getSettings();
```
```ruby
client.index('movies').settings
```
```go
client.Index("movies").GetSettings()
```
```csharp
await client.Index("movies").GetSettingsAsync();
```
```rust
let settings: Settings = client
.index("movies")
.get_settings()
.await
.unwrap();
```
```swift
client.index("movies").getSettings { (result) in
switch result {
case .success(let setting):
print(setting)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').getSettings();
```
###### Response: `200 Ok`
```json
{
"displayedAttributes": [
"*"
],
"searchableAttributes": [
"*"
],
"filterableAttributes": [],
"sortableAttributes": [],
"rankingRules":
[
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness"
],
"stopWords": [],
"nonSeparatorTokens": [],
"separatorTokens": [],
"dictionary": [],
"synonyms": {},
"distinctAttribute": null,
"typoTolerance": {
"enabled": true,
"minWordSizeForTypos": {
"oneTypo": 5,
"twoTypos": 9
},
"disableOnWords": [],
"disableOnAttributes": []
},
"faceting": {
"maxValuesPerFacet": 100
},
"pagination": {
"maxTotalHits": 1000
},
"proximityPrecision": "byWord",
"facetSearch": true,
"prefixSearch": "indexingTime",
"searchCutoffMs": null,
"embedders": {}
}
```
#### Update settings
Update the settings of an index.
Passing `null` to an index setting will reset it to its default value.
Updates in the settings route are **partial**. This means that any parameters not provided in the body will be left unchanged.
If the provided index does not exist, it will be created.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
| Name | Type | Default value | Description |
| :--------------------------------------------------- | :--------------- | :----------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------- |
| **[`dictionary`](#dictionary)** | Array of strings | Empty | List of strings Meilisearch should parse as a single term |
| **[`displayedAttributes`](#displayed-attributes)** | Array of strings | All attributes: `["*"]` | Fields displayed in the returned documents |
| **[`distinctAttribute`](#distinct-attribute)** | String | `null` | Search returns documents with distinct (different) values of the given field |
| **[`faceting`](#faceting)** | Object | [Default object](#faceting-object) | Faceting settings |
| **[`filterableAttributes`](#filterable-attributes)** | Array of strings or objects | Empty | Attributes to use as filters and facets |
| **[`pagination`](#pagination)** | Object | [Default object](#pagination-object) | Pagination settings |
| **[`proximityPrecision`](#proximity-precision)** | String | `"byWord"` | Precision level when calculating the proximity ranking rule |
| **[`facetSearch`](#facet-search)** | Boolean | `true` | Enable or disable [facet search](/reference/api/facet_search) functionality |
| **[`prefixSearch`](#prefix-search)** | String | `"indexingTime"` | When Meilisearch should return results only matching the beginning of query |
| **[`rankingRules`](#ranking-rules)** | Array of strings | `["words",`
`"typo",`
`"proximity",`
`"attribute",`
`"sort",`
`"exactness"]` | List of ranking rules in order of importance |
| **[`searchableAttributes`](#searchable-attributes)** | Array of strings | All attributes: `["*"]` | Fields in which to search for matching query words sorted by order of importance |
| **[`searchCutoffMs`](#search-cutoff)** | Integer | `null`, or 1500ms | Maximum duration of a search query |
| **[`separatorTokens`](#separator-tokens)** | Array of strings | Empty | List of characters delimiting where one term begins and ends |
| **[`nonSeparatorTokens`](#non-separator-tokens)** | Array of strings | Empty | List of characters not delimiting where one term begins and ends |
| **[`sortableAttributes`](#sortable-attributes)** | Array of strings | Empty | Attributes to use when sorting search results |
| **[`stopWords`](#stop-words)** | Array of strings | Empty | List of words ignored by Meilisearch when present in search queries |
| **[`synonyms`](#synonyms)** | Object | Empty | List of associated words treated similarly |
| **[`typoTolerance`](#typo-tolerance)** | Object | [Default object](#typo-tolerance-object) | Typo tolerance settings |
| **[`embedders`](#embedders)** | Object of objects | [Default object](#embedders-object) | Embedder required for performing meaning-based search queries |
##### Example
```bash
curl \
-X PATCH 'MEILISEARCH_URL/indexes/movies/settings' \
-H 'Content-Type: application/json' \
--data-binary '{
"rankingRules": [
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:desc",
"rank:desc"
],
"distinctAttribute": "movie_id",
"searchableAttributes": [
"title",
"overview",
"genres"
],
"displayedAttributes": [
"title",
"overview",
"genres",
"release_date"
],
"stopWords": [
"the",
"a",
"an"
],
"sortableAttributes": [
"title",
"release_date"
],
"synonyms": {
"wolverine": [
"xmen",
"logan"
],
"logan": ["wolverine"]
},
"typoTolerance": {
"minWordSizeForTypos": {
"oneTypo": 8,
"twoTypos": 10
},
"disableOnAttributes": ["title"]
},
"pagination": {
"maxTotalHits": 5000
},
"faceting": {
"maxValuesPerFacet": 200
},
"searchCutoffMs": 150
}'
```
```js
client.index('movies').updateSettings({
rankingRules: [
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'release_date:desc',
'rank:desc'
],
distinctAttribute: 'movie_id',
searchableAttributes: [
'title',
'overview',
'genres'
],
displayedAttributes: [
'title',
'overview',
'genres',
'release_date'
],
stopWords: [
'the',
'a',
'an'
],
sortableAttributes: [
'title',
'release_date'
],
synonyms: {
'wolverine': ['xmen', 'logan'],
'logan': ['wolverine']
},
typoTolerance: {
'minWordSizeForTypos': {
'oneTypo': 8,
'twoTypos': 10
},
'disableOnAttributes': [
'title'
]
},
pagination: {
maxTotalHits: 5000
},
faceting: {
maxValuesPerFacet: 200
},
searchCutoffMs: 150
})
```
```py
client.index('movies').update_settings({
'rankingRules': [
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'release_date:desc',
'rank:desc'
],
'distinctAttribute': 'movie_id',
'searchableAttributes': [
'title',
'overview',
'genres'
],
'displayedAttributes': [
'title',
'overview',
'genres',
'release_date'
],
'sortableAttributes': [
'title',
'release_date'
],
'stopWords': [
'the',
'a',
'an'
],
'synonyms': {
'wolverine': ['xmen', 'logan'],
'logan': ['wolverine']
},
'typoTolerance': {
'minWordSizeForTypos': {
'oneTypo': 8,
'twoTypos': 10
},
'disableOnAttributes': ['title']
},
'pagination': {
'maxTotalHits': 5000
},
'faceting': {
'maxValuesPerFacet': 200
},
'searchCutoffMs': 150
})
```
```php
$client->index('movies')->updateSettings([
'rankingRules' => [
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'release_date:desc',
'rank:desc'
],
'distinctAttribute' => 'movie_id',
'searchableAttributes' => [
'title',
'overview',
'genres'
],
'displayedAttributes' => [
'title',
'overview',
'genres',
'release_date'
],
'stopWords' => [
'the',
'a',
'an'
],
'sortableAttributes' => [
'title',
'release_date'
],
'synonyms' => [
'wolverine' => ['xmen', 'logan'],
'logan' => ['wolverine']
],
'typoTolerance' => [
'minWordSizeForTypos' => [
'oneTypo' => 8,
'twoTypos' => 10
],
'disableOnAttributes' => ['title']
],
'pagination' => [
'maxTotalHits' => 5000
],
'faceting' => [
'maxValuesPerFacet' => 200
],
'searchCutoffMs' => 150
]);
```
```java
Settings settings = new Settings();
settings.setRankingRules(
new String[] {
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:desc",
"rank:desc"
});
settings.setDistinctAttribute("movie_id");
settings.setSearchableAttributes(
new String[] {
"title",
"overview",
"genres"
});
settings.setDisplayedAttributes(
new String[] {
"title",
"overview",
"genres",
"release_date"
});
settings.setStopWords(
new String[] {
"the",
"a",
"an"
});
settings.setSortableAttributes(
new String[] {
"title",
"release_date"
});
HashMap synonyms = new HashMap();
synonyms.put("wolverine", new String[] {"xmen", "logan"});
synonyms.put("logan", new String[] {"wolverine"});
settings.setSynonyms(synonyms);
HashMap minWordSizeTypos =
new HashMap() {
{
put("oneTypo", 8);
put("twoTypos", 10);
}
};
TypoTolerance typoTolerance = new TypoTolerance();
typoTolerance.setMinWordSizeForTypos(minWordSizeTypos);
settings.setTypoTolerance(typoTolerance);
settings.setSearchCutoffMs(150);
client.index("movies").updateSettings(settings);
```
```ruby
client.index('movies').update_settings({
ranking_rules: [
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'release_date:desc',
'rank:desc'
],
distinct_attribute: 'movie_id',
searchable_attributes: [
'title',
'overview',
'genres'
],
displayed_attributes: [
'title',
'overview',
'genres',
'release_date'
],
stop_words: [
'the',
'a',
'an'
],
sortable_attributes: [
'title',
'release_date'
],
synonyms: {
wolverine: ['xmen', 'logan'],
logan: ['wolverine']
},
pagination: {
max_total_hits: 5000
},
faceting: {
max_values_per_facet: 200
},
search_cutoff_ms: 150
})
```
```go
distinctAttribute := "movie_id"
settings := meilisearch.Settings{
RankingRules: []string{
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:desc",
"rank:desc",
},
DistinctAttribute: &distinctAttribute,
SearchableAttributes: []string{
"title",
"overview",
"genres",
},
DisplayedAttributes: []string{
"title",
"overview",
"genres",
"release_date",
},
StopWords: []string{
"the",
"a",
"an",
},
SortableAttributes: []string{
"title",
"release_date",
},
Synonyms: map[string][]string{
"wolverine": []string{"xmen", "logan"},
"logan": []string{"wolverine"},
},
TypoTolerance: &meilisearch.TypoTolerance{
MinWordSizeForTypos: meilisearch.MinWordSizeForTypos{
OneTypo: 8,
TwoTypos: 10,
},
DisableOnAttributes: []string{"title"},
},
Pagination: &meilisearch.Pagination{
MaxTotalHits: 5000,
},
Faceting: &meilisearch.Faceting{
MaxValuesPerFacet: 200,
},
SearchCutoffMs: 150,
}
client.Index("movies").UpdateSettings(&settings)
```
```csharp
Settings newSettings = new Settings
{
RankingRules = new string[]
{
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:desc",
"rank:desc"
},
DistinctAttribute = "movie_id",
SearchableAttributes = new string[] { "title", "overview", "genres" },
DisplayedAttributes = new string[] { "title", "overview", "genres", "release_date" },
SortableAttributes = new string[] { "title", "release_date" },
StopWords = new string[] { "the", "a", "an" },
Synonyms = new Dictionary>
{
{ "wolverine", new string[] { "xmen", "logan" } },
{ "logan", new string[] { "wolverine" } },
},
FilterableAttributes = new string[] { },
TypoTolerance = new TypoTolerance
{
DisableOnAttributes = new string[] { "title" },
MinWordSizeForTypos = new TypoTolerance.TypoSize
{
OneTypo = 8,
TwoTypos = 10
}
},
SearchCutoffMs = 150
};
TaskInfo task = await client.Index("movies").UpdateSettingsAsync(newFilters);
```
```rust
let mut synonyms = std::collections::HashMap::new();
synonyms.insert(String::from("wolverine"), vec!["xmen", "logan"]);
synonyms.insert(String::from("logan"), vec!["wolverine"]);
let min_word_size_for_typos = MinWordSizeForTypos {
one_typo: Some(4),
two_typos; Some(12)
}
let typo_tolerance = TypoToleranceSettings {
enabled: Some(true),
disable_on_attributes: Some(vec!["title".to_string()]),
disable_on_words: Some(vec![])
min_word_size_for_typos: Some(min_word_size_for_typos),
};
let settings = Settings::new()
.with_ranking_rules([
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:desc",
"rank:desc"
])
.with_distinct_attribute(Some("movie_id"))
.with_searchable_attributes([
"title",
"overview",
"genres"
])
.with_displayed_attributes([
"title",
"overview",
"genres",
"release_date"
])
.with_stop_words([
"the",
"a",
"an"
])
.with_sortable_attributes([
"title",
"release_date"
])
.with_synonyms(synonyms)
.with_typo_tolerance(typo_tolerance)
.with_search_cutoff(150);
let task: TaskInfo = client
.index("movies")
.set_settings(&settings)
.await
.unwrap();
```
```swift
let settings = Setting(rankingRules: [
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:desc",
"rank:desc"
], searchableAttributes: [
"title",
"overview",
"genres"
], displayedAttributes: [
"title",
"overview",
"genres",
"release_date"
], stopWords: [
"the",
"a",
"an"
], synonyms: [
"wolverine": ["xmen", "logan"],
"logan": ["wolverine"]
], distinctAttribute: "movie_id",
sortableAttributes: [
"title",
"release_date"
])
client.index("movies").updateSettings(settings) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').updateSettings(
IndexSettings(
rankingRules: [
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'release_date:desc',
'rank:desc'
],
distinctAttribute: 'movie_id',
searchableAttributes: ['title', 'overview', 'genres'],
displayedAttributes: [
'title',
'overview',
'genres',
'release_date'
],
stopWords: ['the', 'a', 'an'],
sortableAttributes: ['title', 'release_date'],
synonyms: {
'wolverine': ['xmen', 'logan'],
'logan': ['wolverine'],
},
),
);
```
If Meilisearch encounters an error when updating any of the settings in a request, it immediately stops processing the request and returns an error message. In this case, the database settings remain unchanged. The returned error message will only address the first error encountered.
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset settings
Reset all the settings of an index to their [default value](#settings-object).
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies/settings'
```
```js
client.index('movies').resetSettings()
```
```py
client.index('movies').reset_settings()
```
```php
$client->index('movies')->resetSettings();
```
```java
client.index("movies").resetSettings();
```
```ruby
client.index('movies').reset_settings
```
```go
client.Index("movies").ResetSettings()
```
```csharp
await client.Index("movies").ResetSettingsAsync();
```
```rust
let task: TaskInfo = client
.index("movies")
.reset_settings()
.await
.unwrap();
```
```swift
client.index("movies").resetSettings { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').resetSettings();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Dictionary
Allows users to instruct Meilisearch to consider groups of strings as a single term by adding a supplementary dictionary of user-defined terms.
This is particularly useful when working with datasets containing many domain-specific words, and in languages where words are not separated by whitespace such as Japanese.
Custom dictionaries are also useful in a few use-cases for space-separated languages, such as datasets with names such as `"J. R. R. Tolkien"` and `"W. E. B. Du Bois"`.
User-defined dictionaries can be used together with synonyms. It can be useful to configure Meilisearch so different spellings of an author's initials return the same results:
```json
"dictionary": ["W. E. B.", "W.E.B."],
"synonyms": {
"W. E. B.": ["W.E.B."],
"W.E.B.": ["W. E. B."]
}
```
#### Get dictionary
Get an index's user-defined dictionary.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/books/settings/dictionary'
```
```js
client.index('books').getDictionary()
```
```py
client.index('books').get_dictionary()
```
```php
$client->index('books')->getDictionary();
```
```java
client.index("books").getDictionarySettings();
```
```ruby
client.index('books').dictionary
```
```go
client.Index("books").GetDictionary()
```
```csharp
var indexDictionary = await client.Index("books").GetDictionaryAsync();
```
```rust
let task: TaskInfo = client
.index('books')
.get_dictionary()
.await
.unwrap();
```
```swift
client.index("books").getDictionary { result in
// handle result
}
```
###### Response: `200 OK`
```json
[]
```
#### Update dictionary
Update an index's user-defined dictionary.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```json
["J. R. R.", "W. E. B."]
```
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/books/settings/dictionary' \
-H 'Content-Type: application/json' \
--data-binary '[
"J. R. R.",
"W. E. B."
]'
```
```js
client.index('books').updateDictionary(['J. R. R.', 'W. E. B.'])
```
```py
client.index('books').update_dictionary(["J. R. R.", "W. E. B."])
```
```php
$client->index('books')->updateDictionary(['J. R. R.', 'W. E. B.']);
```
```java
client.index("books").updateDictionarySettings(new String[] {"J. R. R.", "W. E. B."});
```
```ruby
client.index('books').update_dictionary(['J. R. R.', 'W. E. B.'])
```
```go
client.Index("books").UpdateDictionary([]string{
"J. R. R.",
"W. E. B.",
})
```
```csharp
var newDictionary = new string[] { "J. R. R.", "W. E. B." };
await client.Index("books").UpdateDictionaryAsync(newDictionary);
```
```rust
let task: TaskInfo = client
.index('books')
.set_dictionary(['J. R. R.', 'W. E. B.'])
.await
.unwrap();
```
```swift
client.index("books").updateDictionary(["J. R. R.", "W. E. B."]) { result in
// handle result
}
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2023-09-11T15:39:06.073314Z"
}
```
Use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset dictionary
Reset an index's dictionary to its default value, `[]`.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/books/settings/dictionary'
```
```js
client.index('books').resetDictionary()
```
```py
client.index('books').reset_dictionary()
```
```php
$client->index('books')->resetDictionary();
```
```java
client.index("books").resetDictionarySettings();
```
```ruby
client.index('books').reset_dictionary
```
```go
client.Index("books").ResetDictionary()
```
```csharp
await client.Index("books").ResetDictionaryAsync();
```
```rust
let task: TaskInfo = client
.index('books')
.reset_dictionary()
.await
.unwrap();
```
```swift
client.index("books").resetDictionary { result in
// handle result
}
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2022-04-14T20:53:32.863107Z"
}
```
Use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Displayed attributes
The attributes added to the `displayedAttributes` list appear in search results. `displayedAttributes` only affects the search endpoints. It has no impact on the [get documents with POST](/reference/api/documents#get-documents-with-post) and [get documents with GET](/reference/api/documents#get-documents-with-get) endpoints.
By default, the `displayedAttributes` array is equal to all fields in your dataset. This behavior is represented by the value `["*"]`.
[To learn more about displayed attributes, refer to our dedicated guide.](/learn/relevancy/displayed_searchable_attributes#displayed-fields)
#### Get displayed attributes
Get the displayed attributes of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/settings/displayed-attributes'
```
```js
client.index('movies').getDisplayedAttributes()
```
```py
client.index('movies').get_displayed_attributes()
```
```php
$client->index('movies')->getDisplayedAttributes();
```
```java
client.index("movies").getDisplayedAttributesSettings();
```
```ruby
client.index('movies').get_displayed_attributes
```
```go
client.Index("movies").GetDisplayedAttributes()
```
```csharp
await client.Index("movies").GetDisplayedAttributesAsync();
```
```rust
let displayed_attributes: Vec = client
.index("movies")
.get_displayed_attributes()
.await
.unwrap();
```
```swift
client.index("movies").getDisplayedAttributes { (result) in
switch result {
case .success(let displayedAttributes):
print(displayedAttributes)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').getDisplayedAttributes();
```
###### Response: `200 Ok`
```json
[
"title",
"overview",
"genres",
"release_date.year"
]
```
#### Update displayed attributes
Update the displayed attributes of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
[, , …]
```
An array of strings. Each string should be an attribute that exists in the selected index.
If an attribute contains an object, you can use dot notation to specify one or more of its keys, for example, `"displayedAttributes": ["release_date.year"]`.
If the field does not exist, no error will be thrown.
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/movies/settings/displayed-attributes' \
-H 'Content-Type: application/json' \
--data-binary '[
"title",
"overview",
"genres",
"release_date"
]'
```
```js
client.index('movies').updateDisplayedAttributes([
'title',
'overview',
'genres',
'release_date'
])
```
```py
client.index('movies').update_displayed_attributes([
'title',
'overview',
'genres',
'release_date'
])
```
```php
$client->index('movies')->updateDisplayedAttributes([
'title',
'overview',
'genres',
'release_date'
]);
```
```java
client.index("movies").updateDisplayedAttributesSettings(new String[]
{
"title",
"overview",
"genres",
"release_date"
});
```
```ruby
client.index('movies').update_displayed_attributes([
'title',
'overview',
'genres',
'release_date'
])
```
```go
displayedAttributes := []string{
"title",
"overview",
"genres",
"release_date",
}
client.Index("movies").UpdateDisplayedAttributes(&displayedAttributes)
```
```csharp
await client.Index("movies").UpdateDisplayedAttributesAsync(new[]
{
"title",
"overview",
"genres",
"release_date"
});
```
```rust
let displayed_attributes = [
"title",
"overview",
"genres",
"release_date"
];
let task: TaskInfo = client
.index("movies")
.set_displayed_attributes(&displayed_attributes)
.await
.unwrap();
```
```swift
let displayedAttributes: [String] = ["title", "overview", "genres", "release_date"]
client.index("movies").updateDisplayedAttributes(displayedAttributes) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').updateDisplayedAttributes([
'title',
'overview',
'genres',
'release_date',
]);
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset displayed attributes
Reset the displayed attributes of the index to the default value.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies/settings/displayed-attributes'
```
```js
client.index('movies').resetDisplayedAttributes()
```
```py
client.index('movies').reset_displayed_attributes()
```
```php
$client->index('movies')->resetDisplayedAttributes();
```
```java
client.index("movies").resetDisplayedAttributesSettings();
```
```ruby
client.index('movies').reset_displayed_attributes
```
```go
client.Index("movies").ResetDisplayedAttributes()
```
```csharp
await client.Index("movies").ResetDisplayedAttributesAsync();
```
```rust
let task: TaskInfo = client
.index("movies")
.reset_displayed_attributes()
.await
.unwrap();
```
```swift
client.index("movies").resetDisplayedAttributes { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').resetDisplayedAttributes();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Distinct attribute
The distinct attribute is a field whose value will always be unique in the returned documents.
Updating distinct attributes will re-index all documents in the index, which can take some time. We recommend updating your index settings first and then adding documents as this reduces RAM consumption.
[To learn more about the distinct attribute, refer to our dedicated guide.](/learn/relevancy/distinct_attribute)
#### Get distinct attribute
Get the distinct attribute of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/shoes/settings/distinct-attribute'
```
```js
client.index('shoes').getDistinctAttribute()
```
```py
client.index('shoes').get_distinct_attribute()
```
```php
$client->index('shoes')->getDistinctAttribute();
```
```java
client.index("shoes").getDistinctAttributeSettings();
```
```ruby
client.index('shoes').distinct_attribute
```
```go
client.Index("shoes").GetDistinctAttribute()
```
```csharp
string result = await client.Index("shoes").GetDistinctAttributeAsync();
```
```rust
let distinct_attribute: Option = client
.index("shoes")
.get_distinct_attribute()
.await
.unwrap();
```
```swift
client.index("shoes").getDistinctAttribute { (result) in
switch result {
case .success(let distinctAttribute):
print(distinctAttribute)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('shoes').getDistinctAttribute();
```
###### Response: `200 Ok`
```json
"skuid"
```
#### Update distinct attribute
Update the distinct attribute field of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
```
A string. The string should be an attribute that exists in the selected index.
If an attribute contains an object, you can use dot notation to set one or more of its keys as a value for this setting, for example, `"distinctAttribute": "product.skuid"`.
If the field does not exist, no error will be thrown.
[To learn more about the distinct attribute, refer to our dedicated guide.](/learn/relevancy/distinct_attribute)
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/shoes/settings/distinct-attribute' \
-H 'Content-Type: application/json' \
--data-binary '"skuid"'
```
```js
client.index('shoes').updateDistinctAttribute('skuid')
```
```py
client.index('shoes').update_distinct_attribute('skuid')
```
```php
$client->index('shoes')->updateDistinctAttribute('skuid');
```
```java
client.index("shoes").updateDistinctAttributeSettings("skuid");
```
```ruby
client.index('shoes').update_distinct_attribute('skuid')
```
```go
client.Index("shoes").UpdateDistinctAttribute("skuid")
```
```csharp
TaskInfo result = await client.Index("shoes").UpdateDistinctAttributeAsync("skuid");
```
```rust
let task: TaskInfo = client
.index("shoes")
.set_distinct_attribute("skuid")
.await
.unwrap();
```
```swift
client.index("shoes").updateDistinctAttribute("skuid") { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('shoes').updateDistinctAttribute('skuid');
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset distinct attribute
Reset the distinct attribute of an index to its default value.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/shoes/settings/distinct-attribute'
```
```js
client.index('shoes').resetDistinctAttribute()
```
```py
client.index('shoes').reset_distinct_attribute()
```
```php
$client->index('shoes')->resetDistinctAttribute();
```
```java
client.index("shoes").resetDistinctAttributeSettings();
```
```ruby
client.index('shoes').reset_distinct_attribute
```
```go
client.Index("shoes").ResetDistinctAttribute()
```
```csharp
TaskInfo result = await client.Index("shoes").ResetDistinctAttributeAsync();
```
```rust
let task: TaskInfo = client
.index("shoes")
.reset_distinct_attribute()
.await
.unwrap();
```
```swift
client.index("shoes").resetDistinctAttribute { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('shoes').resetDistinctAttribute();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Faceting
With Meilisearch, you can create [faceted search interfaces](/learn/filtering_and_sorting/search_with_facet_filters). This setting allows you to:
- Define the maximum number of values returned by the `facets` search parameter
- Sort facet values by value count or alphanumeric order
[To learn more about faceting, refer to our dedicated guide.](/learn/filtering_and_sorting/search_with_facet_filters)
#### Faceting object
| Name | Type | Default value | Description |
| :---------------------- | :------ | :--------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- |
| **`maxValuesPerFacet`** | Integer | `100` | Maximum number of facet values returned for each facet. Values are sorted in ascending lexicographical order |
| **`sortFacetValuesBy`** | Object | All facet values are sorted in ascending alphanumeric order (`"*": "alpha"`) | Customize facet order to sort by descending value count (`count`) or ascending alphanumeric order (`alpha`) |
#### Get faceting settings
Get the faceting settings of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/books/settings/faceting'
```
```js
client.index('books').getFaceting()
```
```py
client.index('books').get_faceting_settings()
```
```php
$client->index('books')->getFaceting();
```
```java
client.index("books").getFacetingSettings();
```
```ruby
client.index('books').faceting
```
```go
client.Index("books").GetFaceting()
```
```csharp
await client.Index("movies").GetFacetingAsync();
```
```rust
let faceting: FacetingSettings = client
.index("books")
.get_faceting()
.await
.unwrap();
```
```dart
await client.index('movies').getFaceting();
```
###### Response: `200 OK`
```json
{
"maxValuesPerFacet": 100,
"sortFacetValuesBy": {
"*": "alpha"
}
}
```
#### Update faceting settings
Partially update the faceting settings for an index. Any parameters not provided in the body will be left unchanged.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
{
"maxValuesPerFacet": ,
"sortFacetValuesBy": {
: "count",
: "alpha"
}
}
```
| Name | Type | Default value | Description |
| :---------------------- | :------ | :--------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- |
| **`maxValuesPerFacet`** | Integer | `100` | Maximum number of facet values returned for each facet. Values are sorted in ascending lexicographical order |
| **`sortFacetValuesBy`** | Object | All facet values are sorted in ascending alphanumeric order (`"*": "alpha"`) | Customize facet order to sort by descending value count(`count`) or ascending alphanumeric order (`alpha`) |
Suppose a query's search results contain a total of three values for a `colors` facet: `blue`, `green`, and `red`. If you set `maxValuesPerFacet` to `2`, Meilisearch will only return `blue` and `green` in the response body's `facetDistribution` object.
Setting `maxValuesPerFacet` to a high value might negatively impact performance.
##### Example
The following code sample sets `maxValuesPerFacet` to `2`, sorts the `genres` facet by descending count, and all other facets in ascending alphanumeric order:
```bash
curl \
-X PATCH 'MEILISEARCH_URL/indexes/books/settings/faceting' \
-H 'Content-Type: application/json' \
--data-binary '{
"maxValuesPerFacet": 2,
"sortFacetValuesBy": {
"*": "alpha",
"genres": "count"
}
}'
```
```js
client.index('books').updateFaceting({
maxValuesPerFacet: 2
sortFacetValuesBy: {
'*': 'alpha',
genres: 'count'
}
})
```
```py
params = {
'maxValuesPerFacet': 2,
'sortFacetValuesBy': {
'*': 'count',
'genres': 'count'
}
}
client.index('books').update_faceting_settings(params)
```
```php
$client->index('books')->updateFaceting([
'maxValuesPerFacet' => 2,
'sortFacetValuesBy' => ['*' => 'alpha', 'genres' => 'count']
]);
```
```java
Faceting newFaceting = new Faceting();
newFaceting.setMaxValuesPerFacet(2);
HashMap facetSortValues = new HashMap<>();
facetSortValues.put("*", FacetSortValue.ALPHA);
facetSortValues.put("genres", FacetSortValue.COUNT);
newFaceting.setSortFacetValuesBy(facetSortValues);
client.index("books").updateFacetingSettings(newFaceting);
```
```ruby
client.index('books').update_faceting({
max_values_per_facet: 2,
sort_facet_values_by: {
'*': 'alpha',
genres: 'count'
}
})
```
```go
client.Index("books").UpdateFaceting(&meilisearch.Faceting{
MaxValuesPerFacet: 2,
SortFacetValuesBy: {
"*": SortFacetTypeAlpha,
"genres": SortFacetTypeCount,
}
})
```
```csharp
var faceting = new Faceting
{
MaxValuesPerFacet = 2,
SortFacetValuesBy = new Dictionary
{
["*"] = SortFacetValuesByType.Alpha,
["genres"] = SortFacetValuesByType.Count
}
};
await client.Index("books").UpdateFacetingAsync(faceting);
```
```rust
let mut faceting = FacetingSettings {
max_values_per_facet: 2,
};
let task: TaskInfo = client
.index("books")
.set_faceting(&faceting)
.await
.unwrap();
```
```dart
await client.index('books').updateFaceting(Faceting(
maxValuesPerFacet: 2,
sortFacetValuesBy: {
'*': FacetingSortTypes.alpha,
'genres': FacetingSortTypes.count
}));
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2022-04-14T20:56:44.991039Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset faceting settings
Reset an index's faceting settings to their [default value](#faceting-object). Setting `sortFacetValuesBy` to `null`(`--data-binary '{ "sortFacetValuesBy": null }'`), will restore it to the default value (`"*": "alpha"`).
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/books/settings/faceting'
```
```js
client.index('books').resetFaceting()
```
```py
client.index('books').reset_faceting_settings()
```
```php
$client->index('books')->resetFaceting();
```
```java
client.index("books").resetFacetingSettings();
```
```ruby
index('books').reset_faceting
```
```go
client.Index("books").ResetFaceting()
```
```csharp
await client.Index("movies").ResetFacetingAsync();
```
```rust
let task: TaskInfo = client
.index("books")
.reset_faceting()
.await
.unwrap();
```
```dart
await client.index('movies').resetFaceting();
```
###### Response: `200 OK`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2022-04-14T20:53:32.863107Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Filterable attributes
Attributes in the `filterableAttributes` list can be used as [filters](/learn/filtering_and_sorting/filter_search_results) or [facets](/learn/filtering_and_sorting/search_with_facet_filters).
Updating filterable attributes will re-index all documents in the index, which can take some time. To reduce RAM consumption, first update your index settings and then add documents.
#### Filterable attribute object
`filterableAttributes` may be an array of either strings filterable attribute objects.
Filterable attribute objects must contain the following fields:
| Name | Type | Default value | Description |
| ----------------------- | ---------------- | ------------- | -------------------------------------------------------- |
| **`attributePatterns`** | Array of strings | `[]` | A list of strings indicating filterable fields |
| **`features`** | Object | `{"facetSearch": false, "filters": {"equality": true, "comparison": false}` | A list outlining filter types enabled for the specified attributes |
##### `attributePatterns`
Attribute patterns may begin or end with a * wildcard to match multiple fields: `customer_*`, `attribute*`.
##### `features`
`features` allows you to decide which filter features are allowed for the specified attributes. It accepts the following fields:
- `facetSearch`: Whether facet search should be enabled for the specified attributes. Boolean, defaults to `false`
- `filter`: A list outlining the filter types for the specified attributes. Must be an object and accepts the following fields:
- `equality`: Enables `=`, `!=`, `IN`, `EXISTS`, `IS NULL`, `IS EMPTY`, `NOT`, `AND`, and `OR`. Boolean, defaults to `true`
- `comparison`: Enables `>`, `>=`, `<`, `<=`, `TO`, `EXISTS`, `IS NULL`, `IS EMPTY`, `NOT`, `AND`, and `OR`. Boolean, defaults to `false`
Calculating `comparison` filters is a resource-intensive operation. Disabling them may lead to better search and indexing performance. `equality` filters use fewer resources and have limited impact on performance.
Use the simple string syntax to match reserved attributes. Reserved Meilisearch fields are always prefixed with an underscore (`_`), such as `_geo` and `_vector`.
If set as a filterable attribute, reserved attributes ignore the `features` field and automatically activate all search features. Reserved fields will not be matched by wildcard `attributePatterns` such as `_*`.
#### Get filterable attributes
Get the filterable attributes for an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/settings/filterable-attributes'
```
```js
client.index('movies').getFilterableAttributes()
```
```py
client.index('movies').get_filterable_attributes()
```
```php
$client->index('movies')->getFilterableAttributes();
```
```java
client.index("movies").getFilterableAttributesSettings();
```
```ruby
client.index('movies').filterable_attributes
```
```go
client.Index("movies").GetFilterableAttributes()
```
```csharp
IEnumerable attributes = await client.Index("movies").GetFilterableAttributesAsync();
```
```rust
let filterable_attributes: Vec = client
.index("movies")
.get_filterable_attributes()
.await
.unwrap();
```
```swift
client.index("movies").getFilterableAttributes { (result) in
switch result {
case .success(let attributes):
print(attributes)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').getFilterableAttributes();
```
###### Response: `200 Ok`
```json
[
"genres",
"director",
"release_date.year"
]
```
#### Update filterable attributes
Update an index's filterable attributes list.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
[, , …]
```
An array of strings containing the attributes that can be used as filters at query time. All filter types are enabled for the specified attributes when using the array of strings format.
You may also use an array of objects:
```
[
{
"attributePatterns": [, , …],
"features": {
"facetSearch": ,
"filter": {
"equality": ,
"comparison":
}
}
}
]
```
If the specified field does not exist, Meilisearch will silently ignore it.
If an attribute contains an object, you can use dot notation to set one or more of its keys as a value for this setting: `"filterableAttributes": ["release_date.year"]` or `"attributePatterns": ["release_date.year"]`.
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/movies/settings/filterable-attributes' \
-H 'Content-Type: application/json' \
--data-binary '[
"genres",
"director",
{
"attributePatterns": ["*_ratings"],
"features": {
"facetSearch": false,
"filters": {
"equality": true,
"comparison": false
}
}
}
]'
```
```js
client.index('movies')
.updateFilterableAttributes([
"genres",
{
attributePatterns: ["genre"],
features: {
facetSearch: true,
filter: { equality: true, comparison: false },
},
}
])
```
```py
client.index('movies').update_filterable_attributes([
'genres',
'director'
])
```
```php
$client->index('movies')->updateFilterableAttributes([
'author',
[
'attributePatterns' => ['genres'],
'features' => [
'facetSearch' => true,
'filter' => [
'equality' => true,
'comparison' => false,
],
],
],
]);
```
```java
Settings settings = new Settings();
settings.setFilterableAttributes(new String[] {"genres", "director"});
client.index("movies").updateSettings(settings);
```
```ruby
client.index('movies').update_filterable_attributes([
'genres',
'director'
])
```
```go
filterableAttributes := []string{
"genres",
"director",
}
client.Index("movies").UpdateFilterableAttributes(&filterableAttributes)
```
```csharp
List attributes = new() { "genres", "director" };
TaskInfo result = await client.Index("movies").UpdateFilterableAttributesAsync(attributes);
```
```rust
let filterable_attributes = [
"genres",
"director"
];
let task: TaskInfo = client
.index("movies")
.set_filterable_attributes(&filterable_attributes)
.await
.unwrap();
```
```swift
client.index("movies").updateFilterableAttributes(["genre", "director"]) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client
.index('movies')
.updateFilterableAttributes(['genres', 'director']);
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset filterable attributes
Reset an index's filterable attributes list back to its default value.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies/settings/filterable-attributes'
```
```js
client.index('movies').resetFilterableAttributes()
```
```py
client.index('movies').reset_filterable_attributes()
```
```php
$client->index('movies')->resetFilterableAttributes();
```
```java
client.index("movies").resetFilterableAttributesSettings();
```
```ruby
client.index('movies').reset_filterable_attributes
```
```go
client.Index("movies").ResetFilterableAttributes()
```
```csharp
TaskInfo result = await client.Index("movies").ResetFilterableAttributesAsync();
```
```rust
let task: TaskInfo = client
.index("movies")
.reset_filterable_attributes()
.await
.unwrap();
```
```swift
client.index("movies").resetFilterableAttributes { (result) in
switch result {
case .success(let attributes):
print(attributes)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').resetFilterableAttributes();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Localized attributes
By default, Meilisearch auto-detects the languages used in your documents. This setting allows you to explicitly define which languages are present in a dataset, and in which fields.
Localized attributes affect `searchableAttributes`, `filterableAttributes`, and `sortableAttributes`.
Configuring multiple languages for a single index may negatively impact performance.
[`locales`](/reference/api/search#query-locales) and `localizedAttributes` have the same goal: explicitly state the language used in a search when Meilisearch's language auto-detection is not working as expected.
If you believe Meilisearch is detecting incorrect languages because of the query text, explicitly set the search language with `locales`.
If you believe Meilisearch is detecting incorrect languages because of document, explicitly set the document language at the index level with `localizedAttributes`.
For full control over the way Meilisearch detects languages during indexing and at search time, set both `locales` and `localizedAttributes`.
#### Localized attributes object
`localizedAttributes` must be an array of locale objects. Its default value is `[]`.
Locale objects must have the following fields:
| Name | Type | Default value | Description |
| :----------------- | :------ | :------------ | :---------------------------------------------------------- |
| **`locales`** | Array of strings | `[]` | A list of strings indicating one or more ISO-639 locales |
| **`attributePatterns`** | Array of strings | `[]` | A list of strings indicating which fields correspond to the specified locales |
##### `locales`
Meilisearch supports the following [ISO-639-3](https://iso639-3.sil.org/) three-letter `locales`: `epo`, `eng`, `rus`, `cmn`, `spa`, `por`, `ita`, `ben`, `fra`, `deu`, `ukr`, `kat`, `ara`, `hin`, `jpn`, `heb`, `yid`, `pol`, `amh`, `jav`, `kor`, `nob`, `dan`, `swe`, `fin`, `tur`, `nld`, `hun`, `ces`, `ell`, `bul`, `bel`, `mar`, `kan`, `ron`, `slv`, `hrv`, `srp`, `mkd`, `lit`, `lav`, `est`, `tam`, `vie`, `urd`, `tha`, `guj`, `uzb`, `pan`, `aze`, `ind`, `tel`, `pes`, `mal`, `ori`, `mya`, `nep`, `sin`, `khm`, `tuk`, `aka`, `zul`, `sna`, `afr`, `lat`, `slk`, `cat`, `tgl`, `hye`.
You may alternatively use [ISO-639-1 two-letter equivalents](https://iso639-3.sil.org/code_tables/639/data) to the supported `locales`.
You may also assign an empty array to `locales`. In this case, Meilisearch will auto-detect the language of the associated `attributePatterns`.
##### `attributePatterns`
Attribute patterns may begin or end with a `*` wildcard to match multiple fields: `en_*`, `*-ar`.
You may also set `attributePatterns` to `*`, in which case Meilisearch will treat all fields as if they were in the associated locale.
#### Get localized attributes settings
Get the localized attributes settings of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/INDEX_NAME/settings/localized-attributes'
```
```js
client.index('INDEX_NAME').getLocalizedAttributes()
```
```py
client.index('INDEX_NAME').get_localized_attributes()
```
```php
$client->index('INDEX_NAME')->getLocalizedAttributes();
```
```java
client.index("INDEX_NAME").getLocalizedAttributesSettings();
```
```ruby
client.index('INDEX_NAME').localized_attributes
```
```go
client.index("INDEX_NAME").GetLocalizedAttributes()
```
```rust
let localized_attributes: Option> = client
.index("books")
.get_localized_attributes()
.await
.unwrap();
```
###### Response: `200 OK`
```json
[
{"locales": ["jpn"], "attributePatterns": ["*_ja"]}
]
```
#### Update localized attribute settings
Update the localized attributes settings of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
[
{
"locales": [, …],
"attributePatterns": [, …],
}
]
```
| Name | Type | Default value | Description |
| :----------------- | :------ | :------------ | :---------------------------------------------------------- |
| **`localizedAttributes`** | Array of objects | `[]` | Explicitly set specific locales for one or more attributes |
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/INDEX_NAME/settings/localized-attributes' \
-H 'Content-Type: application/json' \
--data-binary '[
{"locales": ["jpn"], "attributePatterns": ["*_ja"]}
]'
```
```js
client.index('INDEX_NAME').updateLocalizedAttributes([
{ attributePatterns: ['*_ja'], locales: ['jpn'] },
])
```
```py
client.index('INDEX_NAME').update_localized_attributes([
{'attribute_patterns': ['*_ja'], 'locales': ['jpn']}
])
```
```php
$client->index('INDEX_NAME')->updateLocalizedAttributes([
'locales' => ['jpn'],
'attributePatterns' => ['*_ja']
]);
```
```java
LocalizedAttribute attribute = new LocalizedAttribute();
attribute.setAttributePatterns(new String[] {"jpn"});
attribute.setLocales(new String[] {"*_ja"});
client.index("INDEX_NAME").updateLocalizedAttributesSettings(
new LocalizedAttributes[] {attribute}
);
```
```ruby
client.index('INDEX_NAME').update_localized_attributes([
{ attribute_patterns: ['*_ja'], locales: ['jpn'] },
])
```
```go
client.index("INDEX_NAME").UpdateLocalizedAttributes([]*LocalizedAttributes{
{ AttributePatterns: ["*_ja"], Locales: ["jpn"] },
})
```
```rust
let localized_attributes = vec![LocalizedAttributes {
locales: vec!["jpn".to_string()],
attribute_patterns: vec!["*_ja".to_string()],
}];
let task: TaskInfo = client
.index("books")
.set_localized_attributes(&localizced_attributes)
.await
.unwrap();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "INDEX_NAME",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2022-04-14T20:56:44.991039Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset localized attributes settings
Reset an index's localized attributes to their [default value](#localized-attributes-object).
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/INDEX_NAME/settings/localized-attributes'
```
```js
client.index('INDEX_NAME').resetLocalizedAttributes()
```
```py
client.index('INDEX_NAME').reset_localized_attributes()
```
```php
$client->index('INDEX_NAME')->resetLocalizedAttributes();
```
```java
client.index("INDEX_NAME").resetLocalizedAttributesSettings();
```
```ruby
client.index('INDEX_NAME').reset_localized_attributes
```
```go
client.index("INDEX_NAME").ResetLocalizedAttributes()
```
```rust
let task: TaskInfo = client
.index("books")
.reset_localized_attributes()
.await
.unwrap();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "INDEX_NAME",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2022-04-14T20:53:32.863107Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Pagination
To protect your database from malicious scraping, Meilisearch has a default limit of 1000 results per search. This setting allows you to configure the maximum number of results returned per search.
`maxTotalHits` takes priority over search parameters such as `limit`, `offset`, `hitsPerPage`, and `page`.
For example, if you set `maxTotalHits` to 100, you will not be able to access search results beyond 100 no matter the value configured for `offset`.
[To learn more about paginating search results with Meilisearch, refer to our dedicated guide.](/guides/front_end/pagination)
#### Pagination object
| Name | Type | Default value | Description |
| :----------------- | :------ | :------------ | :---------------------------------------------------------- |
| **`maxTotalHits`** | Integer | `1000` | The maximum number of search results Meilisearch can return |
#### Get pagination settings
Get the pagination settings of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/books/settings/pagination'
```
```js
client.index('books').getPagination()
```
```py
client.index('books').get_pagination_settings()
```
```php
$client->index('books')->getPagination();
```
```java
client.index("books").getPaginationSettings();
```
```ruby
index('books').pagination
```
```go
client.Index("books").GetPagination()
```
```csharp
await client.Index("movies").GetPaginationAsync();
```
```rust
let pagination: PaginationSetting = client
.index("books")
.get_pagination()
.await
.unwrap();
```
```dart
await client.index('movies').getPagination();
```
###### Response: `200 OK`
```json
{
"maxTotalHits": 1000
}
```
#### Update pagination settings
Partially update the pagination settings for an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
{maxTotalHits: }
```
| Name | Type | Default value | Description |
| :----------------- | :------ | :------------ | :---------------------------------------------------------- |
| **`maxTotalHits`** | Integer | `1000` | The maximum number of search results Meilisearch can return |
Setting `maxTotalHits` to a value higher than the default will negatively impact search performance. Setting `maxTotalHits` to values over `20000` may result in queries taking seconds to complete.
##### Example
```bash
curl \
-X PATCH 'MEILISEARCH_URL/indexes/books/settings/pagination' \
-H 'Content-Type: application/json' \
--data-binary '{
"maxTotalHits": 100
}'
```
```js
client.index('books').updateSettings({ pagination: { maxTotalHits: 100 }})
```
```py
client.index('books').update_pagination_settings({'maxTotalHits': 100})
```
```php
$client->index('books')->updateSettings([
'pagination' => [
'maxTotalHits' => 100
]
]);
```
```java
Pagination newPagination = new Pagination();
newPagination.setMaxTotalHits(100);
client.index("books").updatePaginationSettings(newPagination);
```
```ruby
index('books').update_pagination({ max_total_hits: 100 })
```
```go
client.Index("books").UpdatePagination(&meilisearch.Pagination{
MaxTotalHits: 100,
})
```
```csharp
var pagination = new Pagination {
MaxTotalHits = 20
};
await client.Index("movies").UpdatePaginationAsync(pagination);
```
```rust
let pagination = PaginationSetting {max_total_hits:100};
let task: TaskInfo = client
.index("books")
.set_pagination(pagination)
.await
.unwrap();
```
```dart
await client
.index('books')
.updatePagination(Pagination(maxTotalHits: 100));
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2022-04-14T20:56:44.991039Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset pagination settings
Reset an index's pagination settings to their [default value](#pagination-object).
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/books/settings/pagination'
```
```js
client.index('books').resetPagination()
```
```py
client.index('books').reset_pagination_settings()
```
```php
$client->index('books')->resetPagination();
```
```java
client.index("books").resetPaginationSettings();
```
```ruby
index('books').reset_pagination
```
```go
client.Index("books").ResetPagination()
```
```csharp
await client.Index("movies").ResetPaginationAsync();
```
```rust
let task: TaskInfo = client
.index("books")
.reset_pagination()
.await
.unwrap();
```
```dart
await client.index('movies').resetPagination();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2022-04-14T20:53:32.863107Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Proximity precision
Calculating the distance between words is a resource-intensive operation. Lowering the precision of this operation may significantly improve performance and will have little impact on result relevancy in most use-cases. Meilisearch uses word distance when [ranking results according to proximity](/learn/relevancy/ranking_rules#3-proximity) and when users perform [phrase searches](/reference/api/search#phrase-search).
`proximityPrecision` accepts one of the following string values:
- `"byWord"`: calculate the precise distance between query terms. Higher precision, but may lead to longer indexing time. This is the default setting
- `"byAttribute"`: determine if multiple query terms are present in the same attribute. Lower precision, but shorter indexing time
#### Get proximity precision settings
Get the proximity precision settings of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/books/settings/proximity-precision'
```
```js
client.index('books').getProximityPrecision()
```
```py
client.index('books').get_proximity_precision()
```
```php
$client->index('books')->getProximityPrecision();
```
```java
client.index("books").getProximityPrecisionSettings();
```
```ruby
client.index('books').proximity_precision
```
```go
client.Index("books").GetProximityPrecision()
```
```csharp
await client.Index("books").GetProximityPrecisionAsync();
```
```rust
let proximity_precision: String = client
.index("books")
.get_proximity_precision()
.await
.unwrap();
```
```swift
let precisionValue = try await self.client.index("books").getProximityPrecision()
```
###### Response: `200 OK`
```
"byWord"
```
#### Update proximity precision settings
Update the pagination settings for an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
"byWord"|"byAttribute"
```
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/books/settings/proximity-precision' \
-H 'Content-Type: application/json' \
--data-binary '"byAttribute"'
```
```js
client.index('books').updateProximityPrecision('byAttribute')
```
```py
client.index('books').update_proximity_precision(ProximityPrecision.BY_ATTRIBUTE)
```
```php
$client->index('books')->updateProximityPrecision('byAttribute');
```
```java
client.index("books").updateProximityPrecisionSettings("byAttribute");
```
```ruby
client.index('books').update_proximity_precision('byAttribute')
```
```go
client.Index("books").UpdateProximityPrecision(ByAttribute)
```
```csharp
await client.Index("books").UpdateProximityPrecisionAsync("byAttribute");
```
```rust
let task: TaskInfo = client
.index("books")
.set_proximity_precision("byAttribute".to_string())
.await
.unwrap();
```
```swift
let task = try await self.client.index("books").updateProximityPrecision(.byWord)
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2023-04-14T15:50:29.821044Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset proximity precision settings
Reset an index's proximity precision setting to its default value.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/books/settings/proximity-precision'
```
```js
client.index('books').resetProximityPrecision()
```
```py
client.index('books').reset_proximity_precision()
```
```php
$client->index('books')->resetProximityPrecision();
```
```java
client.index("books").resetProximityPrecisionSettings();
```
```ruby
client.index('books').reset_proximity_precision
```
```go
client.Index("books").ResetProximityPrecision()
```
```csharp
await client.Index("books").ResetProximityPrecisionAsync();
```
```rust
let task: TaskInfo = client
.index("books")
.reset_proximity_precision()
.await
.unwrap();
```
```swift
let task = try await self.client.index("books").resetProximityPrecision()
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2023-04-14T15:51:47.821044Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Facet search
Processing filterable attributes for facet search is a resource-intensive operation. This feature is enabled by default, but disabling it may speed up indexing.
`facetSearch` accepts a single Boolean value. If set to `false`, it disables facet search for the whole index. Meilisearch returns an error if you try to access the `/facet-search` endpoint when facet search is disabled.
#### Get facet search settings
Get the facet search settings of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/INDEX_UID/settings/facet-search'
```
```js
client.index('INDEX_NAME').getFacetSearch();
```
```php
$client->index('INDEX_NAME')->getFacetSearch();
```
```ruby
client.index('INDEX_UID').facet_search_setting
```
###### Response: `200 OK`
```json
true
```
#### Update facet search settings
Update the facet search settings for an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
```
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/INDEX_UID/settings/facet-search' \
-H 'Content-Type: application/json' \
--data-binary 'false'
```
```js
client.index('INDEX_NAME').updateFacetSearch(false);
```
```php
$client->index('INDEX_NAME')->updateFacetSearch(false);
```
```ruby
client.index('INDEX_UID').update_facet_search_setting(false)
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "INDEX_UID",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2024-07-19T22:33:18.523881Z"
}
```
Use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset facet search settings
Reset an index's facet search to its default settings.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/INDEX_UID/settings/facet-search'
```
```js
client.index('INDEX_NAME').resetFacetSearch();
```
```php
$client->index('INDEX_NAME')->resetFacetSearch();
```
```ruby
client.index('INDEX_UID').reset_facet_search_setting
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "INDEX_UID",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2024-07-19T22:35:33.723983Z"
}
```
Use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Prefix search
Prefix search is the process through which Meilisearch matches documents that begin with a specific query term, instead of only exact matches. This is a resource-intensive operation that happens during indexing by default.
Use `prefixSearch` to change how prefix search works. It accepts one of the following strings:
- `"indexingTime"`: calculate prefix search during indexing. This is the default behavior
- `"disabled"`: do not calculate prefix search. May speed up indexing, but will severely impact search result relevancy
#### Get prefix search settings
Get the prefix search settings of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/INDEX_UID/settings/prefix-search'
```
```js
client.index('INDEX_NAME').getPrefixSearch();
```
```php
$client->index('INDEX_NAME')->getPrefixSearch();
```
```ruby
client.index('INDEX_UID').prefix_search
```
###### Response: `200 OK`
```json
"indexingTime"
```
#### Update prefix search settings
Update the prefix search settings for an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
"indexingTime" | "disabled"
```
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/INDEX_UID/settings/prefix-search' \
-H 'Content-Type: application/json' \
--data-binary '"disabled"'
```
```js
client.index('INDEX_NAME').updatePrefixSearch('disabled');
```
```php
$client->index('INDEX_NAME')->updatePrefixSearch('disabled');
```
```ruby
client.index('INDEX_UID').update_prefix_search('disabled')
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "INDEX_UID",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2024-07-19T22:33:18.523881Z"
}
```
Use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset prefix search settings
Reset an index's prefix search to its default settings.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :--------------------------------------------------------------------- |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/INDEX_UID/settings/facet-search'
```
```js
client.index('INDEX_NAME').resetFacetSearch();
```
```php
$client->index('INDEX_NAME')->resetFacetSearch();
```
```ruby
client.index('INDEX_UID').reset_facet_search_setting
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "INDEX_UID",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2024-07-19T22:35:33.723983Z"
}
```
Use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Ranking rules
Ranking rules are built-in rules that rank search results according to certain criteria. They are applied in the same order in which they appear in the `rankingRules` array.
[To learn more about ranking rules, refer to our dedicated guide.](/learn/relevancy/relevancy)
#### Ranking rules array
| Name | Description |
| :---------------- | :------------------------------------------------------------------------------ |
| **`"words"`** | Sorts results by decreasing number of matched query terms |
| **`"typo"`** | Sorts results by increasing number of typos |
| **`"proximity"`** | Sorts results by increasing distance between matched query terms |
| **`"attribute"`** | Sorts results based on the attribute ranking order |
| **`"sort"`** | Sorts results based on parameters decided at query time |
| **`"exactness"`** | Sorts results based on the similarity of the matched words with the query words |
##### Default order
```json
[
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness"
]
```
#### Get ranking rules
Get the ranking rules of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/settings/ranking-rules'
```
```js
client.index('movies').getRankingRules()
```
```py
client.index('movies').get_ranking_rules()
```
```php
$client->index('movies')->getRankingRules();
```
```java
client.index("movies").getRankingRulesSettings();
```
```ruby
client.index('movies').ranking_rules
```
```go
client.Index("movies").GetRankingRules()
```
```csharp
await client.Index("movies").GetRankingRulesAsync();
```
```rust
let ranking_rules: Vec = client
.index("movies")
.get_ranking_rules()
.await
.unwrap();
```
```swift
client.index("movies").getRankingRules { (result) in
switch result {
case .success(let rankingRules):
print(rankingRules)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').getRankingRules();
```
###### Response: `200 Ok`
```json
[
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:desc"
]
```
#### Update ranking rules
Update the ranking rules of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
[, , …]
```
An array that contains ranking rules in order of importance.
To create a custom ranking rule, give an attribute followed by a colon (`:`) and either `asc` for ascending order or `desc` for descending order.
- To apply an **ascending sort** (results sorted by increasing value): `attribute_name:asc`
- To apply a **descending sort** (results sorted by decreasing value): `attribute_name:desc`
If some documents do not contain the attribute defined in a custom ranking rule, the application of the ranking rule is undefined and the search results might not be sorted as you expected.
Make sure that any attribute used in a custom ranking rule is present in all of your documents. For example, if you set the custom ranking rule `desc(year)`, make sure that all your documents contain the attribute `year`.
[To learn more about ranking rules, refer to our dedicated guide.](/learn/relevancy/ranking_rules)
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/movies/settings/ranking-rules' \
-H 'Content-Type: application/json' \
--data-binary '[
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:asc",
"rank:desc"
]'
```
```js
client.index('movies').updateRankingRules([
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'release_date:asc',
'rank:desc'
])
```
```py
client.index('movies').update_ranking_rules([
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'release_date:asc',
'rank:desc'
])
```
```php
$client->index('movies')->updateRankingRules([
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'release_date:asc',
'rank:desc'
]);
```
```java
Settings settings = new Settings();
settings.setRankingRules(new String[]
{
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:asc",
"rank:desc"
});
client.index("movies").updateSettings(settings);
```
```ruby
client.index('movies').update_ranking_rules([
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'release_date:asc',
'rank:desc'
])
```
```go
rankingRules := []string{
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:asc",
"rank:desc",
}
client.Index("movies").UpdateRankingRules(&rankingRules)
```
```csharp
await client.Index("movies").UpdateRankingRulesAsync(new[]
{
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:asc",
"rank:desc"
});
```
```rust
let ranking_rules = [
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:asc",
"rank:desc",
];
let task: TaskInfo = client
.index("movies")
.set_ranking_rules(&ranking_rules)
.await
.unwrap();
```
```swift
let rankingRules: [String] = [
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness",
"release_date:asc",
"rank:desc"
]
client.index("movies").updateRankingRules(rankingRules) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').updateRankingRules([
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'release_date:asc',
'rank:desc',
]);
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset ranking rules
Reset the ranking rules of an index to their [default value](#default-order).
Resetting ranking rules is not the same as removing them. To remove a ranking rule, use the [update ranking rules endpoint](#update-ranking-rules).
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies/settings/ranking-rules'
```
```js
client.index('movies').resetRankingRules()
```
```py
client.index('movies').reset_ranking_rules()
```
```php
$client->index('movies')->resetRankingRules();
```
```java
client.index("movies").resetRankingRulesSettings();
```
```ruby
client.index('movies').reset_ranking_rules
```
```go
client.Index("movies").ResetRankingRules()
```
```csharp
await client.Index("movies").ResetRankingRulesAsync();
```
```rust
let task: TaskInfo = client
.index("movies")
.reset_ranking_rules()
.await
.unwrap();
```
```swift
client.index("movies").resetRankingRules { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').resetRankingRules();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Searchable attributes
The values associated with attributes in the `searchableAttributes` list are searched for matching query words. The order of the list also determines the [attribute ranking order](/learn/relevancy/attribute_ranking_order).
By default, the `searchableAttributes` array is equal to all fields in your dataset. This behavior is represented by the value `["*"]`.
Updating searchable attributes will re-index all documents in the index, which can take some time. We recommend updating your index settings first and then adding documents as this reduces RAM consumption.
[To learn more about searchable attributes, refer to our dedicated guide.](/learn/relevancy/displayed_searchable_attributes#searchable-fields)
#### Get searchable attributes
Get the searchable attributes of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/settings/searchable-attributes'
```
```js
client.index('movies').getSearchableAttributes()
```
```py
client.index('movies').get_searchable_attributes()
```
```php
$client->index('movies')->getSearchableAttributes();
```
```java
client.index("movies").getSearchableAttributesSettings();
```
```ruby
client.index('movies').searchable_attributes
```
```go
client.Index("movies").GetSearchableAttributes()
```
```csharp
await client.Index("movies").GetSearchableAttributesAsync();
```
```rust
let searchable_attributes: Vec = client
.index("movies")
.get_searchable_attributes()
.await
.unwrap();
```
```swift
client.index("movies").getSearchableAttributes { (result) in
switch result {
case .success(let searchableAttributes):
print(searchableAttributes)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').getSearchableAttributes();
```
###### Response: `200 Ok`
```json
[
"title",
"overview",
"genres",
"release_date.year"
]
```
#### Update searchable attributes
Update the searchable attributes of an index.
Due to an implementation bug, manually updating `searchableAttributes` will change the displayed order of document fields in the JSON response. This behavior is inconsistent and will be fixed in a future release.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
[, , …]
```
An array of strings. Each string should be an attribute that exists in the selected index. The array should be given in [order of importance](/learn/relevancy/attribute_ranking_order): from the most important attribute to the least important attribute.
If an attribute contains an object, you can use dot notation to set one or more of its keys as a value for this setting: `"searchableAttributes": ["release_date.year"]`.
If the field does not exist, no error will be thrown.
[To learn more about searchable attributes, refer to our dedicated guide.](/learn/relevancy/displayed_searchable_attributes#searchable-fields)
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/movies/settings/searchable-attributes' \
-H 'Content-Type: application/json' \
--data-binary '[
"title",
"overview",
"genres"
]'
```
```js
client.index('movies').updateSearchableAttributes([
'title',
'overview',
'genres'
])
```
```py
client.index('movies').update_searchable_attributes([
'title',
'overview',
'genres'
])
```
```php
$client->index('movies')->updateSearchableAttributes([
'title',
'overview',
'genres'
]);
```
```java
client.index("movies").updateSearchableAttributesSettings(new String[]
{
"title",
"overview",
"genres"
});
```
```ruby
client.index('movies').update_searchable_attributes([
'title',
'overview',
'genres'
])
```
```go
searchableAttributes := []string{
"title",
"overview",
"genres",
}
client.Index("movies").UpdateSearchableAttributes(&searchableAttributes)
```
```csharp
await client.Index("movies").UpdateSearchableAttributesAsync(new[] {"title", "overview", "genres"});
```
```rust
let searchable_attributes = [
"title",
"overview",
"genres"
];
let task: TaskInfo = client
.index("movies")
.set_searchable_attributes(&searchable_attributes)
.await
.unwrap();
```
```swift
let searchableAttributes: [String] = ["title", "overview", "genres"]
client.index("movies").updateSearchableAttributes(searchableAttributes) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client
.index('movies')
.updateSearchableAttributes(['title', 'overview', 'genres']);
```
In this example, a document with a match in `title` will be more relevant than another document with a match in `overview`.
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset searchable attributes
Reset the searchable attributes of the index to the default value.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies/settings/searchable-attributes'
```
```js
client.index('movies').resetSearchableAttributes()
```
```py
client.index('movies').reset_searchable_attributes()
```
```php
$client->index('movies')->resetSearchableAttributes();
```
```java
client.index("movies").resetSearchableAttributesSettings();
```
```ruby
client.index('movies').reset_searchable_attributes
```
```go
client.Index("movies").ResetSearchableAttributes()
```
```csharp
await client.Index("movies").ResetSearchableAttributesAsync();
```
```rust
let task: TaskInfo = client
.index("movies")
.reset_searchable_attributes()
.await
.unwrap();
```
```swift
client.index("movies").resetSearchableAttributes { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').resetSearchableAttributes();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Search cutoff
Configure the maximum duration of a search query. Meilisearch will interrupt any search taking longer than the cutoff value.
By default, Meilisearch interrupts searches after 1500 milliseconds.
#### Get search cutoff
Get an index's search cutoff value.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/settings/search-cutoff-ms'
```
```js
client.index('movies').getSearchCutoffMs()
```
```py
client.index('movies').get_search_cutoff_ms()
```
```php
$client->index('movies')->getSearchCutoffMs();
```
```java
client.index("movies").getSearchCutoffMsSettings();
```
```ruby
client.index('movies').search_cutoff_ms
```
```go
client.Index("movies").GetSearchCutoffMs()
```
```csharp
var searchCutoff = await client.Index("movies").GetSearchCutoffMsAsync();
```
```rust
let search_cutoff_ms: String = client
.index("movies")
.get_search_cutoff_ms()
.await
.unwrap();
```
```swift
let precisionValue = try await self.client.index("books").getSearchCutoffMs()
```
###### Response: `200 Ok`
```json
null
```
#### Update search cutoff
Update an index's search cutoff value.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
150
```
A single integer indicating the cutoff value in milliseconds.
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/movies/settings/search-cutoff-ms' \
-H 'Content-Type: application/json' \
--data-binary '150'
```
```js
client.index('movies').updateSearchCutoffMs(150)
```
```py
client.index('movies').update_search_cutoff_ms(150)
```
```php
$client->index('movies')->updateSearchCutoffMs(150);
```
```java
client.index("movies").updateSearchCutoffMsSettings(150);
```
```ruby
client.index('movies').update_search_cutoff_ms(150)
```
```go
client.Index("movies").UpdateSearchCutoffMs(150)
```
```csharp
await client.Index("movies").UpdateSearchCutoffMsAsync(150);
```
```rust
let task: TaskInfo = client
.index("movies")
.set_search_cutoff_ms(Some(150))
.await
.unwrap();
```
```swift
let task = try await self.client.index("books").updateSearchCutoffMs(150)
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2023-03-21T06:33:41.000000Z"
}
```
Use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset search cutoff
Reset an index's search cutoff value to its default value, `null`. This translates to a cutoff of 1500ms.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies/settings/search-cutoff-ms'
```
```js
client.index('movies').resetSearchCutoffMs()
```
```py
client.index('movies').reset_search_cutoff_ms()
```
```php
$client->index('movies')->resetSearchCutoffMs();
```
```java
client.index("movies").resetSearchCutoffMsSettings();
```
```ruby
client.index('movies').reset_search_cutoff_ms
```
```go
client.Index("books").ResetSearchCutoffMs()
```
```csharp
await client.Index("movies").ResetSearchCutoffMsAsync();
```
```rust
let task: TaskInfo = client
.index("movies")
.reset_search_cutoff_ms()
.await
.unwrap();
```
```swift
let task = try await self.client.index("books").resetSearchCutoffMs()
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2023-03-21T07:05:16.000000Z"
}
```
### Separator tokens
Configure strings as custom separator tokens indicating where a word ends and begins.
Tokens in the `separatorTokens` list are added on top of [Meilisearch's default list of separators](/learn/engine/datatypes#string). To remove separators from the default list, use [the `nonSeparatorTokens` setting](#non-separator-tokens).
#### Get separator tokens
Get an index's list of custom separator tokens.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/articles/settings/separator-tokens'
```
```js
client.index('books').getSeparatorTokens()
```
```py
client.index('articles').get_separator_tokens()
```
```php
$client->index('articles')->getSeparatorTokens();
```
```java
client.index("articles").getSeparatorTokensSettings();
```
```ruby
client.index('articles').separator_tokens
```
```go
client.Index("articles").GetSeparatorTokens()
```
```csharp
await client.Index("movies").GetSeparatorTokensAsync();
```
```rust
let task: TaskInfo = client
.index('articles')
.get_separator_tokens()
.await
.unwrap();
```
```swift
client.index("books").getSeparatorTokens { result in
// handle result
}
```
```dart
await client.index('articles').getSeparatorTokens();
```
###### Response: `200 Ok`
```json
[]
```
#### Update separator tokens
Update an index's list of custom separator tokens.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
["|", "…"]
```
An array of strings, with each string indicating a word separator.
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/articles/settings/separator-tokens' \
-H 'Content-Type: application/json' \
--data-binary '["|", "…"]'
```
```js
client.index('books').updateSeparatorTokens(['|', '…'])
```
```py
client.index('articles').update_separator_tokens(["|", "…"])
```
```php
$client->index('articles')->updateSeparatorTokens(['|', '…']);
```
```java
String[] newSeparatorTokens = { "|", "…" };
client.index("articles").updateSeparatorTokensSettings(newSeparatorTokens);
```
```ruby
client.index('articles').update_separator_tokens(['|', '…'])
```
```go
client.Index("articles").UpdateSeparatorTokens([]string{
"|",
"…",
})
```
```csharp
await client.Index("movies").UpdateSeparatorTokensAsync(new[] { "|", "…" });
```
```rust
let task: TaskInfo = client
.index('articles')
.set_separator_tokens(&vec!['|'.to_string(), '…'.to_string()])
.await
.unwrap();
```
```swift
client.index("books").updateSeparatorTokens(["|", "…"]) { result in
// handle result
}
```
```dart
await client.index('articles').updateSeparatorTokens(["|", "…"]);
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
Use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset separator tokens
Reset an index's list of custom separator tokens to its default value, `[]`.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/articles/settings/separator-tokens'
```
```js
client.index('books').resetSeparatorTokens()
```
```py
client.index('articles').reset_separator_tokens()
```
```php
$client->index('articles')->resetSeparatorTokens();
```
```java
client.index("articles").resetSeparatorTokensSettings();
```
```ruby
client.index('articles').reset_separator_tokens
```
```go
client.Index("articles").ResetSeparatorTokens()
```
```csharp
await client.Index("movies").ResetSeparatorTokensAsync();
```
```rust
let task: TaskInfo = client
.index('articles')
.reset_separator_tokens()
.await
.unwrap();
```
```swift
client.index("books").resetSeparatorTokens { result in
// handle result
}
```
```dart
await client.index('articles').resetSeparatorTokens();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
Use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Non-separator tokens
Remove tokens from Meilisearch's default [list of word separators](/learn/engine/datatypes#string).
#### Get non-separator tokens
Get an index's list of non-separator tokens.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/articles/settings/non-separator-tokens'
```
```js
client.index('books').getNonSeparatorTokens()
```
```py
client.index('articles').get_non_separator_tokens()
```
```php
$client->index('articles')->getNonSeparatorTokens();
```
```java
client.index("articles").getNonSeparatorTokensSettings();
```
```ruby
client.index('articles').non_separator_tokens
```
```go
client.Index("articles").GetNonSeparatorTokens()
```
```csharp
await client.Index("movies").GetNonSeparatorTokensAsync();
```
```rust
let task: TaskInfo = client
.index('articles')
.get_non_separator_tokens()
.await
.unwrap();
```
```swift
client.index("books").getNonSeparatorTokens { result in
// handle result
}
```
```dart
await client.index('articles').getNonSeparatorTokens();
```
###### Response: `200 Ok`
```json
[]
```
#### Update non-separator tokens
Update an index's list of non-separator tokens.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
["@", "#"]
```
An array of strings, with each string indicating a token present in [list of word separators](/learn/engine/datatypes#string).
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/articles/settings/non-separator-tokens' \
-H 'Content-Type: application/json' \
--data-binary '["@", "#"]'
```
```js
client.index('books').updateNonSeparatorTokens(['@', '#'])
```
```py
client.index('articles').update_non_separator_tokens(["@", "#"])
```
```php
$client->index('articles')->updateNonSeparatorTokens(['@', '#']);
```
```java
String[] newSeparatorTokens = { "@", "#" };
client.index("articles").updateNonSeparatorTokensSettings(newSeparatorTokens);
```
```ruby
client.index('articles').update_non_separator_tokens(['@', '#'])
```
```go
client.Index("articles").UpdateNonSeparatorTokens([]string{
"@",
"#",
})
```
```csharp
await client.Index("movies").UpdateNonSeparatorTokensAsync(new[] { "@", "#" });
```
```rust
let task: TaskInfo = client
.index('articles')
.set_non_separator_tokens(&vec!['@'.to_string(), '#'.to_string()])
.await
.unwrap();
```
```swift
client.index("books").updateNonSeparatorTokens(["@", "#"]) { result in
// handle result
}
```
```dart
await client.index('articles').updateNonSeparatorTokens(["@", "#"]);
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
Use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset non-separator tokens
Reset an index's list of non-separator tokens to its default value, `[]`.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/articles/settings/separator-tokens'
```
```js
client.index('books').resetSeparatorTokens()
```
```py
client.index('articles').reset_separator_tokens()
```
```php
$client->index('articles')->resetSeparatorTokens();
```
```java
client.index("articles").resetSeparatorTokensSettings();
```
```ruby
client.index('articles').reset_separator_tokens
```
```go
client.Index("articles").ResetSeparatorTokens()
```
```csharp
await client.Index("movies").ResetSeparatorTokensAsync();
```
```rust
let task: TaskInfo = client
.index('articles')
.reset_separator_tokens()
.await
.unwrap();
```
```swift
client.index("books").resetSeparatorTokens { result in
// handle result
}
```
```dart
await client.index('articles').resetSeparatorTokens();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
Use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Sortable attributes
Attributes that can be used when sorting search results using the [`sort` search parameter](/reference/api/search#sort).
Updating sortable attributes will re-index all documents in the index, which can take some time. We recommend updating your index settings first and then adding documents as this reduces RAM consumption.
[To learn more about sortable attributes, refer to our dedicated guide.](/learn/filtering_and_sorting/sort_search_results)
#### Get sortable attributes
Get the sortable attributes of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/books/settings/sortable-attributes'
```
```js
client.index('books').getSortableAttributes()
```
```py
client.index('books').get_sortable_attributes()
```
```php
$client->index('books')->getSortableAttributes();
```
```java
client.index("books").getSortableAttributesSettings();
```
```ruby
client.index('books').sortable_attributes
```
```go
client.Index("books").GetSortableAttributes()
```
```csharp
await client.Index("books").GetSortableAttributesAsync();
```
```rust
let sortable_attributes: Vec = client
.index("books")
.get_sortable_attributes()
.await
.unwrap();
```
```swift
client.index("books").getSortableAttributes { (result: Result, Swift.Error>) in
switch result {
case .success(let attributes):
print(attributes)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('books').getSortableAttributes();
```
###### Response: `200 Ok`
```json
[
"price",
"author.surname"
]
```
#### Update sortable attributes
Update an index's sortable attributes list.
[You can read more about sorting at query time on our dedicated guide.](/learn/filtering_and_sorting/sort_search_results)
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
[, , …]
```
An array of strings. Each string should be an attribute that exists in the selected index.
If an attribute contains an object, you can use dot notation to set one or more of its keys as a value for this setting: `"sortableAttributes": ["author.surname"]`.
If the field does not exist, no error will be thrown.
[To learn more about sortable attributes, refer to our dedicated guide.](/learn/filtering_and_sorting/sort_search_results)
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/books/settings/sortable-attributes' \
-H 'Content-Type: application/json' \
--data-binary '[
"price",
"author"
]'
```
```js
client.index('books')
.updateSortableAttributes([
'price',
'author'
])
```
```py
client.index('books').update_sortable_attributes([
'price',
'author'
])
```
```php
$client->index('books')->updateSortableAttributes([
'price',
'author'
]);
```
```java
client.index("books").updateSortableAttributesSettings(new String[] {"price", "author"});
```
```ruby
client.index('books').update_sortable_attributes([
'price',
'author'
])
```
```go
sortableAttributes := []string{
"price",
"author",
}
client.Index("books").UpdateSortableAttributes(&sortableAttributes)
```
```csharp
await client.Index("books").UpdateSortableAttributesAsync(new [] { "price", "author" });
```
```rust
let sortable_attributes = [
"price",
"author"
];
let task: TaskInfo = client
.index("books")
.set_sortable_attributes(&sortable_attributes)
.await
.unwrap();
```
```swift
client.index("books").updateSortableAttributes(["price", "author"]) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('books').updateSortableAttributes(['price', 'author']);
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset sortable attributes
Reset an index's sortable attributes list back to its default value.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/books/settings/sortable-attributes'
```
```js
client.index('books').resetSortableAttributes()
```
```py
client.index('books').reset_sortable_attributes()
```
```php
$client->index('books')->resetSortableAttributes();
```
```java
client.index("books").resetSortableAttributesSettings();
```
```ruby
client.index('books').reset_sortable_attributes
```
```go
client.Index("books").ResetSortableAttributes()
```
```csharp
await client.Index("books").ResetSortableAttributesAsync();
```
```rust
let task: TaskInfo = client
.index("books")
.reset_sortable_attributes()
.await
.unwrap();
```
```swift
client.index("books").resetSortableAttributes() { (result) in
switch result {
case .success(let attributes):
print(attributes)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('books').resetSortableAttributes();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Stop words
Words added to the `stopWords` list are ignored in future search queries.
Updating stop words will re-index all documents in the index, which can take some time. We recommend updating your index settings first and then adding documents as this reduces RAM consumption.
Stop words are strongly related to the language used in your dataset. For example, most datasets containing English documents will have countless occurrences of `the` and `of`. Italian datasets, instead, will benefit from ignoring words like `a`, `la`, or `il`.
[This website maintained by a French developer](https://sites.google.com/site/kevinbouge/stopwords-lists) offers lists of possible stop words in different languages. Note that, depending on your dataset and use case, you will need to tweak these lists for optimal results.
#### Get stop words
Get the stop words list of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/settings/stop-words'
```
```js
client.index('movies').getStopWords()
```
```py
client.index('movies').get_stop_words()
```
```php
$client->index('movies')->getStopWords();
```
```java
client.index("movies").getStopWordsSettings();
```
```ruby
client.index('movies').stop_words
```
```go
client.Index("movies").GetStopWords()
```
```csharp
await client.Index("movies").GetStopWordsAsync();
```
```rust
let stop_words: Vec = client
.index("movies")
.get_stop_words()
.await
.unwrap();
```
```swift
client.index("movies").getStopWords { (result) in
switch result {
case .success(let stopWords):
print(stopWords)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').getStopWords();
```
###### Response: `200 Ok`
```json
[
"of",
"the",
"to"
]
```
#### Update stop words
Update the list of stop words of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
[, , …]
```
An array of strings. Each string should be a single word.
If a list of stop words already exists, it will be overwritten (_replaced_).
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/movies/settings/stop-words' \
-H 'Content-Type: application/json' \
--data-binary '[
"the",
"of",
"to"
]'
```
```js
client.index('movies').updateStopWords(['of', 'the', 'to'])
```
```py
client.index('movies').update_stop_words(['of', 'the', 'to'])
```
```php
$client->index('movies')->updateStopWords(['the', 'of', 'to']);
```
```java
client.index("movies").updateStopWordsSettings(new String[] {"of", "the", "to"});
```
```ruby
client.index('movies').update_stop_words(['of', 'the', 'to'])
```
```go
stopWords := []string{"of", "the", "to"}
client.Index("movies").UpdateStopWords(&stopWords)
```
```csharp
await client.Index("movies").UpdateStopWordsAsync(new[] {"of", "the", "to"});
```
```rust
let stop_words = ["of", "the", "to"];
let task: TaskInfo = client
.index("movies")
.set_stop_words(&stop_words)
.await
.unwrap();
```
```swift
let stopWords: [String] = ["of", "the", "to"]
client.index("movies").updateStopWords(stopWords) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').updateStopWords(['of', 'the', 'to']);
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset stop words
Reset the list of stop words of an index to its default value.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies/settings/stop-words'
```
```js
client.index('movies').resetStopWords()
```
```py
client.index('movies').reset_stop_words()
```
```php
$client->index('movies')->resetStopWords();
```
```java
client.index("movies").resetStopWordsSettings();
```
```ruby
client.index('movies').reset_stop_words
```
```go
client.Index("movies").ResetStopWords()
```
```csharp
await client.Index("movies").ResetStopWordsAsync();
```
```rust
let task: TaskInfo = client
.index("movies")
.reset_stop_words()
.await
.unwrap();
```
```swift
client.index("movies").resetStopWords { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').resetStopWords();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Synonyms
The `synonyms` object contains words and their respective synonyms. A synonym in Meilisearch is considered equal to its associated word for the purposes of calculating search results.
[To learn more about synonyms, refer to our dedicated guide.](/learn/relevancy/synonyms)
#### Get synonyms
Get the list of synonyms of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/settings/synonyms'
```
```js
client.index('movies').getSynonyms()
```
```py
client.index('movies').get_synonyms()
```
```php
$client->index('movies')->getSynonyms();
```
```java
client.index("movies").getSynonymsSettings();
```
```ruby
client.index('movies').synonyms
```
```go
client.Index("movies").GetSynonyms()
```
```csharp
await client.Index("movies").GetSynonymsAsync();
```
```rust
let synonyms: HashMap> = client
.index("movies")
.get_synonyms()
.await
.unwrap();
```
```swift
client.index("movies").getSynonyms { (result) in
switch result {
case .success(let synonyms):
print(synonyms)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').getSynonyms();
```
###### Response: `200 OK`
```json
{
"wolverine": [
"xmen",
"logan"
],
"logan": [
"wolverine",
"xmen"
],
"wow": [
"world of warcraft"
]
}
```
#### Update synonyms
Update the list of synonyms of an index. Synonyms are [normalized](/learn/relevancy/synonyms#normalization).
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
{
: [, , …],
…
}
```
An object that contains all synonyms and their associated words. Add the associated words in an array to set a synonym for a word.
[To learn more about synonyms, refer to our dedicated guide.](/learn/relevancy/synonyms)
##### Example
```bash
curl \
-X PUT 'MEILISEARCH_URL/indexes/movies/settings/synonyms' \
-H 'Content-Type: application/json' \
--data-binary '{
"wolverine": [
"xmen",
"logan"
],
"logan": [
"wolverine",
"xmen"
],
"wow": ["world of warcraft"]
}'
```
```js
client.index('movies').updateSynonyms({
wolverine: ['xmen', 'logan'],
logan: ['wolverine', 'xmen'],
wow: ['world of warcraft']
})
```
```py
client.index('movies').update_synonyms({
'wolverine': ['xmen', 'logan'],
'logan': ['wolverine', 'xmen'],
'wow': ['world of warcraft']
})
```
```php
$client->index('movies')->updateSynonyms([
'wolverine' => ['xmen', 'logan'],
'logan' => ['wolverine', 'xmen'],
'wow' => ['world of warcraft']
]);
```
```java
HashMap synonyms = new HashMap();
synonyms.put("wolverine", new String[] {"xmen", "logan"});
synonyms.put("logan", new String[] {"wolverine"});
client.index("movies").updateSynonymsSettings(synonyms);
```
```ruby
client.index('movies').update_synonyms({
wolverine: ['xmen', 'logan'],
logan: ['wolverine', 'xmen'],
wow: ['world of warcraft']
})
```
```go
synonyms := map[string][]string{
"wolverine": []string{"xmen", "logan"},
"logan": []string{"wolverine", "xmen"},
"wow": []string{"world of warcraft"},
}
client.Index("movies").UpdateSynonyms(&synonyms)
```
```csharp
var synonyms = new Dictionary>
{
{ "wolverine", new string[] { "xmen", "logan" } },
{ "logan", new string[] { "wolverine", "xmen" } },
{ "wow", new string[] { "world of warcraft" } }
};
await client.Index("movies").UpdateSynonymsAsync(synonyms);
```
```rust
let mut synonyms = std::collections::HashMap::new();
synonyms.insert(String::from("wolverine"), vec![String::from("xmen"), String::from("logan")]);
synonyms.insert(String::from("logan"), vec![String::from("xmen"), String::from("wolverine")]);
synonyms.insert(String::from("wow"), vec![String::from("world of warcraft")]);
let task: TaskInfo = client
.index("movies")
.set_synonyms(&synonyms)
.await
.unwrap();
```
```swift
let synonyms: [String: [String]] = [
"wolverine": ["xmen", "logan"],
"logan": ["wolverine", "xmen"],
"wow": ["world of warcraft"]
]
client.index("movies").updateSynonyms(synonyms) { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').updateSynonyms({
'wolverine': ['xmen', 'logan'],
'logan': ['wolverine', 'xmen'],
'wow': ['world of warcraft'],
});
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset synonyms
Reset the list of synonyms of an index to its default value.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/movies/settings/synonyms'
```
```js
client.index('movies').resetSynonyms()
```
```py
client.index('movies').reset_synonyms()
```
```php
$client->index('movies')->resetSynonyms();
```
```java
client.index("movies").resetSynonymsSettings();
```
```ruby
client.index('movies').reset_synonyms
```
```go
client.Index("movies").ResetSynonyms()
```
```csharp
await client.Index("movies").ResetSynonymsAsync();
```
```rust
let task: TaskInfo = client
.index("movies")
.reset_synonyms()
.await
.unwrap();
```
```swift
client.index("movies").resetSynonyms { (result) in
switch result {
case .success(let task):
print(task)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').resetSynonyms();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "movies",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2021-08-11T09:25:53.000000Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Typo tolerance
Typo tolerance helps users find relevant results even when their search queries contain spelling mistakes or typos. This setting allows you to configure the minimum word size for typos and disable typo tolerance for specific words or attributes.
[To learn more about typo tolerance, refer to our dedicated guide.](/learn/relevancy/typo_tolerance_settings)
#### Typo tolerance object
| Name | Type | Default Value | Description |
| :--------------------------------- | :--------------- | :------------ | :------------------------------------------------------------------------------- |
| **`enabled`** | Boolean | `true` | Whether typo tolerance is enabled or not |
| **`minWordSizeForTypos.oneTypo`** | Integer | `5` | The minimum word size for accepting 1 typo; must be between `0` and `twoTypos` |
| **`minWordSizeForTypos.twoTypos`** | Integer | `9` | The minimum word size for accepting 2 typos; must be between `oneTypo` and `255` |
| **`disableOnWords`** | Array of strings | Empty | An array of words for which the typo tolerance feature is disabled |
| **`disableOnAttributes`** | Array of strings | Empty | An array of attributes for which the typo tolerance feature is disabled |
#### Get typo tolerance settings
Get the typo tolerance settings of an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/books/settings/typo-tolerance'
```
```js
client.index('books').getTypoTolerance()
```
```py
client.index('books').get_typo_tolerance()
```
```php
$client->index('books')->getTypoTolerance();
```
```java
client.index("books").getTypoToleranceSettings();
```
```ruby
index('books').typo_tolerance
```
```go
client.Index("books").GetTypoTolerance()
```
```csharp
await client.Index("books").GetTypoToleranceAsync();
```
```rust
let typo_tolerance: TypoToleranceSettings = client
.index("books")
.get_typo_tolerance()
.await
.unwrap();
```
```dart
await client.index('books').getTypoTolerance();
```
###### Response: `200 OK`
```json
{
"enabled": true,
"minWordSizeForTypos": {
"oneTypo": 5,
"twoTypos": 9
},
"disableOnWords": [],
"disableOnAttributes": []
}
```
#### Update typo tolerance settings
Partially update the typo tolerance settings for an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```
{
"enabled": ,
"minWordSizeForTypos": {
"oneTypo": ,
"twoTypos":
},
"disableOnWords": [, , …],
"disableOnAttributes": [, , …]
}
```
| Name | Type | Default Value | Description |
| :--------------------------------- | :--------------- | :------------ | :------------------------------------------------------------------------------- |
| **`enabled`** | Boolean | `true` | Whether typo tolerance is enabled or not |
| **`minWordSizeForTypos.oneTypo`** | Integer | `5` | The minimum word size for accepting 1 typo; must be between `0` and `twoTypos` |
| **`minWordSizeForTypos.twoTypos`** | Integer | `9` | The minimum word size for accepting 2 typos; must be between `oneTypo` and `255` |
| **`disableOnWords`** | Array of strings | Empty | An array of words for which the typo tolerance feature is disabled |
| **`disableOnAttributes`** | Array of strings | Empty | An array of attributes for which the typo tolerance feature is disabled |
##### Example
```bash
curl \
-X PATCH 'MEILISEARCH_URL/indexes/books/settings/typo-tolerance' \
-H 'Content-Type: application/json' \
--data-binary '{
"minWordSizeForTypos": {
"oneTypo": 4,
"twoTypos": 10
},
"disableOnAttributes": ["title"]
}'
```
```js
client.index('books').updateTypoTolerance({
minWordSizeForTypos: {
oneTypo: 4,
twoTypos: 10
},
disableOnAttributes: [
'title'
]
})
```
```py
client.index('books').update_typo_tolerance({
'minWordSizeForTypos': {
'oneTypo': 4,
'twoTypos': 10
},
'disableOnAttributes': [
'title'
]
})
```
```php
$client->index('books')->updateTypoTolerance([
'minWordSizeForTypos' => [
'oneTypo' => 4,
'twoTypos' => 10
],
'disableOnAttributes' => [
'title'
]
]);
```
```java
TypoTolerance typoTolerance = new TypoTolerance();
HashMap minWordSizeTypos =
new HashMap() {
{
put("oneTypo", 4);
put("twoTypos", 10);
}
};
typoTolerance.setMinWordSizeForTypos(minWordSizeTypos);
typoTolerance.setDisableOnAttributes(new String[] {"title"});
client.index("books").updateTypoToleranceSettings(typoTolerance);
```
```ruby
index('books').update_typo_tolerance({
min_word_size_for_typos: {
one_typo: 4,
two_typos: 10
},
disable_on_attributes: ['title']
})
```
```go
client.Index("books").UpdateTypoTolerance(&meilisearch.TypoTolerance{
MinWordSizeForTypos: meilisearch.MinWordSizeForTypos{
OneTypo: 4,
TwoTypos: 10,
},
DisableOnAttributes: []string{"title"},
})
```
```csharp
var typoTolerance = new TypoTolerance {
DisableOnAttributes = new string[] { "title" },
MinWordSizeTypos = new TypoTolerance.TypoSize {
OneTypo = 4,
TwoTypos = 10
}
};
await client.Index("books").UpdateTypoToleranceAsync(typoTolerance);
```
```rust
let typo_tolerance = TypoToleranceSettings {
enabled: Some(false),
disable_on_attributes: None,
disable_on_words: None,
min_word_size_for_typos: None,
};
let task: TaskInfo = client
.index("books")
.set_typo_tolerance(&typo_tolerance)
.await
.unwrap();
```
```dart
final toUpdate = TypoTolerance(
minWordSizeForTypos: MinWordSizeForTypos(
oneTypo: 4,
twoTypos: 10,
),
disableOnAttributes: ['title'],
);
await client.index('books').updateTypoTolerance(toUpdate);
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2022-04-14T20:56:44.991039Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset typo tolerance settings
Reset an index's typo tolerance settings to their [default value](#typo-tolerance-object).
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/books/settings/typo-tolerance'
```
```js
client.index('books').resetTypoTolerance()
```
```py
client.index('books').reset_typo_tolerance()
```
```php
$client->index('books')->resetTypoTolerance();
```
```java
client.index("books").resetTypoToleranceSettings();
```
```ruby
index('books').reset_typo_tolerance
```
```go
client.Index("books").ResetTypoTolerance()
```
```csharp
await client.Index("books").ResetTypoToleranceAsync();
```
```rust
let task: TaskInfo = client
.index("books")
.reset_typo_tolerance()
.await
.unwrap();
```
```dart
await client.index('books').resetTypoTolerance();
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2022-04-14T20:53:32.863107Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
### Embedders
Embedders translate documents and queries into vector embeddings. You must configure at least one embedder to use AI-powered search.
#### Embedders object
The embedders object may contain up to 256 embedder objects. Each embedder object must be assigned a unique name:
```json
{
"default": {
"source": "huggingFace",
"model": "BAAI/bge-base-en-v1.5",
"documentTemplate": "A movie titled '{{doc.title}}' whose description starts with {{doc.overview|truncatewords: 20}}"
},
"openai": {
"source": "openAi",
"apiKey": "OPENAI_API_KEY",
"model": "text-embedding-3-small",
"documentTemplate": "A movie titled {{doc.title}} whose description starts with {{doc.overview|truncatewords: 20}}",
}
}
```
These embedder objects may contain the following fields:
| Name | Type | Default Value | Description |
| ------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`source`** | String | Empty | The third-party tool that will generate embeddings from documents. Must be `openAi`, `huggingFace`, `ollama`, `rest`, or `userProvided` |
| **`url`** | String | `http://localhost:11434/api/embeddings` | The URL Meilisearch contacts when querying the embedder |
| **`apiKey`** | String | Empty | Authentication token Meilisearch should send with each request to the embedder. If not present, Meilisearch will attempt to read it from environment variables |
| **`model`** | String | Empty | The model your embedder uses when generating vectors |
| **`documentTemplate`** | String | `{% for field in fields %} {% if field.is_searchable and not field.value == nil %}{{ field.name }}: {{ field.value }} {% endif %} {% endfor %}` | Template defining the data Meilisearch sends to the embedder |
| **`documentTemplateMaxBytes`** | Integer | `400` | Maximum allowed size of rendered document template |
| **`dimensions`** | Integer | Empty | Number of dimensions in the chosen model. If not supplied, Meilisearch tries to infer this value |
| **`revision`** | String | Empty | Model revision hash |
| **`distribution`** | Object | Empty | Describes the natural distribution of search results. Must contain two fields, `mean` and `sigma`, each containing a numeric value between `0` and `1` |
| **`request`** | Object | Empty | A JSON value representing the request Meilisearch makes to the remote embedder |
| **`response`** | Object | Empty | A JSON value representing the response Meilisearch expects from the remote embedder |
| **`binaryQuantized`** | Boolean | Empty | Once set to `true`, irreversibly converts all vector dimensions to 1-bit values |
| **`indexingEmbedder`** | Object | Empty | Configures embedder to vectorize documents during indexing |
| **`searchEmbedder`** | Object | Empty | Configures embedder to vectorize search queries |
| **`pooling`** | String | `"useModel"` | Pooling method for Hugging Face embedders |
#### Get embedder settings
Get the embedders configured for an index.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/INDEX_NAME/settings/embedders'
```
```ruby
client.index('INDEX_NAME').embedders
```
###### Response: `200 OK`
```json
{
"default": {
"source": "openAi",
"apiKey": "OPENAI_API_KEY",
"model": "text-embedding-3-small",
"documentTemplate": "A movie titled {{doc.title}} whose description starts with {{doc.overview|truncatewords: 20}}",
"dimensions": 1536
}
}
```
#### Update embedder settings
Partially update the embedder settings for an index. When this setting is updated Meilisearch may reindex all documents and regenerate their embeddings.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Body
```json
{
"default": {
"source": ,
"url": ,
"apiKey": ,
"model": ,
"documentTemplate": ,
"documentTemplateMaxBytes": ,
"dimensions": ,
"revision": ,
"distribution": {
"mean": ,
"sigma":
},
"request": { … },
"response": { … },
"headers": { … },
"binaryQuantized": ,
"pooling": ,
"indexingEmbedder": { … },
"searchEmbedder": { … }
}
}
```
Set an embedder to `null` to remove it from the embedders list.
###### `source`
Use `source` to configure an embedder's source. The source corresponds to a service that generates embeddings from your documents.
Meilisearch supports the following sources:
- `openAi`
- `huggingFace`
- `ollama`
- `rest`
- `userProvided`
- `composite`
`rest` is a generic source compatible with any embeddings provider offering a REST API.
Use `userProvided` when you want to generate embeddings manually. In this case, you must include vector data in your documents' `_vectors` field. You must also generate vectors for search queries.
This field is mandatory.
####### Composite embedders
Choose `composite` to use one embedder during indexing time, and another embedder at search time. Must be used together with [`indexingEmbedder` and `searchEmbedder`](#indexingembedder-and-searchembedder).
This is an experimental feature. Use the experimental features endpoint to activate it:
```sh
curl \
-X PATCH 'MEILISEARCH_URL/experimental-features/' \
-H 'Content-Type: application/json' \
--data-binary '{
"compositeEmbedders": true
}'
```
###### `url`
Meilisearch queries `url` to generate vector embeddings for queries and documents. `url` must point to a REST-compatible embedder. You may also use `url` to work with proxies, such as when targeting `openAi` from behind a proxy.
This field is mandatory when using `rest` embedders.
This field is optional when using `ollama` and `openAi` embedders. `ollama` URLs must end with either `/api/embed` or `/api/embeddings`.
This field is incompatible with `huggingFace` and `userProvided` embedders.
###### `apiKey`
Authentication token Meilisearch should send with each request to the embedder.
This field is mandatory if using a protected `rest` embedder.
This field is optional for `openAI` and `ollama` embedders. If you don't specify `apiKey`, Meilisearch will attempt to read it from environment variables `OPENAI_API_KEY` and `MEILI_OLLAMA_URL`, respectively.
This field is incompatible with `huggingFace` and `userProvided` embedders.
###### `model`
The model your embedder uses when generating vectors. These are the officially supported models Meilisearch supports:
- `openAi`: `text-embedding-3-small`, `text-embedding-3-large`, `openai-text-embedding-ada-002`
- `huggingFace`: `BAAI/bge-base-en-v1.5`
Other models, such as [HuggingFace's BERT models](https://huggingface.co/models?other=bert) or those provided by Ollama and REST embedders may also be compatible with Meilisearch.
This field is mandatory for `Ollama` embedders.
This field is optional for `openAi` and `huggingFace`. By default, Meilisearch uses `text-embedding-3-small` and `BAAI/bge-base-en-v1.5` respectively.
This field is incompatible with `rest` and `userProvided` embedders.
###### `documentTemplate`
`documentTemplate` is a string containing a [Liquid template](https://shopify.github.io/liquid/basics/introduction). Meillisearch interpolates the template for each document and sends the resulting text to the embedder. The embedder then generates document vectors based on this text.
You may use the following context values:
- `{{doc.FIELD}}`: `doc` stands for the document itself. `FIELD` must correspond to an attribute present on all documents value will be replaced by the value of that field in the input document
- `{{fields}}`: a list of all the `field`s appearing in any document in the index. Each `field` object in this list has the following properties:
- `name`: the field's attribute
- `value`: the field's value
- `is_searchable`: whether the field is present in the searchable attributes list
If a `field` does not exist in a document, its `value` is `nil`.
For best results, build short templates that only contain highly relevant data. If working with a long field, consider [truncating it](https://shopify.github.io/liquid/filters/truncatewords/). If you do not manually set it, `documentTemplate` will include all searchable and non-null document fields. This may lead to suboptimal performance and relevancy.
This field is incompatible with `userProvided` embedders.
This field is optional but strongly encouraged for all other embedders.
###### `documentTemplateMaxBytes`
The maximum size of a rendered document template. Longer texts are truncated to fit the configured limit.
`documentTemplateMaxBytes` must be an integer. It defaults to `400`.
This field is incompatible with `userProvided` embedders.
This field is optional for all other embedders.
###### `dimensions`
Number of dimensions in the chosen model. If not supplied, Meilisearch tries to infer this value.
In most cases, `dimensions` should be the exact same value of your chosen model. Setting `dimensions` to a value lower than the model may lead to performance improvements and is only supported in the following OpenAI models:
- `openAi`: `text-embedding-3-small`, `text-embedding-3-large`
This field is mandatory for `userProvided` embedders.
This field is optional for `openAi`, `huggingFace`, `ollama`, and `rest` embedders.
###### `revision`
Use this field to use a specific revision of a model.
This field is optional for the `huggingFace` embedder.
This field is incompatible with all other embedders.
###### `request`
`request` must be a JSON object with the same structure and data of the request you must send to your `rest` embedder.
The field containing the input text Meilisearch should send to the embedder must be replaced with `"{{text}}"`:
```json
{
"source": "rest",
"request": {
"prompt": "{{text}}"
…
},
…
}
```
If sending multiple documents in a single request, replace the input field with `["{{text}}", "{{..}}"]`:
```json
{
"source": "rest",
"request": {
"prompt": ["{{text}}", "{{..}}"]
…
},
…
}
```
This field is mandatory when using the `rest` embedder.
This field is incompatible with all other embedders.
###### `response`
`response` must be a JSON object with the same structure and data of the response you expect to receive from your `rest` embedder.
The field containing the embedding itself must be replaced with `"{{embedding}}"`:
```json
{
"source": "rest",
"response": {
"data": "{{embedding}}"
…
},
…
}
```
If a single response includes multiple embeddings, the field containing the embedding itself must be an array with two items. One must declare the location and structure of a single embedding, while the second item should be `"{{..}}"`:
```json
{
"source": "rest",
"response": {
"data": [
{
"embedding": "{{embedding}}"
},
"{{..}}"
]
…
},
…
}
```
This field is mandatory when using the `rest` embedder.
This field is incompatible with all other embedders.
###### `distribution`
For mathematical reasons, the `_rankingScore` of semantic search results tend to be closely grouped around an average value that depends on the embedder and model used. This may result in relevant semantic hits being underrepresented and irrelevant semantic hits being overrepresented compared with keyword search hits.
Use `distribution` when configuring an embedder to correct the returned `_rankingScore`s of the semantic hits with an affine transformation:
```sh
curl \
-X PATCH 'MEILISEARCH_URL/indexes/INDEX_NAME/settings' \
-H 'Content-Type: application/json' \
--data-binary '{
"embedders": {
"default": {
"source": "huggingFace",
"model": "MODEL_NAME",
"distribution": {
"mean": 0.7,
"sigma": 0.3
}
}
}
}'
```
Configuring `distribution` requires a certain amount of trial and error, in which you must perform semantic searches and monitor the results. Based on their `rankingScore`s and relevancy, add the observed `mean` and `sigma` values for that index.
`distribution` is an optional field compatible with all embedder sources. It must be an object with two fields:
- `mean`: a number between `0` and `1` indicating the semantic score of "somewhat relevant" hits before using the `distribution` setting
- `sigma`: a number between `0` and `1` indicating the average absolute difference in `_rankingScore`s between "very relevant" hits and "somewhat relevant" hits, and "somewhat relevant" hits and "irrelevant hits".
Changing `distribution` does not trigger a reindexing operation.
###### `headers`
`headers` must be a JSON object whose keys represent the name of additional headers to send in requests to embedders, and whose values represent the value of these additional headers.
By default, Meilisearch sends the following headers with all requests to `rest` embedders:
- `Authorization: Bearer EMBEDDER_API_KEY` (only if `apiKey` was provided)
- `Content-Type: application/json`
If `headers` includes one of these fields, the explicitly declared values take precedence over the defaults.
This field is optional when using the `rest` embedder.
This field is incompatible with all other embedders.
###### `binaryQuantized`
When set to `true`, compresses vectors by representing each dimension with 1-bit values. This reduces the relevancy of semantic search results, but greatly reduces database size.
This option can be useful when working with large Meilisearch projects. Consider activating it if your project contains more than one million documents and uses models with more than 1400 dimensions.
**Activating `binaryQuantized` is irreversible.** Once enabled, Meilisearch converts all vectors and discards all vector data that does fit within 1-bit. The only way to recover the vectors' original values is to re-vectorize the whole index in a new embedder.
###### `pooling`
Configure how Meilisearch should merge individual tokens into a single embedding.
`pooling` must be one of the following strings:
- `"useModel"`: Meilisearch will fetch the pooling method from the model configuration. Default value for new embedders
- `"forceMean"`: always use mean pooling. Default value for embedders created in Meilisearch \<=v1.13
- `"forceCls"`: always use CLS pooling
If in doubt, use `"useModel"`. `"forceMean"` and `"forceCls"` are compatibility options that might be necessary for certain embedders and models.
`pooling` is optional for embedders with the `huggingFace` source.
`pooling` is invalid for all other embedder sources.
###### `indexingEmbedder` and `searchEmbedder`
When using a [composite embedder](#composite-embedders), configure separate embedders Meilisearch should use when vectorizing documents and search queries.
`indexingEmbedder` often benefits from the higher bandwidth and speed of remote providers so it can vectorize large batches of documents quickly. `searchEmbedder` may often benefits from the lower latency of processing queries locally.
Both fields must be an object and accept the same fields as a regular embedder, with the following exceptions:
- `indexingEmbedder` and `searchEmbedder` must use the same model for generating embeddings
- `indexingEmbedder` and `searchEmbedder` must have identical `dimension`s and `pooling` methods
- `source` is mandatory for both `indexingEmbedder` and `searchEmbedder`
- Neither sub-embedder can set `source` to `composite` or `userProvided`
- Neither `binaryQuantized` and `distribution` are valid sub-embedder fields and must always be declared in the main embedder
- `documentTemplate` and `documentTemplateMaxBytes` are invalid fields for `searchEmbedder`
- `documentTemplate` and `documentTemplateMaxBytes` are mandatory for `indexingEmbedder`, if applicable to its source
`indexingEmbedder` and `searchEmbedder` are mandatory when using the `composite` source.
`indexingEmbedder` and `searchEmbedder` are incompatible with all other embedder sources.
##### Example
```bash
curl \
-X PATCH 'MEILISEARCH_URL/indexes/INDEX_NAME/settings/embedders' \
-H 'Content-Type: application/json' \
--data-binary '{
"default": {
"source": "openAi",
"apiKey": "OPEN_AI_API_KEY",
"model": "text-embedding-3-small",
"documentTemplate": "A document titled '{{doc.title}}' whose description starts with {{doc.overview|truncatewords: 20}}"
}
}'
```
```js
client.index('INDEX_NAME').updateEmbedders({
default: {
source: 'openAi',
apiKey: 'OPEN_AI_API_KEY',
model: 'text-embedding-3-small',
documentTemplate: 'A document titled '{{doc.title}}' whose description starts with {{doc.overview|truncatewords: 20}}'
}
});
```
```php
$client->updateEmbedders([
'default' => [
'source' => 'openAi',
'apiKey' => 'OPEN_AI_API_KEY',
'model' => 'text-embedding-3-small',
'documentTemplate' => 'A document titled '{{doc.title}}' whose description starts with {{doc.overview|truncatewords: 20}}'
]
]);
```
```ruby
client.index('INDEX_NAME').update_embedders(
default: {
source: 'openAi',
api_key: 'OPEN_AI_API_KEY',
model: 'text-embedding-3-small',
document_template: "A document titled '{{doc.title}}' whose description starts with {{doc.overview|truncatewords: 20}}"
}
)
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "kitchenware",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2024-05-11T09:33:12.691402Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
#### Reset embedder settings
Removes all embedders from your index.
To remove a single embedder, use the [update embedder settings endpoint](#update-embedder-settings) and set the target embedder to `null`.
##### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
##### Example
```bash
curl \
-X DELETE 'MEILISEARCH_URL/indexes/INDEX_NAME/settings/embedders'
```
```ruby
client.index('INDEX_NAME').reset_embedders
```
###### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": "books",
"status": "enqueued",
"type": "settingsUpdate",
"enqueuedAt": "2022-04-14T20:53:32.863107Z"
}
```
You can use the returned `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task).
---
title: Snapshots — Meilisearch API reference
description: The /snapshots route creates database snapshots. Use snapshots to backup your Meilisearch data.
---
## Snapshots
The `/snapshot` route allows you to create database snapshots. Snapshots are `.snapshot` files that can be used to make quick backups of Meilisearch data.
[Learn more about snapshots.](/learn/advanced/snapshots)
Meilisearch Cloud does not support the `/snapshots` route.
### Create a snapshot
Triggers a snapshot creation task. Once the process is complete, Meilisearch creates a snapshot in the [snapshot directory](/learn/self_hosted/configure_meilisearch_at_launch#snapshot-destination). If the snapshot directory does not exist yet, it will be created.
Snapshot tasks take priority over other tasks in the queue.
[Learn more about asynchronous operations](/learn/async/asynchronous_operations).
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/snapshots'
```
```js
client.createSnapshot()
```
```py
client.create_snapshot()
```
```php
$client->createSnapshot();
```
```java
client.createSnapshot();
```
```ruby
client.create_snapshot
```
```go
client.CreateSnapshot()
```
```csharp
await client.CreateSnapshotAsync();
```
```rust
client
.create_snapshot()
.await
.unwrap();
```
```swift
let task = try await self.client.createSnapshot()
```
##### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": null,
"status": "enqueued",
"type": "snapshotCreation",
"enqueuedAt": "2023-06-21T11:09:36.417758Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task)
---
title: Stats — Meilisearch API reference
description: The /stats route you gives extended information and metrics about indexes and the Meilisearch database.
---
## Stats
The `/stats` route gives extended information and metrics about indexes and the Meilisearch database.
### Stats object
```json
{
"databaseSize": 447819776,
"usedDatabaseSize": 196608,
"lastUpdate": "2019-11-15T11:15:22.092896Z",
"indexes": {
"movies": {
"numberOfDocuments": 19654,
"numberOfEmbeddedDocuments": 1,
"numberOfEmbeddings": 1,
"isIndexing": false,
"fieldDistribution": {
"poster": 19654,
"overview": 19654,
"title": 19654,
"id": 19654,
"release_date": 19654
}
},
"books": {
"numberOfDocuments": 5,
"numberOfEmbeddedDocuments": 5,
"numberOfEmbeddings": 10,
"isIndexing": false,
"fieldDistribution": {
"id": 5,
"title": 5,
"author": 5,
"price": 5,
"genres": 5
}
}
}
}
```
| Name | Type | Description |
| ------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| **`databaseSize`** | Integer | Storage space claimed by Meilisearch and LMDB in bytes |
| **`usedDatabaseSize`** | Integer | Storage space used by the database in bytes, excluding unused space claimed by LMDB |
| **`lastUpdate`** | String | When the last update was made to the database in the [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format |
| **`indexes`** | Object | Object containing the statistics for each index found in the database |
| **`numberOfDocuments`** | Integer | Total number of documents in an index |
| **`numberOfEmbeddedDocuments`** | Integer | Total number of documents with at least one embedding |
| **`numberOfEmbeddings`** | Integer | Total number of embeddings in an index |
| **`isIndexing`** | Boolean | If `true`, the index is still processing documents and attempts to search will result in undefined behavior |
| **`fieldDistribution`** | Object | Shows every field in the index along with the total number of documents containing that field in said index |
`fieldDistribution` is not impacted by `searchableAttributes` or `displayedAttributes`. Even if a field is not displayed or searchable, it will still appear in the `fieldDistribution` object.
### Get stats of all indexes
Get stats of all indexes.
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/stats'
```
```js
client.getStats()
```
```py
client.get_all_stats()
```
```php
$client->stats();
```
```java
client.getStats();
```
```ruby
client.stats
```
```go
client.GetStats()
```
```csharp
await client.GetStatsAsync();
```
```rust
let stats: ClientStats = client
.get_stats()
.await
.unwrap();
```
```swift
client.allStats { (result) in
switch result {
case .success(let stats):
print(stats)
case .failure(let error):
print(error)
}
}
```
```dart
await client.getStats();
```
##### Response: `200 Ok`
```json
{
"databaseSize": 447819776,
"usedDatabaseSize": 196608,
"lastUpdate": "2019-11-15T11:15:22.092896Z",
"indexes": {
"movies": {
"numberOfDocuments": 19654,
"numberOfEmbeddedDocuments": 1,
"numberOfEmbeddings": 1,
"isIndexing": false,
"fieldDistribution": {
"poster": 19654,
"overview": 19654,
"title": 19654,
"id": 19654,
"release_date": 19654
}
},
"books": {
"numberOfDocuments": 5,
"numberOfEmbeddedDocuments": 5,
"numberOfEmbeddings": 10,
"isIndexing": false,
"fieldDistribution": {
"id": 5,
"title": 5,
"author": 5,
"price": 5,
"genres": 5
}
}
}
}
```
### Get stats of an index
Get stats of an index.
#### Path parameters
| Name | Type | Description |
| :---------------- | :----- | :------------------------------------------------------------------------ |
| **`index_uid`** * | String | [`uid`](/learn/getting_started/indexes#index-uid) of the requested index |
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/indexes/movies/stats'
```
```js
client.index('movies').getStats()
```
```py
client.index('movies').get_stats()
```
```php
$client->index('movies')->stats();
```
```java
client.index("movies").getStats();
```
```ruby
client.index('movies').stats
```
```go
client.Index("movies").GetStats()
```
```csharp
await client.Index("movies").GetStatsAsync();
```
```rust
let stats: IndexStats = client
.index("movies")
.get_stats()
.await
.unwrap();
```
```swift
client.index("movies").stats { (result) in
switch result {
case .success(let stats):
print(stats)
case .failure(let error):
print(error)
}
}
```
```dart
await client.index('movies').getStats();
```
##### Response: `200 Ok`
```json
{
"numberOfDocuments": 19654,
"numberOfEmbeddedDocuments": 1,
"numberOfEmbeddings": 1,
"isIndexing": false,
"fieldDistribution": {
"poster": 19654,
"overview": 19654,
"title": 19654,
"id": 19654,
"release_date": 19654
}
}
```
---
title: Health — Meilisearch API reference
description: The /health route allows you to verify the status and availability of a Meilisearch instance.
---
## Health
The `/health` route allows you to verify the status and availability of a Meilisearch instance.
### Get health
Get health of Meilisearch server.
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/health'
```
```js
client.health()
```
```py
client.health()
```
```php
$client->health();
```
```java
client.health();
```
```ruby
client.health
```
```go
client.Health()
```
```csharp
await client.HealthAsync();
```
```rust
// health() return an Err() if the server is not healthy, so this example would panic due to the unwrap
client
.health()
.await
.unwrap();
```
```swift
client.health { (result) in
switch result {
case .success:
print("Healthy!")
case .failure(let error):
print(error)
}
}
```
```dart
await client.health();
```
##### Response: `200 OK`
```json
{ "status": "available" }
```
---
title: Version — Meilisearch API reference
description: The /version route allows you to check the version of a running Meilisearch instance.
---
## Version
The `/version` route allows you to check the version of a running Meilisearch instance.
### Version object
| Name | Description |
| :--------------- | :----------------------------------------------------- |
| **`commitSha`** | Commit identifier that tagged the `pkgVersion` release |
| **`commitDate`** | Date when the `commitSha` was created |
| **`pkgVersion`** | Meilisearch version |
### Get version of Meilisearch
Get version of Meilisearch.
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/version'
```
```js
client.getVersion()
```
```py
client.get_version()
```
```php
$client->version();
```
```java
client.getVersion();
```
```ruby
client.version
```
```go
client.GetVersion()
```
```csharp
await client.GetVersionAsync();
```
```rust
let version: Version = client
.get_version()
.await
.unwrap();
```
```swift
client.version { (result) in
switch result {
case .success(let version):
print(version)
case .failure(let error):
print(error)
}
}
```
```dart
await client.getVersion();
```
##### Response: `200 Ok`
```json
{
"commitSha": "b46889b5f0f2f8b91438a08a358ba8f05fc09fc1",
"commitDate": "2019-11-15T09:51:54.278247+00:00",
"pkgVersion": "0.1.1"
}
```
---
title: Dumps — Meilisearch API reference
description: The /dumps route allows the creation of database dumps. Use dumps to migrate Meilisearch to a new version.
---
## Dumps
The `/dumps` route allows the creation of database dumps. Dumps are `.dump` files that can be used to restore Meilisearch data or migrate between different versions.
Meilisearch Cloud does not support the `/dumps` route.
[Learn more about dumps](/learn/advanced/dumps).
### Create a dump
Triggers a dump creation task. Once the process is complete, a dump is created in the [dump directory](/learn/self_hosted/configure_meilisearch_at_launch#dump-directory). If the dump directory does not exist yet, it will be created.
Dump tasks take priority over all other tasks in the queue. This means that a newly created dump task will be processed as soon as the current task is finished.
[Learn more about asynchronous operations](/learn/async/asynchronous_operations).
#### Example
```bash
curl \
-X POST 'MEILISEARCH_URL/dumps'
```
```js
client.createDump()
```
```py
client.create_dump()
```
```php
$client->createDump();
```
```java
client.createDump();
```
```ruby
client.create_dump
```
```go
resp, err := client.CreateDump()
```
```csharp
await client.CreateDumpAsync();
```
```rust
client
.create_dump()
.await
.unwrap();
```
```swift
client.createDump { result in
switch result {
case .success(let dumpStatus):
print(dumpStatus)
case .failure(let error):
print(error)
}
}
```
```dart
await client.createDump();
```
##### Response: `202 Accepted`
```json
{
"taskUid": 1,
"indexUid": null,
"status": "enqueued",
"type": "dumpCreation",
"enqueuedAt": "2022-06-21T16:10:29.217688Z"
}
```
You can use this `taskUid` to get more details on [the status of the task](/reference/api/tasks#get-one-task)
---
title: Experimental — Meilisearch API reference
description: The /experimental-features route allows you to manage some of Meilisearch's experimental features.
---
## Experimental
The `/experimental-features` route allows you to activate or deactivate some of Meilisearch's [experimental features](/learn/resources/experimental_features_overview).
This route is **synchronous**. This means that no task object will be returned, and any activated or deactivated features will be made available or unavailable immediately.
The experimental API route is not compatible with all experimental features. Consult the [experimental feature overview](/learn/resources/experimental_features_overview) for a compatibility list.
### Experimental features object
```json
{
"metrics": false,
"logsRoute": true,
"containsFilter": false,
"editDocumentsByFunction": false,
"network": false
}
```
| Name | Type | Description |
| :---------------------------- | :------ | :--------------------------------------------- |
| **`metrics`** | Boolean | `true` if feature is active, `false` otherwise |
| **`logsRoute`** | Boolean | `true` if feature is active, `false` otherwise |
| **`containsFilter`** | Boolean | `true` if feature is active, `false` otherwise |
| **`editDocumentsByFunction`** | Boolean | `true` if feature is active, `false` otherwise |
| **`network`** | Boolean | `true` if feature is active, `false` otherwise |
### Get all experimental features
Get a list of all experimental features that can be activated via the `/experimental-features` route and whether or not they are currently activated.
#### Example
```bash
curl \
-X GET 'MEILISEARCH_URL/experimental-features/'
```
```ruby
client.experimental_features
```
```go
client.ExperimentalFeatures().Get()
```
```rust
let client = Client::new("http://localhost:7700", Some("apiKey"));
let features = ExperimentalFeatures::new(&client);
let res = features
.get()
.await
.unwrap();
```
##### Response: `200 Ok`
```json
{
"metrics": false,
"logsRoute": true,
"containsFilter": false,
"editDocumentsByFunction": false,
"network": false
}
```
### Configure experimental features
Activate or deactivate experimental features.
```bash
curl \
-X PATCH 'MEILISEARCH_URL/experimental-features/' \
-H 'Content-Type: application/json' \
--data-binary '{
"metrics": true
}'
```
```ruby
client.update_experimental_features(metrics: true)
```
```go
client.ExperimentalFeatures().SetMetrics(true).Update()
```
```rust
let client = Client::new("http://localhost:7700", Some("apiKey"));
let features = ExperimentalFeatures::new(&client);
features.set_metrics(true)
let res = features
.update()
.await
.unwrap();
```
Setting a field to `null` leaves its value unchanged.
##### Body
```
{: