search_source.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. // Copyright 2012-2015 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 (
  6. "fmt"
  7. )
  8. // SearchSource enables users to build the search source.
  9. // It resembles the SearchSourceBuilder in Elasticsearch.
  10. type SearchSource struct {
  11. query Query
  12. postFilter Filter
  13. from int
  14. size int
  15. explain *bool
  16. version *bool
  17. sorts []SortInfo
  18. sorters []Sorter
  19. trackScores bool
  20. minScore *float64
  21. timeout string
  22. fieldNames []string
  23. fieldDataFields []string
  24. scriptFields []*ScriptField
  25. partialFields []*PartialField
  26. fetchSourceContext *FetchSourceContext
  27. facets map[string]Facet
  28. aggregations map[string]Aggregation
  29. highlight *Highlight
  30. globalSuggestText string
  31. suggesters []Suggester
  32. rescores []*Rescore
  33. defaultRescoreWindowSize *int
  34. indexBoosts map[string]float64
  35. stats []string
  36. }
  37. func NewSearchSource() *SearchSource {
  38. return &SearchSource{
  39. from: -1,
  40. size: -1,
  41. trackScores: false,
  42. sorts: make([]SortInfo, 0),
  43. sorters: make([]Sorter, 0),
  44. fieldDataFields: make([]string, 0),
  45. scriptFields: make([]*ScriptField, 0),
  46. partialFields: make([]*PartialField, 0),
  47. facets: make(map[string]Facet),
  48. aggregations: make(map[string]Aggregation),
  49. rescores: make([]*Rescore, 0),
  50. indexBoosts: make(map[string]float64),
  51. stats: make([]string, 0),
  52. }
  53. }
  54. // Query sets the query to use with this search source.
  55. func (s *SearchSource) Query(query Query) *SearchSource {
  56. s.query = query
  57. return s
  58. }
  59. // PostFilter is executed as the last filter. It only affects the
  60. // search hits but not facets.
  61. func (s *SearchSource) PostFilter(postFilter Filter) *SearchSource {
  62. s.postFilter = postFilter
  63. return s
  64. }
  65. func (s *SearchSource) From(from int) *SearchSource {
  66. s.from = from
  67. return s
  68. }
  69. func (s *SearchSource) Size(size int) *SearchSource {
  70. s.size = size
  71. return s
  72. }
  73. func (s *SearchSource) MinScore(minScore float64) *SearchSource {
  74. s.minScore = &minScore
  75. return s
  76. }
  77. func (s *SearchSource) Explain(explain bool) *SearchSource {
  78. s.explain = &explain
  79. return s
  80. }
  81. func (s *SearchSource) Version(version bool) *SearchSource {
  82. s.version = &version
  83. return s
  84. }
  85. func (s *SearchSource) Timeout(timeout string) *SearchSource {
  86. s.timeout = timeout
  87. return s
  88. }
  89. func (s *SearchSource) TimeoutInMillis(timeoutInMillis int) *SearchSource {
  90. s.timeout = fmt.Sprintf("%dms", timeoutInMillis)
  91. return s
  92. }
  93. func (s *SearchSource) Sort(field string, ascending bool) *SearchSource {
  94. s.sorts = append(s.sorts, SortInfo{Field: field, Ascending: ascending})
  95. return s
  96. }
  97. func (s *SearchSource) SortWithInfo(info SortInfo) *SearchSource {
  98. s.sorts = append(s.sorts, info)
  99. return s
  100. }
  101. func (s *SearchSource) SortBy(sorter ...Sorter) *SearchSource {
  102. s.sorters = append(s.sorters, sorter...)
  103. return s
  104. }
  105. func (s *SearchSource) TrackScores(trackScores bool) *SearchSource {
  106. s.trackScores = trackScores
  107. return s
  108. }
  109. func (s *SearchSource) Facet(name string, facet Facet) *SearchSource {
  110. s.facets[name] = facet
  111. return s
  112. }
  113. func (s *SearchSource) Aggregation(name string, aggregation Aggregation) *SearchSource {
  114. s.aggregations[name] = aggregation
  115. return s
  116. }
  117. func (s *SearchSource) DefaultRescoreWindowSize(defaultRescoreWindowSize int) *SearchSource {
  118. s.defaultRescoreWindowSize = &defaultRescoreWindowSize
  119. return s
  120. }
  121. func (s *SearchSource) Highlight(highlight *Highlight) *SearchSource {
  122. s.highlight = highlight
  123. return s
  124. }
  125. func (s *SearchSource) Highlighter() *Highlight {
  126. if s.highlight == nil {
  127. s.highlight = NewHighlight()
  128. }
  129. return s.highlight
  130. }
  131. func (s *SearchSource) GlobalSuggestText(text string) *SearchSource {
  132. s.globalSuggestText = text
  133. return s
  134. }
  135. func (s *SearchSource) Suggester(suggester Suggester) *SearchSource {
  136. s.suggesters = append(s.suggesters, suggester)
  137. return s
  138. }
  139. func (s *SearchSource) AddRescore(rescore *Rescore) *SearchSource {
  140. s.rescores = append(s.rescores, rescore)
  141. return s
  142. }
  143. func (s *SearchSource) ClearRescores() *SearchSource {
  144. s.rescores = make([]*Rescore, 0)
  145. return s
  146. }
  147. func (s *SearchSource) FetchSource(fetchSource bool) *SearchSource {
  148. if s.fetchSourceContext == nil {
  149. s.fetchSourceContext = NewFetchSourceContext(fetchSource)
  150. } else {
  151. s.fetchSourceContext.SetFetchSource(fetchSource)
  152. }
  153. return s
  154. }
  155. func (s *SearchSource) FetchSourceContext(fetchSourceContext *FetchSourceContext) *SearchSource {
  156. s.fetchSourceContext = fetchSourceContext
  157. return s
  158. }
  159. func (s *SearchSource) Fields(fieldNames ...string) *SearchSource {
  160. if s.fieldNames == nil {
  161. s.fieldNames = make([]string, 0)
  162. }
  163. s.fieldNames = append(s.fieldNames, fieldNames...)
  164. return s
  165. }
  166. func (s *SearchSource) Field(fieldName string) *SearchSource {
  167. if s.fieldNames == nil {
  168. s.fieldNames = make([]string, 0)
  169. }
  170. s.fieldNames = append(s.fieldNames, fieldName)
  171. return s
  172. }
  173. func (s *SearchSource) NoFields() *SearchSource {
  174. s.fieldNames = make([]string, 0)
  175. return s
  176. }
  177. func (s *SearchSource) FieldDataFields(fieldDataFields ...string) *SearchSource {
  178. s.fieldDataFields = append(s.fieldDataFields, fieldDataFields...)
  179. return s
  180. }
  181. func (s *SearchSource) FieldDataField(fieldDataField string) *SearchSource {
  182. s.fieldDataFields = append(s.fieldDataFields, fieldDataField)
  183. return s
  184. }
  185. func (s *SearchSource) ScriptFields(scriptFields ...*ScriptField) *SearchSource {
  186. s.scriptFields = append(s.scriptFields, scriptFields...)
  187. return s
  188. }
  189. func (s *SearchSource) ScriptField(scriptField *ScriptField) *SearchSource {
  190. s.scriptFields = append(s.scriptFields, scriptField)
  191. return s
  192. }
  193. func (s *SearchSource) PartialFields(partialFields ...*PartialField) *SearchSource {
  194. s.partialFields = append(s.partialFields, partialFields...)
  195. return s
  196. }
  197. func (s *SearchSource) PartialField(partialField *PartialField) *SearchSource {
  198. s.partialFields = append(s.partialFields, partialField)
  199. return s
  200. }
  201. func (s *SearchSource) IndexBoost(index string, boost float64) *SearchSource {
  202. s.indexBoosts[index] = boost
  203. return s
  204. }
  205. func (s *SearchSource) Stats(statsGroup ...string) *SearchSource {
  206. s.stats = append(s.stats, statsGroup...)
  207. return s
  208. }
  209. func (s *SearchSource) Source() interface{} {
  210. source := make(map[string]interface{})
  211. if s.from != -1 {
  212. source["from"] = s.from
  213. }
  214. if s.size != -1 {
  215. source["size"] = s.size
  216. }
  217. if s.timeout != "" {
  218. source["timeout"] = s.timeout
  219. }
  220. if s.query != nil {
  221. source["query"] = s.query.Source()
  222. }
  223. if s.postFilter != nil {
  224. source["post_filter"] = s.postFilter.Source()
  225. }
  226. if s.minScore != nil {
  227. source["min_score"] = *s.minScore
  228. }
  229. if s.version != nil {
  230. source["version"] = *s.version
  231. }
  232. if s.explain != nil {
  233. source["explain"] = *s.explain
  234. }
  235. if s.fetchSourceContext != nil {
  236. source["_source"] = s.fetchSourceContext.Source()
  237. }
  238. if s.fieldNames != nil {
  239. switch len(s.fieldNames) {
  240. case 1:
  241. source["fields"] = s.fieldNames[0]
  242. default:
  243. source["fields"] = s.fieldNames
  244. }
  245. }
  246. if len(s.fieldDataFields) > 0 {
  247. source["fielddata_fields"] = s.fieldDataFields
  248. }
  249. if len(s.partialFields) > 0 {
  250. pfmap := make(map[string]interface{})
  251. for _, partialField := range s.partialFields {
  252. pfmap[partialField.Name] = partialField.Source()
  253. }
  254. source["partial_fields"] = pfmap
  255. }
  256. if len(s.scriptFields) > 0 {
  257. sfmap := make(map[string]interface{})
  258. for _, scriptField := range s.scriptFields {
  259. sfmap[scriptField.FieldName] = scriptField.Source()
  260. }
  261. source["script_fields"] = sfmap
  262. }
  263. if len(s.sorters) > 0 {
  264. sortarr := make([]interface{}, 0)
  265. for _, sorter := range s.sorters {
  266. sortarr = append(sortarr, sorter.Source())
  267. }
  268. source["sort"] = sortarr
  269. } else if len(s.sorts) > 0 {
  270. sortarr := make([]interface{}, 0)
  271. for _, sort := range s.sorts {
  272. sortarr = append(sortarr, sort.Source())
  273. }
  274. source["sort"] = sortarr
  275. }
  276. if s.trackScores {
  277. source["track_scores"] = s.trackScores
  278. }
  279. if len(s.indexBoosts) > 0 {
  280. source["indices_boost"] = s.indexBoosts
  281. }
  282. if len(s.facets) > 0 {
  283. facetsMap := make(map[string]interface{})
  284. for field, facet := range s.facets {
  285. facetsMap[field] = facet.Source()
  286. }
  287. source["facets"] = facetsMap
  288. }
  289. if len(s.aggregations) > 0 {
  290. aggsMap := make(map[string]interface{})
  291. for name, aggregate := range s.aggregations {
  292. aggsMap[name] = aggregate.Source()
  293. }
  294. source["aggregations"] = aggsMap
  295. }
  296. if s.highlight != nil {
  297. source["highlight"] = s.highlight.Source()
  298. }
  299. if len(s.suggesters) > 0 {
  300. suggesters := make(map[string]interface{})
  301. for _, s := range s.suggesters {
  302. suggesters[s.Name()] = s.Source(false)
  303. }
  304. if s.globalSuggestText != "" {
  305. suggesters["text"] = s.globalSuggestText
  306. }
  307. source["suggest"] = suggesters
  308. }
  309. if len(s.rescores) > 0 {
  310. // Strip empty rescores from request
  311. rescores := make([]*Rescore, 0)
  312. for _, r := range s.rescores {
  313. if !r.IsEmpty() {
  314. rescores = append(rescores, r)
  315. }
  316. }
  317. if len(rescores) == 1 {
  318. rescores[0].defaultRescoreWindowSize = s.defaultRescoreWindowSize
  319. source["rescore"] = rescores[0].Source()
  320. } else {
  321. slice := make([]interface{}, 0)
  322. for _, r := range rescores {
  323. r.defaultRescoreWindowSize = s.defaultRescoreWindowSize
  324. slice = append(slice, r.Source())
  325. }
  326. source["rescore"] = slice
  327. }
  328. }
  329. if len(s.stats) > 0 {
  330. source["stats"] = s.stats
  331. }
  332. return source
  333. }
  334. // -- Script Field --
  335. type ScriptField struct {
  336. FieldName string
  337. script string
  338. lang string
  339. params map[string]interface{}
  340. }
  341. func NewScriptField(fieldName, script, lang string, params map[string]interface{}) *ScriptField {
  342. return &ScriptField{fieldName, script, lang, params}
  343. }
  344. func (f *ScriptField) Source() interface{} {
  345. source := make(map[string]interface{})
  346. source["script"] = f.script
  347. if f.lang != "" {
  348. source["lang"] = f.lang
  349. }
  350. if f.params != nil && len(f.params) > 0 {
  351. source["params"] = f.params
  352. }
  353. return source
  354. }
  355. // -- Partial Field --
  356. type PartialField struct {
  357. Name string
  358. includes []string
  359. excludes []string
  360. }
  361. func NewPartialField(name string, includes, excludes []string) *PartialField {
  362. return &PartialField{name, includes, excludes}
  363. }
  364. func (f *PartialField) Source() interface{} {
  365. source := make(map[string]interface{})
  366. if f.includes != nil {
  367. switch len(f.includes) {
  368. case 0:
  369. case 1:
  370. source["include"] = f.includes[0]
  371. default:
  372. source["include"] = f.includes
  373. }
  374. }
  375. if f.excludes != nil {
  376. switch len(f.excludes) {
  377. case 0:
  378. case 1:
  379. source["exclude"] = f.excludes[0]
  380. default:
  381. source["exclude"] = f.excludes
  382. }
  383. }
  384. return source
  385. }