Sort results

Sorting allows your users to sort results in a way that’s most meaningful to them.

By default, full-text queries sort results by the relevance score. You can choose to sort the results by any field value in either ascending or descending order by setting the order parameter to asc or desc.

For example, to sort results by descending order of a line_id value, use the following query:

  1. GET shakespeare/_search
  2. {
  3. "query": {
  4. "term": {
  5. "play_name": {
  6. "value": "Henry IV"
  7. }
  8. }
  9. },
  10. "sort": [
  11. {
  12. "line_id": {
  13. "order": "desc"
  14. }
  15. }
  16. ]
  17. }

The results are sorted by line_id in descending order:

  1. {
  2. "took" : 24,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 3205,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [
  17. {
  18. "_index" : "shakespeare",
  19. "_id" : "3204",
  20. "_score" : null,
  21. "_source" : {
  22. "type" : "line",
  23. "line_id" : 3205,
  24. "play_name" : "Henry IV",
  25. "speech_number" : 8,
  26. "line_number" : "",
  27. "speaker" : "KING HENRY IV",
  28. "text_entry" : "Exeunt"
  29. },
  30. "sort" : [
  31. 3205
  32. ]
  33. },
  34. {
  35. "_index" : "shakespeare",
  36. "_id" : "3203",
  37. "_score" : null,
  38. "_source" : {
  39. "type" : "line",
  40. "line_id" : 3204,
  41. "play_name" : "Henry IV",
  42. "speech_number" : 8,
  43. "line_number" : "5.5.45",
  44. "speaker" : "KING HENRY IV",
  45. "text_entry" : "Let us not leave till all our own be won."
  46. },
  47. "sort" : [
  48. 3204
  49. ]
  50. },
  51. {
  52. "_index" : "shakespeare",
  53. "_id" : "3202",
  54. "_score" : null,
  55. "_source" : {
  56. "type" : "line",
  57. "line_id" : 3203,
  58. "play_name" : "Henry IV",
  59. "speech_number" : 8,
  60. "line_number" : "5.5.44",
  61. "speaker" : "KING HENRY IV",
  62. "text_entry" : "And since this business so fair is done,"
  63. },
  64. "sort" : [
  65. 3203
  66. ]
  67. },
  68. {
  69. "_index" : "shakespeare",
  70. "_id" : "3201",
  71. "_score" : null,
  72. "_source" : {
  73. "type" : "line",
  74. "line_id" : 3202,
  75. "play_name" : "Henry IV",
  76. "speech_number" : 8,
  77. "line_number" : "5.5.43",
  78. "speaker" : "KING HENRY IV",
  79. "text_entry" : "Meeting the cheque of such another day:"
  80. },
  81. "sort" : [
  82. 3202
  83. ]
  84. },
  85. {
  86. "_index" : "shakespeare",
  87. "_id" : "3200",
  88. "_score" : null,
  89. "_source" : {
  90. "type" : "line",
  91. "line_id" : 3201,
  92. "play_name" : "Henry IV",
  93. "speech_number" : 8,
  94. "line_number" : "5.5.42",
  95. "speaker" : "KING HENRY IV",
  96. "text_entry" : "Rebellion in this land shall lose his sway,"
  97. },
  98. "sort" : [
  99. 3201
  100. ]
  101. },
  102. {
  103. "_index" : "shakespeare",
  104. "_id" : "3199",
  105. "_score" : null,
  106. "_source" : {
  107. "type" : "line",
  108. "line_id" : 3200,
  109. "play_name" : "Henry IV",
  110. "speech_number" : 8,
  111. "line_number" : "5.5.41",
  112. "speaker" : "KING HENRY IV",
  113. "text_entry" : "To fight with Glendower and the Earl of March."
  114. },
  115. "sort" : [
  116. 3200
  117. ]
  118. },
  119. {
  120. "_index" : "shakespeare",
  121. "_id" : "3198",
  122. "_score" : null,
  123. "_source" : {
  124. "type" : "line",
  125. "line_id" : 3199,
  126. "play_name" : "Henry IV",
  127. "speech_number" : 8,
  128. "line_number" : "5.5.40",
  129. "speaker" : "KING HENRY IV",
  130. "text_entry" : "Myself and you, son Harry, will towards Wales,"
  131. },
  132. "sort" : [
  133. 3199
  134. ]
  135. },
  136. {
  137. "_index" : "shakespeare",
  138. "_id" : "3197",
  139. "_score" : null,
  140. "_source" : {
  141. "type" : "line",
  142. "line_id" : 3198,
  143. "play_name" : "Henry IV",
  144. "speech_number" : 8,
  145. "line_number" : "5.5.39",
  146. "speaker" : "KING HENRY IV",
  147. "text_entry" : "Who, as we hear, are busily in arms:"
  148. },
  149. "sort" : [
  150. 3198
  151. ]
  152. },
  153. {
  154. "_index" : "shakespeare",
  155. "_id" : "3196",
  156. "_score" : null,
  157. "_source" : {
  158. "type" : "line",
  159. "line_id" : 3197,
  160. "play_name" : "Henry IV",
  161. "speech_number" : 8,
  162. "line_number" : "5.5.38",
  163. "speaker" : "KING HENRY IV",
  164. "text_entry" : "To meet Northumberland and the prelate Scroop,"
  165. },
  166. "sort" : [
  167. 3197
  168. ]
  169. },
  170. {
  171. "_index" : "shakespeare",
  172. "_id" : "3195",
  173. "_score" : null,
  174. "_source" : {
  175. "type" : "line",
  176. "line_id" : 3196,
  177. "play_name" : "Henry IV",
  178. "speech_number" : 8,
  179. "line_number" : "5.5.37",
  180. "speaker" : "KING HENRY IV",
  181. "text_entry" : "Towards York shall bend you with your dearest speed,"
  182. },
  183. "sort" : [
  184. 3196
  185. ]
  186. }
  187. ]
  188. }
  189. }

