search_queries_multi_match.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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 (
  6. "fmt"
  7. "strings"
  8. )
  9. // MultiMatchQuery builds on the MatchQuery to allow multi-field queries.
  10. //
  11. // For more details, see
  12. // https://www.elastic.co/guide/en/elasticsearch/reference/5.2/query-dsl-multi-match-query.html
  13. type MultiMatchQuery struct {
  14. text interface{}
  15. fields []string
  16. fieldBoosts map[string]*float64
  17. typ string // best_fields, boolean, most_fields, cross_fields, phrase, phrase_prefix
  18. operator string // AND or OR
  19. analyzer string
  20. boost *float64
  21. slop *int
  22. fuzziness string
  23. prefixLength *int
  24. maxExpansions *int
  25. minimumShouldMatch string
  26. rewrite string
  27. fuzzyRewrite string
  28. tieBreaker *float64
  29. lenient *bool
  30. cutoffFrequency *float64
  31. zeroTermsQuery string
  32. queryName string
  33. }
  34. // MultiMatchQuery creates and initializes a new MultiMatchQuery.
  35. func NewMultiMatchQuery(text interface{}, fields ...string) *MultiMatchQuery {
  36. q := &MultiMatchQuery{
  37. text: text,
  38. fields: make([]string, 0),
  39. fieldBoosts: make(map[string]*float64),
  40. }
  41. q.fields = append(q.fields, fields...)
  42. return q
  43. }
  44. // Field adds a field to run the multi match against.
  45. func (q *MultiMatchQuery) Field(field string) *MultiMatchQuery {
  46. q.fields = append(q.fields, field)
  47. return q
  48. }
  49. // FieldWithBoost adds a field to run the multi match against with a specific boost.
  50. func (q *MultiMatchQuery) FieldWithBoost(field string, boost float64) *MultiMatchQuery {
  51. q.fields = append(q.fields, field)
  52. q.fieldBoosts[field] = &boost
  53. return q
  54. }
  55. // Type can be "best_fields", "boolean", "most_fields", "cross_fields",
  56. // "phrase", or "phrase_prefix".
  57. func (q *MultiMatchQuery) Type(typ string) *MultiMatchQuery {
  58. var zero = float64(0.0)
  59. var one = float64(1.0)
  60. switch strings.ToLower(typ) {
  61. default: // best_fields / boolean
  62. q.typ = "best_fields"
  63. q.tieBreaker = &zero
  64. case "most_fields":
  65. q.typ = "most_fields"
  66. q.tieBreaker = &one
  67. case "cross_fields":
  68. q.typ = "cross_fields"
  69. q.tieBreaker = &zero
  70. case "phrase":
  71. q.typ = "phrase"
  72. q.tieBreaker = &zero
  73. case "phrase_prefix":
  74. q.typ = "phrase_prefix"
  75. q.tieBreaker = &zero
  76. }
  77. return q
  78. }
  79. // Operator sets the operator to use when using boolean query.
  80. // It can be either AND or OR (default).
  81. func (q *MultiMatchQuery) Operator(operator string) *MultiMatchQuery {
  82. q.operator = operator
  83. return q
  84. }
  85. // Analyzer sets the analyzer to use explicitly. It defaults to use explicit
  86. // mapping config for the field, or, if not set, the default search analyzer.
  87. func (q *MultiMatchQuery) Analyzer(analyzer string) *MultiMatchQuery {
  88. q.analyzer = analyzer
  89. return q
  90. }
  91. // Boost sets the boost for this query.
  92. func (q *MultiMatchQuery) Boost(boost float64) *MultiMatchQuery {
  93. q.boost = &boost
  94. return q
  95. }
  96. // Slop sets the phrase slop if evaluated to a phrase query type.
  97. func (q *MultiMatchQuery) Slop(slop int) *MultiMatchQuery {
  98. q.slop = &slop
  99. return q
  100. }
  101. // Fuzziness sets the fuzziness used when evaluated to a fuzzy query type.
  102. // It defaults to "AUTO".
  103. func (q *MultiMatchQuery) Fuzziness(fuzziness string) *MultiMatchQuery {
  104. q.fuzziness = fuzziness
  105. return q
  106. }
  107. // PrefixLength for the fuzzy process.
  108. func (q *MultiMatchQuery) PrefixLength(prefixLength int) *MultiMatchQuery {
  109. q.prefixLength = &prefixLength
  110. return q
  111. }
  112. // MaxExpansions is the number of term expansions to use when using fuzzy
  113. // or prefix type query. It defaults to unbounded so it's recommended
  114. // to set it to a reasonable value for faster execution.
  115. func (q *MultiMatchQuery) MaxExpansions(maxExpansions int) *MultiMatchQuery {
  116. q.maxExpansions = &maxExpansions
  117. return q
  118. }
  119. // MinimumShouldMatch represents the minimum number of optional should clauses
  120. // to match.
  121. func (q *MultiMatchQuery) MinimumShouldMatch(minimumShouldMatch string) *MultiMatchQuery {
  122. q.minimumShouldMatch = minimumShouldMatch
  123. return q
  124. }
  125. func (q *MultiMatchQuery) Rewrite(rewrite string) *MultiMatchQuery {
  126. q.rewrite = rewrite
  127. return q
  128. }
  129. func (q *MultiMatchQuery) FuzzyRewrite(fuzzyRewrite string) *MultiMatchQuery {
  130. q.fuzzyRewrite = fuzzyRewrite
  131. return q
  132. }
  133. // TieBreaker for "best-match" disjunction queries (OR queries).
  134. // The tie breaker capability allows documents that match more than one
  135. // query clause (in this case on more than one field) to be scored better
  136. // than documents that match only the best of the fields, without confusing
  137. // this with the better case of two distinct matches in the multiple fields.
  138. //
  139. // A tie-breaker value of 1.0 is interpreted as a signal to score queries as
  140. // "most-match" queries where all matching query clauses are considered for scoring.
  141. func (q *MultiMatchQuery) TieBreaker(tieBreaker float64) *MultiMatchQuery {
  142. q.tieBreaker = &tieBreaker
  143. return q
  144. }
  145. // Lenient indicates whether format based failures will be ignored.
  146. func (q *MultiMatchQuery) Lenient(lenient bool) *MultiMatchQuery {
  147. q.lenient = &lenient
  148. return q
  149. }
  150. // CutoffFrequency sets a cutoff value in [0..1] (or absolute number >=1)
  151. // representing the maximum threshold of a terms document frequency to be
  152. // considered a low frequency term.
  153. func (q *MultiMatchQuery) CutoffFrequency(cutoff float64) *MultiMatchQuery {
  154. q.cutoffFrequency = &cutoff
  155. return q
  156. }
  157. // ZeroTermsQuery can be "all" or "none".
  158. func (q *MultiMatchQuery) ZeroTermsQuery(zeroTermsQuery string) *MultiMatchQuery {
  159. q.zeroTermsQuery = zeroTermsQuery
  160. return q
  161. }
  162. // QueryName sets the query name for the filter that can be used when
  163. // searching for matched filters per hit.
  164. func (q *MultiMatchQuery) QueryName(queryName string) *MultiMatchQuery {
  165. q.queryName = queryName
  166. return q
  167. }
  168. // Source returns JSON for the query.
  169. func (q *MultiMatchQuery) Source() (interface{}, error) {
  170. //
  171. // {
  172. // "multi_match" : {
  173. // "query" : "this is a test",
  174. // "fields" : [ "subject", "message" ]
  175. // }
  176. // }
  177. source := make(map[string]interface{})
  178. multiMatch := make(map[string]interface{})
  179. source["multi_match"] = multiMatch
  180. multiMatch["query"] = q.text
  181. if len(q.fields) > 0 {
  182. var fields []string
  183. for _, field := range q.fields {
  184. if boost, found := q.fieldBoosts[field]; found {
  185. if boost != nil {
  186. fields = append(fields, fmt.Sprintf("%s^%f", field, *boost))
  187. } else {
  188. fields = append(fields, field)
  189. }
  190. } else {
  191. fields = append(fields, field)
  192. }
  193. }
  194. multiMatch["fields"] = fields
  195. }
  196. if q.typ != "" {
  197. multiMatch["type"] = q.typ
  198. }
  199. if q.operator != "" {
  200. multiMatch["operator"] = q.operator
  201. }
  202. if q.analyzer != "" {
  203. multiMatch["analyzer"] = q.analyzer
  204. }
  205. if q.boost != nil {
  206. multiMatch["boost"] = *q.boost
  207. }
  208. if q.slop != nil {
  209. multiMatch["slop"] = *q.slop
  210. }
  211. if q.fuzziness != "" {
  212. multiMatch["fuzziness"] = q.fuzziness
  213. }
  214. if q.prefixLength != nil {
  215. multiMatch["prefix_length"] = *q.prefixLength
  216. }
  217. if q.maxExpansions != nil {
  218. multiMatch["max_expansions"] = *q.maxExpansions
  219. }
  220. if q.minimumShouldMatch != "" {
  221. multiMatch["minimum_should_match"] = q.minimumShouldMatch
  222. }
  223. if q.rewrite != "" {
  224. multiMatch["rewrite"] = q.rewrite
  225. }
  226. if q.fuzzyRewrite != "" {
  227. multiMatch["fuzzy_rewrite"] = q.fuzzyRewrite
  228. }
  229. if q.tieBreaker != nil {
  230. multiMatch["tie_breaker"] = *q.tieBreaker
  231. }
  232. if q.lenient != nil {
  233. multiMatch["lenient"] = *q.lenient
  234. }
  235. if q.cutoffFrequency != nil {
  236. multiMatch["cutoff_frequency"] = *q.cutoffFrequency
  237. }
  238. if q.zeroTermsQuery != "" {
  239. multiMatch["zero_terms_query"] = q.zeroTermsQuery
  240. }
  241. if q.queryName != "" {
  242. multiMatch["_name"] = q.queryName
  243. }
  244. return source, nil
  245. }