# Sort search results
Source: https://www.meilisearch.com/docs/learn/filtering_and_sorting/sort_search_results

By default, Meilisearch sorts results according to their relevancy. You can alter this behavior so users can decide at search time results they want to see first.

By default, Meilisearch focuses on ordering results according to their relevancy. You can alter this sorting behavior so users can decide at search time what type of results they want to see first.

This can be useful in many situations, such as when a user wants to see the cheapest products available in a webshop.

<Tip>
  Sorting at search time can be particularly effective when combined with [placeholder searches](/reference/api/search#placeholder-search).
</Tip>

## Configure Meilisearch for sorting at search time

To allow your users to sort results at search time you must:

1. Decide which attributes you want to use for sorting
2. Add those attributes to the `sortableAttributes` index setting
3. Update Meilisearch's [ranking rules](/learn/relevancy/relevancy) (optional)

<Note>
  Meilisearch sorts strings in lexicographic order based on their byte values. For example, `á`, which has a value of 225, will be sorted after `z`, which has a value of 122.

  Uppercase letters are sorted as if they were lowercase. They will still appear uppercase in search results.
</Note>

### Add attributes to `sortableAttributes`

Meilisearch allows you to sort results based on document fields. Only fields containing numbers, strings, arrays of numeric values, and arrays of string values can be used for sorting.

After you have decided which fields you will allow your users to sort on, you must add their attributes to the [`sortableAttributes` index setting](/reference/api/settings#sortable-attributes).

<Warning>
  If a field has values of different types across documents, Meilisearch will give precedence to numbers over strings. This means documents with numeric field values will be ranked higher than those with string values.

  This can lead to unexpected behavior when sorting. For optimal user experience, only sort based on fields containing the same type of value.
</Warning>

#### Example

Suppose you have collection of books containing the following fields:

```json theme={null}
[
  {
    "id": 1,
    "title": "Solaris",
    "author": "Stanislaw Lem",
    "genres": [
      "science fiction"
    ],
    "rating": {
      "critics": 95,
      "users": 87
    },
    "price": 5.00
  },
  …
]
```

If you are using this dataset in a webshop, you might want to allow your users to sort on `author` and `price`:

<CodeGroup>
  ```bash cURL theme={null}
  curl \
    -X PUT 'MEILISEARCH_URL/indexes/books/settings/sortable-attributes' \
    -H 'Content-Type: application/json' \
    --data-binary '[
      "author",
      "price"
    ]'
  ```

  ```javascript JS theme={null}
  client.index('books').updateSortableAttributes([
      'author',
      'price'
    ])
  ```

  ```python Python theme={null}
  client.index('books').update_sortable_attributes([
    'author',
    'price'
  ])
  ```

  ```php PHP theme={null}
  $client->index('books')->updateSortableAttributes([
    'author',
    'price'
  ]);
  ```

  ```java Java theme={null}
  client.index("books").updateSortableAttributesSettings(new String[] {"price", "author"});
  ```

  ```ruby Ruby theme={null}
  client.index('books').update_sortable_attributes(['author', 'price'])
  ```

  ```go Go theme={null}
  sortableAttributes := []string{
    "author",
    "price",
  }
  client.Index("books").UpdateSortableAttributes(&sortableAttributes)
  ```

  ```csharp C# theme={null}
  await client.Index("books").UpdateSortableAttributesAsync(new [] { "price", "author" });
  ```

  ```rust Rust theme={null}
  let sortable_attributes = [
    "author",
    "price"
  ];

  let task: TaskInfo = client
    .index("books")
    .set_sortable_attributes(&sortable_attributes)
    .await
    .unwrap();
  ```

  ```swift Swift theme={null}
  client.index("books").updateSortableAttributes(["price", "author"]) { (result: Result<TaskInfo, Swift.Error>) in
    switch result {
    case .success(let task):
      print(task)
    case .failure(let error):
      print(error)
    }
  }
  ```

  ```dart Dart theme={null}
  await client.index('books').updateSortableAttributes(['author', 'price']);
  ```
</CodeGroup>

### Customize ranking rule order (optional)

When users sort results at search time, [Meilisearch's ranking rules](/learn/relevancy/relevancy) are set up so the top matches emphasize relevant results over sorting order. You might need to alter this behavior depending on your application's needs.

This is the default configuration of Meilisearch's ranking rules:

```json theme={null}
[
  "words",
  "typo",
  "proximity",
  "attribute",
  "sort",
  "exactness"
]
```

`"sort"` is in fifth place. This means it acts as a tie-breaker rule: Meilisearch will first place results closely matching search terms at the top of the returned documents list and only then will apply the `"sort"` parameters as requested by the user. In other words, by default Meilisearch provides a very relevant sorting.

Placing `"sort"` ranking rule higher in the list will emphasize exhaustive sorting over relevant sorting: your results will more closely follow the sorting order your user chose, but will not be as relevant.

<Tip>
  Sorting applies equally to all documents. Meilisearch does not offer native support for promoting, pinning, and boosting specific documents so they are displayed more prominently than other search results. Consult these Meilisearch blog articles for workarounds on [implementing promoted search results with React InstantSearch](https://blog.meilisearch.com/promoted-search-results-with-react-instantsearch) and [document boosting](https://blog.meilisearch.com/document-boosting).
</Tip>

#### Example

If your users care more about finding cheaper books than they care about finding specific matches to their queries, you can place `sort` much higher in the ranking rules:

<CodeGroup>
  ```bash cURL theme={null}
  curl \
    -X PUT 'MEILISEARCH_URL/indexes/books/settings/ranking-rules' \
    -H 'Content-Type: application/json' \
    --data-binary '[
      "words",
      "sort",
      "typo",
      "proximity",
      "attribute",
      "exactness"
    ]'
  ```

  ```javascript JS theme={null}
  client.index('books').updateRankingRules([
    'words',
    'sort',
    'typo',
    'proximity',
    'attribute',
    'exactness'
  ])
  ```

  ```python Python theme={null}
  client.index('books').update_ranking_rules([
      'words',
      'sort',
      'typo',
      'proximity',
      'attribute',
      'exactness'
  ])
  ```

  ```php PHP theme={null}
  $client->index('books')->updateRankingRules([
    'words',
    'sort',
    'typo',
    'proximity',
    'attribute',
    'exactness'
  ]);
  ```

  ```java Java theme={null}
  Settings settings = new Settings();
  settings.setRankingRules(new String[]
  {
    "words",
    "sort",
    "typo",
    "proximity",
    "attribute",
    "exactness"
  });
  client.index("books").updateSettings(settings);
  ```

  ```ruby Ruby theme={null}
  client.index('books').update_ranking_rules([
    'words',
    'sort',
    'typo',
    'proximity',
    'attribute',
    'exactness'
  ])
  ```

  ```go Go theme={null}
  rankingRules := []string{
    "words",
    "sort",
    "typo",
    "proximity",
    "attribute",
    "exactness",
  }
  client.Index("books").UpdateRankingRules(&rankingRules)
  ```

  ```csharp C# theme={null}
  await client.Index("books").UpdateRankingRulesAsync(new[]
  {
      "words",
      "sort",
      "typo",
      "proximity",
      "attribute",
      "exactness"
  });
  ```

  ```rust Rust theme={null}
  let ranking_rules = [
    "words",
    "sort",
    "typo",
    "proximity",
    "attribute",
    "exactness"
  ];

  let task: TaskInfo = client
    .index("books")
    .set_ranking_rules(&ranking_rules)
    .await
    .unwrap();
  ```

  ```swift Swift theme={null}
  let rankingRules: [String] = [
    "words",
    "sort",
    "typo",
    "proximity",
    "attribute",
    "exactness"
  ]
  client.index("books").updateRankingRules(rankingRules) { (result) in
    switch result {
    case .success(let task):
      print(task)
    case .failure(let error):
      print(error)
    }
  }
  ```

  ```dart Dart theme={null}
  await client.index('books').updateRankingRules(
      ['words', 'sort', 'typo', 'proximity', 'attribute', 'exactness']);
  ```
</CodeGroup>

## Sort results at search time

After configuring `sortableAttributes`, you can use the [`sort` search parameter](/reference/api/search#sort) to control the sorting order of your search results.

`sort` expects a list of attributes that have been added to the `sortableAttributes` list.

Attributes must be given as `attribute:sorting_order`. In other words, each attribute must be followed by a colon (`:`) and a sorting order: either ascending (`asc`) or descending (`desc`).

When using the `POST` route, `sort` expects an array of strings:

```json theme={null}
"sort": [
  "price:asc",
  "author:desc"
]
```

When using the `GET` route, `sort` expects a comma-separated string:

```
sort="price:desc,author:asc"
```

The order of `sort` values matter: the higher an attribute is in the search parameter value, the more Meilisearch will prioritize it over attributes placed lower. In our example, if multiple documents have the same value for `price`, Meilisearch will decide the order between these similarly-priced documents based on their `author`.

### Example

Suppose you are searching for books in a webshop and want to see the cheapest science fiction titles. This query searches for `"science fiction"` books sorted from cheapest to most expensive:

<CodeGroup>
  ```bash cURL theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/books/search' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "q": "science fiction",
      "sort": ["price:asc"]
    }'
  ```

  ```javascript JS theme={null}
  client.index('books').search('science fiction', {
    sort: ['price:asc'],
  })
  ```

  ```python Python theme={null}
  client.index('books').search('science fiction', {
    'sort': ['price:asc']
  })
  ```

  ```php PHP theme={null}
  $client->index('books')->search('science fiction', ['sort' => ['price:asc']]);
  ```

  ```java Java theme={null}
  SearchRequest searchRequest = SearchRequest.builder().q("science fiction").sort(new String[] {"price:asc"}).build();
  client.index("books").search(searchRequest);
  ```

  ```ruby Ruby theme={null}
  client.index('books').search('science fiction', { sort: ['price:asc'] })
  ```

  ```go Go theme={null}
  resp, err := client.Index("books").Search("science fiction", &meilisearch.SearchRequest{
    Sort: []string{
      "price:asc",
    },
  })
  ```

  ```csharp C# theme={null}
  var sq = new SearchQuery
  {
    Sort = new[] { "price:asc" },
  };
  await client.Index("books").SearchAsync<Book>("science fiction", sq);
  ```

  ```rust Rust theme={null}
  let results: SearchResults<Books> = client
    .index("books")
    .search()
    .with_query("science fiction")
    .with_sort(&["price:asc"])
    .execute()
    .await
    .unwrap();
  ```

  ```swift Swift theme={null}
  let searchParameters = SearchParameters(
    query: "science fiction",
    sort: ["price:asc"]
  )
  client.index("books").search(searchParameters) { (result: Result<Searchable<Book>, Swift.Error>) in
    switch result {
    case .success(let searchResult):
      print(searchResult)
    case .failure(let error):
      print(error)
    }
  }
  ```

  ```dart Dart theme={null}
  await client
      .index('books')
      .search('science fiction', SearchQuery(sort: ['price:asc']));
  ```
</CodeGroup>

With our example dataset, the results look like this:

```json theme={null}
[
  {
    "id": 1,
    "title": "Solaris",
    "author": "Stanislaw Lem",
    "genres": [
      "science fiction"
    ],
    "rating": {
      "critics": 95,
      "users": 87
    },
    "price": 5.00
  },
  {
    "id": 2,
    "title": "The Parable of the Sower",
    "author": "Octavia E. Butler",
    "genres": [
      "science fiction"
    ],
    "rating": {
      "critics": 90,
      "users": 92
    },
    "price": 10.00
  }
]
```

It is common to search books based on an author's name. `sort` can help grouping results from the same author. This query would only return books matching the query term `"butler"` and group results according to their authors:

<CodeGroup>
  ```bash cURL theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/books/search' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "q": "butler",
      "sort": ["author:desc"]
    }'
  ```

  ```javascript JS theme={null}
  client.index('books').search('butler', {
    sort: ['author:desc'],
  })
  ```

  ```python Python theme={null}
  client.index('books').search('butler', {
    'sort': ['author:desc']
  })
  ```

  ```php PHP theme={null}
  $client->index('books')->search('butler', ['sort' => ['author:desc']]);
  ```

  ```java Java theme={null}
  SearchRequest searchRequest = SearchRequest.builder().q("butler").sort(new String[] {"author:desc"}).build();
  client.index("books").search(searchRequest);
  ```

  ```ruby Ruby theme={null}
  client.index('books').search('butler', { sort: ['author:desc'] })
  ```

  ```go Go theme={null}
  resp, err := client.Index("books").Search("butler", &meilisearch.SearchRequest{
    Sort: []string{
      "author:desc",
    },
  })
  ```

  ```csharp C# theme={null}
  var sq = new SearchQuery
  {
    Sort = new[] { "author:desc" },
  };
  await client.Index("books").SearchAsync<Book>("butler", sq);
  ```

  ```rust Rust theme={null}
  let results: SearchResults<Books> = client
    .index("books")
    .search()
    .with_query("butler")
    .with_sort(&["author:desc"])
    .execute()
    .await
    .unwrap();
  ```

  ```swift Swift theme={null}
  let searchParameters = SearchParameters(
    query: "butler",
    sort: ["author:desc"]
  )
  client.index("books").search(searchParameters) { (result: Result<Searchable<Book>, Swift.Error>) in
    switch result {
    case .success(let searchResult):
      print(searchResult)
    case .failure(let error):
      print(error)
    }
  }
  ```

  ```dart Dart theme={null}
  await client
      .index('books')
      .search('butler', SearchQuery(sort: ['author:desc']));
  ```
</CodeGroup>

```json theme={null}
[
  {
    "id": 2,
    "title": "The Parable of the Sower",
    "author": "Octavia E. Butler",
    "genres": [
      "science fiction"
    ],
    "rating": {
      "critics": 90,
      "users": 92
    },
    "price": 10.00
  },
  {
    "id": 5,
    "title": "Wild Seed",
    "author": "Octavia E. Butler",
    "genres": [
      "fantasy"
    ],
    "rating": {
      "critics": 84,
      "users": 80
    },
    "price": 5.00
  },
  {
    "id": 4,
    "title": "Gender Trouble",
    "author": "Judith Butler",
    "genres": [
      "feminism",
      "philosophy"
    ],
    "rating": {
      "critics": 86,
      "users": 73
    },
    "price": 10.00
  }
]
```

### Sort by nested fields

Use dot notation to sort results based on a document's nested fields. The following query sorts returned documents by their user review scores:

<CodeGroup>
  ```bash cURL theme={null}
  curl \
    -X POST 'MEILISEARCH_URL/indexes/books/search' \
    -H 'Content-Type: application/json' \
    --data-binary '{
      "q": "science fiction",
      "sort": ["rating.users:asc"]
    }'
  ```

  ```javascript JS theme={null}
  client.index('books').search('science fiction', {
    'sort': ['rating.users:asc'],
  })
  ```

  ```python Python theme={null}
  client.index('books').search('science fiction', {
    'sort': ['rating.users:asc']
  })
  ```

  ```php PHP theme={null}
  $client->index('books')->search('science fiction', ['sort' => ['rating.users:asc']]);
  ```

  ```java Java theme={null}
  SearchRequest searchRequest = SearchRequest.builder().q("science fiction").sort(new String[] {"rating.users:asc"}).build();
  client.index("books").search(searchRequest);
  ```

  ```ruby Ruby theme={null}
  client.index('books').search('science fiction', { sort: ['rating.users:asc'] })
  ```

  ```go Go theme={null}
  resp, err := client.Index("books").Search("science fiction", &meilisearch.SearchRequest{
    Sort: []string{
      "rating.users:asc",
    },
  })
  ```

  ```csharp C# theme={null}
  SearchQuery sort = new SearchQuery() { Sort = new string[] { "rating.users:asc" }};
  await client.Index("books").SearchAsync<Book>("science fiction", sort);
  ```

  ```rust Rust theme={null}
  let results: SearchResults<Books> = client
    .index("books")
    .search()
    .with_query("science fiction")
    .with_sort(&["rating.users:asc"])
    .execute()
    .await
    .unwrap();
  ```

  ```swift Swift theme={null}
  let searchParameters = SearchParameters(
      query: "science fiction",
      sort: ["rating.users:asc"]
  )
  client.index("books").search(searchParameters) { (result: Result<Searchable<Meteorite>, Swift.Error>) in
      switch result {
      case .success(let searchResult):
          print(searchResult)
      case .failure(let error):
          print(error)
      }
  }
  ```

  ```dart Dart theme={null}
  await client
      .index('movie_ratings')
      .search('thriller', SearchQuery(sort: ['rating.users:asc']));
  ```
</CodeGroup>

## Sorting and custom ranking rules

There is a lot of overlap between sorting and configuring [custom ranking rules](/learn/relevancy/custom_ranking_rules), as both can greatly influence which results a user will see first.

Sorting is most useful when you want your users to be able to alter the order of returned results at query time. For example, webshop users might want to order results by price depending on what they are searching and to change whether they see the most expensive or the cheapest products first.

Custom ranking rules, instead, establish a default sorting rule that is enforced in every search. This approach can be useful when you want to promote certain results above all others, regardless of a user's preferences. For example, you might want a webshop to always feature discounted products first, no matter what a user is searching for.

## Example application

Take a look at our demos for examples of how to implement sorting:

* **Ecommerce demo**: [preview](https://ecommerce.meilisearch.com/) • [GitHub repository](https://github.com/meilisearch/ecommerce-demo/)
* **CRM SaaS demo**: [preview](https://saas.meilisearch.com/) • [GitHub repository](https://github.com/meilisearch/saas-demo/)