The sort parameter is an array, so you can specify multiple field values in the order of their priority.

If you have two fields with the same value for line_id, OpenSearch uses speech_number, which is the second option for sorting:

  1. GET shakespeare/_search
  2. {
  3. "query": {
  4. "term": {
  5. "play_name": {
  6. "value": "Henry IV"
  7. }
  8. }
  9. },
  10. "sort": [
  11. {
  12. "line_id": {
  13. "order": "desc"
  14. }
  15. },
  16. {
  17. "speech_number": {
  18. "order": "desc"
  19. }
  20. }
  21. ]
  22. }

You can continue to sort by any number of field values to get the results in just the right order. It doesn’t have to be a numerical value—you can also sort by date or timestamp fields:

  1. "sort": [
  2. {
  3. "date": {
  4. "order": "desc"
  5. }
  6. }
  7. ]

A text field that is analyzed cannot be used to sort documents, because the inverted index only contains the individual tokenized terms and not the entire string. So you cannot sort by the play_name, for example.

To bypass this limitation, you can use a raw version of the text field mapped as a keyword type. In the following example, play_name.keyword is not analyzed and you have a copy of the full original version for sorting purposes:

  1. GET shakespeare/_search
  2. {
  3. "query": {
  4. "term": {
  5. "play_name": {
  6. "value": "Henry IV"
  7. }
  8. }
  9. },
  10. "sort": [
  11. {
  12. "play_name.keyword": {
  13. "order": "desc"
  14. }
  15. }
  16. ]
  17. }

The results are sorted by the play_name field in alphabetical order.

Use sort with the search_after parameter for more efficient scrolling. The results start with the document that comes after the sort values you specify in the search_after array.

Make sure you have the same number of values in the search_after array as in the sort array, also ordered in the same way. In this case, you are requesting results starting with the document that comes after line_id = 3202 and speech_number = 8:

  1. GET shakespeare/_search
  2. {
  3. "query": {
  4. "term": {
  5. "play_name": {
  6. "value": "Henry IV"
  7. }
  8. }
  9. },
  10. "sort": [
  11. {
  12. "line_id": {
  13. "order": "desc"
  14. }
  15. },
  16. {
  17. "speech_number": {
  18. "order": "desc"
  19. }
  20. }
  21. ],
  22. "search_after": [
  23. "3202",
  24. "8"
  25. ]
  26. }

Sort mode

The sort mode is applicable to sorting by array or multivalued fields. It specifies what array value should be chosen for sorting the document. For numeric fields that contain an array of numbers, you can sort by the avg, sum, or median modes. To sort by the minimum or maximum values, use the min or max modes that work for both numeric and string data types.

The default mode is min for ascending sort order and max for descending sort order.

The following example illustrates sorting by an array field using the sort mode.

Consider an index that holds student grades. Index two documents into the index:

  1. PUT students/_doc/1
  2. {
  3. "name": "John Doe",
  4. "grades": [70, 90]
  5. }
  6. PUT students/_doc/2
  7. {
  8. "name": "Mary Major",
  9. "grades": [80, 100]
  10. }

Sort all students by highest grade average using the avg mode:

  1. GET students/_search
  2. {
  3. "query" : {
  4. "match_all": {}
  5. },
  6. "sort" : [
  7. {"grades" : {"order" : "desc", "mode" : "avg"}}
  8. ]
  9. }

