sort.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. // Copyright 2012-present Oliver Eilhard. All rights reserved.
  2. // Use of this source code is governed by a MIT-license.
  3. // See http://olivere.mit-license.org/license.txt for details.
  4. package elastic
  5. import "errors"
  6. // -- Sorter --
  7. // Sorter is an interface for sorting strategies, e.g. ScoreSort or FieldSort.
  8. // See https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-sort.html.
  9. type Sorter interface {
  10. Source() (interface{}, error)
  11. }
  12. // -- SortInfo --
  13. // SortInfo contains information about sorting a field.
  14. type SortInfo struct {
  15. Sorter
  16. Field string
  17. Ascending bool
  18. Missing interface{}
  19. IgnoreUnmapped *bool
  20. UnmappedType string
  21. SortMode string
  22. NestedFilter Query
  23. NestedPath string
  24. }
  25. func (info SortInfo) Source() (interface{}, error) {
  26. prop := make(map[string]interface{})
  27. if info.Ascending {
  28. prop["order"] = "asc"
  29. } else {
  30. prop["order"] = "desc"
  31. }
  32. if info.Missing != nil {
  33. prop["missing"] = info.Missing
  34. }
  35. if info.IgnoreUnmapped != nil {
  36. prop["ignore_unmapped"] = *info.IgnoreUnmapped
  37. }
  38. if info.UnmappedType != "" {
  39. prop["unmapped_type"] = info.UnmappedType
  40. }
  41. if info.SortMode != "" {
  42. prop["mode"] = info.SortMode
  43. }
  44. if info.NestedFilter != nil {
  45. src, err := info.NestedFilter.Source()
  46. if err != nil {
  47. return nil, err
  48. }
  49. prop["nested_filter"] = src
  50. }
  51. if info.NestedPath != "" {
  52. prop["nested_path"] = info.NestedPath
  53. }
  54. source := make(map[string]interface{})
  55. source[info.Field] = prop
  56. return source, nil
  57. }
  58. // -- SortByDoc --
  59. // SortByDoc sorts by the "_doc" field, as described in
  60. // https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-scroll.html.
  61. //
  62. // Example:
  63. // ss := elastic.NewSearchSource()
  64. // ss = ss.SortBy(elastic.SortByDoc{})
  65. type SortByDoc struct {
  66. Sorter
  67. }
  68. // Source returns the JSON-serializable data.
  69. func (s SortByDoc) Source() (interface{}, error) {
  70. return "_doc", nil
  71. }
  72. // -- ScoreSort --
  73. // ScoreSort sorts by relevancy score.
  74. type ScoreSort struct {
  75. Sorter
  76. ascending bool
  77. }
  78. // NewScoreSort creates a new ScoreSort.
  79. func NewScoreSort() *ScoreSort {
  80. return &ScoreSort{ascending: false} // Descending by default!
  81. }
  82. // Order defines whether sorting ascending (default) or descending.
  83. func (s *ScoreSort) Order(ascending bool) *ScoreSort {
  84. s.ascending = ascending
  85. return s
  86. }
  87. // Asc sets ascending sort order.
  88. func (s *ScoreSort) Asc() *ScoreSort {
  89. s.ascending = true
  90. return s
  91. }
  92. // Desc sets descending sort order.
  93. func (s *ScoreSort) Desc() *ScoreSort {
  94. s.ascending = false
  95. return s
  96. }
  97. // Source returns the JSON-serializable data.
  98. func (s *ScoreSort) Source() (interface{}, error) {
  99. source := make(map[string]interface{})
  100. x := make(map[string]interface{})
  101. source["_score"] = x
  102. if s.ascending {
  103. x["order"] = "asc"
  104. } else {
  105. x["order"] = "desc"
  106. }
  107. return source, nil
  108. }
  109. // -- FieldSort --
  110. // FieldSort sorts by a given field.
  111. type FieldSort struct {
  112. Sorter
  113. fieldName string
  114. ascending bool
  115. missing interface{}
  116. unmappedType *string
  117. sortMode *string
  118. nestedFilter Query
  119. nestedPath *string
  120. }
  121. // NewFieldSort creates a new FieldSort.
  122. func NewFieldSort(fieldName string) *FieldSort {
  123. return &FieldSort{
  124. fieldName: fieldName,
  125. ascending: true,
  126. }
  127. }
  128. // FieldName specifies the name of the field to be used for sorting.
  129. func (s *FieldSort) FieldName(fieldName string) *FieldSort {
  130. s.fieldName = fieldName
  131. return s
  132. }
  133. // Order defines whether sorting ascending (default) or descending.
  134. func (s *FieldSort) Order(ascending bool) *FieldSort {
  135. s.ascending = ascending
  136. return s
  137. }
  138. // Asc sets ascending sort order.
  139. func (s *FieldSort) Asc() *FieldSort {
  140. s.ascending = true
  141. return s
  142. }
  143. // Desc sets descending sort order.
  144. func (s *FieldSort) Desc() *FieldSort {
  145. s.ascending = false
  146. return s
  147. }
  148. // Missing sets the value to be used when a field is missing in a document.
  149. // You can also use "_last" or "_first" to sort missing last or first
  150. // respectively.
  151. func (s *FieldSort) Missing(missing interface{}) *FieldSort {
  152. s.missing = missing
  153. return s
  154. }
  155. // UnmappedType sets the type to use when the current field is not mapped
  156. // in an index.
  157. func (s *FieldSort) UnmappedType(typ string) *FieldSort {
  158. s.unmappedType = &typ
  159. return s
  160. }
  161. // SortMode specifies what values to pick in case a document contains
  162. // multiple values for the targeted sort field. Possible values are:
  163. // min, max, sum, and avg.
  164. func (s *FieldSort) SortMode(sortMode string) *FieldSort {
  165. s.sortMode = &sortMode
  166. return s
  167. }
  168. // NestedFilter sets a filter that nested objects should match with
  169. // in order to be taken into account for sorting.
  170. func (s *FieldSort) NestedFilter(nestedFilter Query) *FieldSort {
  171. s.nestedFilter = nestedFilter
  172. return s
  173. }
  174. // NestedPath is used if sorting occurs on a field that is inside a
  175. // nested object.
  176. func (s *FieldSort) NestedPath(nestedPath string) *FieldSort {
  177. s.nestedPath = &nestedPath
  178. return s
  179. }
  180. // Source returns the JSON-serializable data.
  181. func (s *FieldSort) Source() (interface{}, error) {
  182. source := make(map[string]interface{})
  183. x := make(map[string]interface{})
  184. source[s.fieldName] = x
  185. if s.ascending {
  186. x["order"] = "asc"
  187. } else {
  188. x["order"] = "desc"
  189. }
  190. if s.missing != nil {
  191. x["missing"] = s.missing
  192. }
  193. if s.unmappedType != nil {
  194. x["unmapped_type"] = *s.unmappedType
  195. }
  196. if s.sortMode != nil {
  197. x["mode"] = *s.sortMode
  198. }
  199. if s.nestedFilter != nil {
  200. src, err := s.nestedFilter.Source()
  201. if err != nil {
  202. return nil, err
  203. }
  204. x["nested_filter"] = src
  205. }
  206. if s.nestedPath != nil {
  207. x["nested_path"] = *s.nestedPath
  208. }
  209. return source, nil
  210. }
  211. // -- GeoDistanceSort --
  212. // GeoDistanceSort allows for sorting by geographic distance.
  213. // See https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-sort.html#_geo_distance_sorting.
  214. type GeoDistanceSort struct {
  215. Sorter
  216. fieldName string
  217. points []*GeoPoint
  218. geohashes []string
  219. distanceType *string
  220. unit string
  221. ascending bool
  222. sortMode *string
  223. nestedFilter Query
  224. nestedPath *string
  225. }
  226. // NewGeoDistanceSort creates a new sorter for geo distances.
  227. func NewGeoDistanceSort(fieldName string) *GeoDistanceSort {
  228. return &GeoDistanceSort{
  229. fieldName: fieldName,
  230. ascending: true,
  231. }
  232. }
  233. // FieldName specifies the name of the (geo) field to use for sorting.
  234. func (s *GeoDistanceSort) FieldName(fieldName string) *GeoDistanceSort {
  235. s.fieldName = fieldName
  236. return s
  237. }
  238. // Order defines whether sorting ascending (default) or descending.
  239. func (s *GeoDistanceSort) Order(ascending bool) *GeoDistanceSort {
  240. s.ascending = ascending
  241. return s
  242. }
  243. // Asc sets ascending sort order.
  244. func (s *GeoDistanceSort) Asc() *GeoDistanceSort {
  245. s.ascending = true
  246. return s
  247. }
  248. // Desc sets descending sort order.
  249. func (s *GeoDistanceSort) Desc() *GeoDistanceSort {
  250. s.ascending = false
  251. return s
  252. }
  253. // Point specifies a point to create the range distance aggregations from.
  254. func (s *GeoDistanceSort) Point(lat, lon float64) *GeoDistanceSort {
  255. s.points = append(s.points, GeoPointFromLatLon(lat, lon))
  256. return s
  257. }
  258. // Points specifies the geo point(s) to create the range distance aggregations from.
  259. func (s *GeoDistanceSort) Points(points ...*GeoPoint) *GeoDistanceSort {
  260. s.points = append(s.points, points...)
  261. return s
  262. }
  263. // GeoHashes specifies the geo point to create the range distance aggregations from.
  264. func (s *GeoDistanceSort) GeoHashes(geohashes ...string) *GeoDistanceSort {
  265. s.geohashes = append(s.geohashes, geohashes...)
  266. return s
  267. }
  268. // Unit specifies the distance unit to use. It defaults to km.
  269. // See https://www.elastic.co/guide/en/elasticsearch/reference/5.6/common-options.html#distance-units
  270. // for details.
  271. func (s *GeoDistanceSort) Unit(unit string) *GeoDistanceSort {
  272. s.unit = unit
  273. return s
  274. }
  275. // GeoDistance is an alias for DistanceType.
  276. func (s *GeoDistanceSort) GeoDistance(geoDistance string) *GeoDistanceSort {
  277. return s.DistanceType(geoDistance)
  278. }
  279. // DistanceType describes how to compute the distance, e.g. "arc" or "plane".
  280. // See https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-sort.html#geo-sorting
  281. // for details.
  282. func (s *GeoDistanceSort) DistanceType(distanceType string) *GeoDistanceSort {
  283. s.distanceType = &distanceType
  284. return s
  285. }
  286. // SortMode specifies what values to pick in case a document contains
  287. // multiple values for the targeted sort field. Possible values are:
  288. // min, max, sum, and avg.
  289. func (s *GeoDistanceSort) SortMode(sortMode string) *GeoDistanceSort {
  290. s.sortMode = &sortMode
  291. return s
  292. }
  293. // NestedFilter sets a filter that nested objects should match with
  294. // in order to be taken into account for sorting.
  295. func (s *GeoDistanceSort) NestedFilter(nestedFilter Query) *GeoDistanceSort {
  296. s.nestedFilter = nestedFilter
  297. return s
  298. }
  299. // NestedPath is used if sorting occurs on a field that is inside a
  300. // nested object.
  301. func (s *GeoDistanceSort) NestedPath(nestedPath string) *GeoDistanceSort {
  302. s.nestedPath = &nestedPath
  303. return s
  304. }
  305. // Source returns the JSON-serializable data.
  306. func (s *GeoDistanceSort) Source() (interface{}, error) {
  307. source := make(map[string]interface{})
  308. x := make(map[string]interface{})
  309. source["_geo_distance"] = x
  310. // Points
  311. var ptarr []interface{}
  312. for _, pt := range s.points {
  313. ptarr = append(ptarr, pt.Source())
  314. }
  315. for _, geohash := range s.geohashes {
  316. ptarr = append(ptarr, geohash)
  317. }
  318. x[s.fieldName] = ptarr
  319. if s.unit != "" {
  320. x["unit"] = s.unit
  321. }
  322. if s.distanceType != nil {
  323. x["distance_type"] = *s.distanceType
  324. }
  325. if s.ascending {
  326. x["order"] = "asc"
  327. } else {
  328. x["order"] = "desc"
  329. }
  330. if s.sortMode != nil {
  331. x["mode"] = *s.sortMode
  332. }
  333. if s.nestedFilter != nil {
  334. src, err := s.nestedFilter.Source()
  335. if err != nil {
  336. return nil, err
  337. }
  338. x["nested_filter"] = src
  339. }
  340. if s.nestedPath != nil {
  341. x["nested_path"] = *s.nestedPath
  342. }
  343. return source, nil
  344. }
  345. // -- ScriptSort --
  346. // ScriptSort sorts by a custom script. See
  347. // https://www.elastic.co/guide/en/elasticsearch/reference/5.6/modules-scripting.html#modules-scripting
  348. // for details about scripting.
  349. type ScriptSort struct {
  350. Sorter
  351. script *Script
  352. typ string
  353. ascending bool
  354. sortMode *string
  355. nestedFilter Query
  356. nestedPath *string
  357. }
  358. // NewScriptSort creates and initializes a new ScriptSort.
  359. // You must provide a script and a type, e.g. "string" or "number".
  360. func NewScriptSort(script *Script, typ string) *ScriptSort {
  361. return &ScriptSort{
  362. script: script,
  363. typ: typ,
  364. ascending: true,
  365. }
  366. }
  367. // Type sets the script type, which can be either "string" or "number".
  368. func (s *ScriptSort) Type(typ string) *ScriptSort {
  369. s.typ = typ
  370. return s
  371. }
  372. // Order defines whether sorting ascending (default) or descending.
  373. func (s *ScriptSort) Order(ascending bool) *ScriptSort {
  374. s.ascending = ascending
  375. return s
  376. }
  377. // Asc sets ascending sort order.
  378. func (s *ScriptSort) Asc() *ScriptSort {
  379. s.ascending = true
  380. return s
  381. }
  382. // Desc sets descending sort order.
  383. func (s *ScriptSort) Desc() *ScriptSort {
  384. s.ascending = false
  385. return s
  386. }
  387. // SortMode specifies what values to pick in case a document contains
  388. // multiple values for the targeted sort field. Possible values are:
  389. // min or max.
  390. func (s *ScriptSort) SortMode(sortMode string) *ScriptSort {
  391. s.sortMode = &sortMode
  392. return s
  393. }
  394. // NestedFilter sets a filter that nested objects should match with
  395. // in order to be taken into account for sorting.
  396. func (s *ScriptSort) NestedFilter(nestedFilter Query) *ScriptSort {
  397. s.nestedFilter = nestedFilter
  398. return s
  399. }
  400. // NestedPath is used if sorting occurs on a field that is inside a
  401. // nested object.
  402. func (s *ScriptSort) NestedPath(nestedPath string) *ScriptSort {
  403. s.nestedPath = &nestedPath
  404. return s
  405. }
  406. // Source returns the JSON-serializable data.
  407. func (s *ScriptSort) Source() (interface{}, error) {
  408. if s.script == nil {
  409. return nil, errors.New("ScriptSort expected a script")
  410. }
  411. source := make(map[string]interface{})
  412. x := make(map[string]interface{})
  413. source["_script"] = x
  414. src, err := s.script.Source()
  415. if err != nil {
  416. return nil, err
  417. }
  418. x["script"] = src
  419. x["type"] = s.typ
  420. if s.ascending {
  421. x["order"] = "asc"
  422. } else {
  423. x["order"] = "desc"
  424. }
  425. if s.sortMode != nil {
  426. x["mode"] = *s.sortMode
  427. }
  428. if s.nestedFilter != nil {
  429. src, err := s.nestedFilter.Source()
  430. if err != nil {
  431. return nil, err
  432. }
  433. x["nested_filter"] = src
  434. }
  435. if s.nestedPath != nil {
  436. x["nested_path"] = *s.nestedPath
  437. }
  438. return source, nil
  439. }