The response contains students sorted by grades in descending order:

  1. {
  2. "took" : 1,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 2,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [
  17. {
  18. "_index" : "students",
  19. "_id" : "2",
  20. "_score" : null,
  21. "_source" : {
  22. "name" : "Mary Major",
  23. "grades" : [
  24. 80,
  25. 100
  26. ]
  27. },
  28. "sort" : [
  29. 90
  30. ]
  31. },
  32. {
  33. "_index" : "students",
  34. "_id" : "1",
  35. "_score" : null,
  36. "_source" : {
  37. "name" : "John Doe",
  38. "grades" : [
  39. 70,
  40. 90
  41. ]
  42. },
  43. "sort" : [
  44. 80
  45. ]
  46. }
  47. ]
  48. }
  49. }

Sorting nested objects

When sorting nested objects, provide the path parameter specifying the path to the field on which to sort.

For example, in the index students, map the variable first_sem as nested:

  1. PUT students
  2. {
  3. "mappings" : {
  4. "properties": {
  5. "first_sem": {
  6. "type" : "nested"
  7. }
  8. }
  9. }
  10. }

Index two documents with nested fields:

  1. PUT students/_doc/1
  2. {
  3. "name": "John Doe",
  4. "first_sem" : {
  5. "grades": [70, 90]
  6. }
  7. }
  8. PUT students/_doc/2
  9. {
  10. "name": "Mary Major",
  11. "first_sem": {
  12. "grades": [80, 100]
  13. }
  14. }

When sorting by grade average, provide the path to the nested field:

  1. GET students/_search
  2. {
  3. "query" : {
  4. "match_all": {}
  5. },
  6. "sort" : [
  7. {"first_sem.grades": {
  8. "order" : "desc",
  9. "mode" : "avg",
  10. "nested": {
  11. "path": "first_sem"
  12. }
  13. }
  14. }
  15. ]
  16. }

Handling missing values

The missing parameter specifies the handling of missing values. The built-in valid values are _last (list the documents with the missing value last) and _first (list the documents with the missing value first). The default value is _last. You can also specify a custom value to be used for missing documents as the sort value.

For example, you can index a document with an average field and another document without an average field:

  1. PUT students/_doc/1
  2. {
  3. "name": "John Doe",
  4. "average": 80
  5. }
  6. PUT students/_doc/2
  7. {
  8. "name": "Mary Major"
  9. }

Sort the documents, ordering the document with a missing field first:

  1. GET students/_search
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "sort": [
  7. {
  8. "average": {
  9. "order": "desc",
  10. "missing": "_first"
  11. }
  12. }
  13. ]
  14. }

The response lists document 2 first:

  1. {
  2. "took" : 1,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 2,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [
  17. {
  18. "_index" : "students",
  19. "_id" : "2",
  20. "_score" : null,
  21. "_source" : {
  22. "name" : "Mary Major"
  23. },
  24. "sort" : [
  25. 9223372036854775807
  26. ]
  27. },
  28. {
  29. "_index" : "students",
  30. "_id" : "1",
  31. "_score" : null,
  32. "_source" : {
  33. "name" : "John Doe",
  34. "average" : 80
  35. },
  36. "sort" : [
  37. 80
  38. ]
  39. }
  40. ]
  41. }
  42. }

Ignoring unmapped fields

If a field is not mapped, a search request that sorts by this field fails by default. To avoid this, you can use the unmapped_type parameter, which signals to OpenSearch to ignore the field. For example, if you set unmapped_type to long, the field is treated as if it were mapped as type long. Additionally, all documents in the index that have an unmapped_type field are treated as if they had no value in this field, so they are not sorted by it.

For example, consider two indexes. Index a document that contains an average field in the first index:

  1. PUT students/_doc/1
  2. {
  3. "name": "John Doe",
  4. "average": 80
  5. }

Index a document that does not contain an average field in the second index:

  1. PUT students_no_map/_doc/2
  2. {
  3. "name": "Mary Major"
  4. }

Search for all documents in both indexes and sort them by the average field:

  1. GET students*/_search
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "sort": [
  7. {
  8. "average": {
  9. "order": "desc"
  10. }
  11. }
  12. ]
  13. }

By default, the second index produces an error because the average field is not mapped:

  1. {
  2. "took" : 3,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 2,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 1,
  9. "failures" : [
  10. {
  11. "shard" : 0,
  12. "index" : "students_no_map",
  13. "node" : "cam9NWqVSV-jUIkQ3tRubw",
  14. "reason" : {
  15. "type" : "query_shard_exception",
  16. "reason" : "No mapping found for [average] in order to sort on",
  17. "index" : "students_no_map",
  18. "index_uuid" : "JgfRkypKSUSpyU-ZXr9kKA"
  19. }
  20. }
  21. ]
  22. },
  23. "hits" : {
  24. "total" : {
  25. "value" : 1,
  26. "relation" : "eq"
  27. },
  28. "max_score" : null,
  29. "hits" : [
  30. {
  31. "_index" : "students",
  32. "_id" : "1",
  33. "_score" : null,
  34. "_source" : {
  35. "name" : "John Doe",
  36. "average" : 80
  37. },
  38. "sort" : [
  39. 80
  40. ]
  41. }
  42. ]
  43. }
  44. }

You can specify the unmapped_type parameter so that the unmapped field is ignored:

  1. GET students*/_search
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "sort": [
  7. {
  8. "average": {
  9. "order": "desc",
  10. "unmapped_type": "long"
  11. }
  12. }
  13. ]
  14. }

The response contains both documents:

  1. {
  2. "took" : 4,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 2,
  6. "successful" : 2,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 2,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [
  17. {
  18. "_index" : "students",
  19. "_id" : "1",
  20. "_score" : null,
  21. "_source" : {
  22. "name" : "John Doe",
  23. "average" : 80
  24. },
  25. "sort" : [
  26. 80
  27. ]
  28. },
  29. {
  30. "_index" : "students_no_map",
  31. "_id" : "2",
  32. "_score" : null,
  33. "_source" : {
  34. "name" : "Mary Major"
  35. },
  36. "sort" : [
  37. -9223372036854775808
  38. ]
  39. }
  40. ]
  41. }
  42. }

Tracking scores

By default, scores are not computed when sorting on a field. You can set track_scores to true to compute and track scores:

  1. GET students/_search
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "sort": [
  7. {
  8. "average": {
  9. "order": "desc"
  10. }
  11. }
  12. ],
  13. "track_scores": true
  14. }

Sorting by geo distance

You can sort documents by _geo_distance. The following parameters are supported.

ParameterDescription
distance_typeSpecifies the method of computing the distance. Valid values are arc and plane. The plane method is faster but less accurate for long distances or close to the poles. Default is arc.
modeSpecifies how to handle a field with several geopoints. By default, documents are sorted by the shortest distance when the sort order is ascending and by the longest distance when the sort order is descending. Valid values are min, max, median, and avg.
unitSpecifies the units used to compute sort values. Default is meters (m).
ignore_unmappedSpecifies how to treat an unmapped field. Set ignore_unmapped to true to ignore unmapped fields. Default is false (produce an error when encountering an unmapped field).

The _geo_distance parameter does not support missing_values. The distance is always considered to be infinity when a document does not contain the field used for computing distance.

For example, index two documents with geopoints:

  1. PUT testindex1/_doc/1
  2. {
  3. "point": [74.00, 40.71]
  4. }
  5. PUT testindex1/_doc/2
  6. {
  7. "point": [73.77, -69.63]
  8. }

Search for all documents and sort them by the distance from the provided point:

  1. GET testindex1/_search
  2. {
  3. "sort": [
  4. {
  5. "_geo_distance": {
  6. "point": [59, -54],
  7. "order": "asc",
  8. "unit": "km",
  9. "distance_type": "arc",
  10. "mode": "min",
  11. "ignore_unmapped": true
  12. }
  13. }
  14. ],
  15. "query": {
  16. "match_all": {}
  17. }
  18. }

The response contains the sorted documents:

  1. {
  2. "took" : 864,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 2,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [
  17. {
  18. "_index" : "testindex1",
  19. "_id" : "2",
  20. "_score" : null,
  21. "_source" : {
  22. "point" : [
  23. 73.77,
  24. -69.63
  25. ]
  26. },
  27. "sort" : [
  28. 1891.2667493895767
  29. ]
  30. },
  31. {
  32. "_index" : "testindex1",
  33. "_id" : "1",
  34. "_score" : null,
  35. "_source" : {
  36. "point" : [
  37. 74.0,
  38. 40.71
  39. ]
  40. },
  41. "sort" : [
  42. 10628.402240213345
  43. ]
  44. }
  45. ]
  46. }
  47. }

You can provide coordinates in any format supported by the geopoint field type. For a description of all formats, see the geopoint field type documentation.

To pass multiple geopoints to _geo_distance, use an array:

  1. GET testindex1/_search
  2. {
  3. "sort": [
  4. {
  5. "_geo_distance": {
  6. "point": [[59, -54], [60, -53]],
  7. "order": "asc",
  8. "unit": "km",
  9. "distance_type": "arc",
  10. "mode": "min",
  11. "ignore_unmapped": true
  12. }
  13. }
  14. ],
  15. "query": {
  16. "match_all": {}
  17. }
  18. }

For each document, the sorting distance is calculated as the minimum, maximum, or average (as specified by the mode) of the distances from all points provided in the search to all points in the document.

Performance considerations

Sorted field values are loaded into memory for sorting. Therefore, for minimum overhead we recommend mapping numeric types to the smallest acceptable types, like short, integer, and float. String types should not have the sorted field analyzed or tokenized.