explain.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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. "context"
  7. "fmt"
  8. "net/url"
  9. "strings"
  10. "gopkg.in/olivere/elastic.v5/uritemplates"
  11. )
  12. // ExplainService computes a score explanation for a query and
  13. // a specific document.
  14. // See https://www.elastic.co/guide/en/elasticsearch/reference/5.2/search-explain.html.
  15. type ExplainService struct {
  16. client *Client
  17. pretty bool
  18. id string
  19. index string
  20. typ string
  21. q string
  22. routing string
  23. lenient *bool
  24. analyzer string
  25. df string
  26. fields []string
  27. lowercaseExpandedTerms *bool
  28. xSourceInclude []string
  29. analyzeWildcard *bool
  30. parent string
  31. preference string
  32. xSource []string
  33. defaultOperator string
  34. xSourceExclude []string
  35. source string
  36. bodyJson interface{}
  37. bodyString string
  38. }
  39. // NewExplainService creates a new ExplainService.
  40. func NewExplainService(client *Client) *ExplainService {
  41. return &ExplainService{
  42. client: client,
  43. xSource: make([]string, 0),
  44. xSourceExclude: make([]string, 0),
  45. fields: make([]string, 0),
  46. xSourceInclude: make([]string, 0),
  47. }
  48. }
  49. // Id is the document ID.
  50. func (s *ExplainService) Id(id string) *ExplainService {
  51. s.id = id
  52. return s
  53. }
  54. // Index is the name of the index.
  55. func (s *ExplainService) Index(index string) *ExplainService {
  56. s.index = index
  57. return s
  58. }
  59. // Type is the type of the document.
  60. func (s *ExplainService) Type(typ string) *ExplainService {
  61. s.typ = typ
  62. return s
  63. }
  64. // Source is the URL-encoded query definition (instead of using the request body).
  65. func (s *ExplainService) Source(source string) *ExplainService {
  66. s.source = source
  67. return s
  68. }
  69. // XSourceExclude is a list of fields to exclude from the returned _source field.
  70. func (s *ExplainService) XSourceExclude(xSourceExclude ...string) *ExplainService {
  71. s.xSourceExclude = append(s.xSourceExclude, xSourceExclude...)
  72. return s
  73. }
  74. // Lenient specifies whether format-based query failures
  75. // (such as providing text to a numeric field) should be ignored.
  76. func (s *ExplainService) Lenient(lenient bool) *ExplainService {
  77. s.lenient = &lenient
  78. return s
  79. }
  80. // Query in the Lucene query string syntax.
  81. func (s *ExplainService) Q(q string) *ExplainService {
  82. s.q = q
  83. return s
  84. }
  85. // Routing sets a specific routing value.
  86. func (s *ExplainService) Routing(routing string) *ExplainService {
  87. s.routing = routing
  88. return s
  89. }
  90. // AnalyzeWildcard specifies whether wildcards and prefix queries
  91. // in the query string query should be analyzed (default: false).
  92. func (s *ExplainService) AnalyzeWildcard(analyzeWildcard bool) *ExplainService {
  93. s.analyzeWildcard = &analyzeWildcard
  94. return s
  95. }
  96. // Analyzer is the analyzer for the query string query.
  97. func (s *ExplainService) Analyzer(analyzer string) *ExplainService {
  98. s.analyzer = analyzer
  99. return s
  100. }
  101. // Df is the default field for query string query (default: _all).
  102. func (s *ExplainService) Df(df string) *ExplainService {
  103. s.df = df
  104. return s
  105. }
  106. // Fields is a list of fields to return in the response.
  107. func (s *ExplainService) Fields(fields ...string) *ExplainService {
  108. s.fields = append(s.fields, fields...)
  109. return s
  110. }
  111. // LowercaseExpandedTerms specifies whether query terms should be lowercased.
  112. func (s *ExplainService) LowercaseExpandedTerms(lowercaseExpandedTerms bool) *ExplainService {
  113. s.lowercaseExpandedTerms = &lowercaseExpandedTerms
  114. return s
  115. }
  116. // XSourceInclude is a list of fields to extract and return from the _source field.
  117. func (s *ExplainService) XSourceInclude(xSourceInclude ...string) *ExplainService {
  118. s.xSourceInclude = append(s.xSourceInclude, xSourceInclude...)
  119. return s
  120. }
  121. // DefaultOperator is the default operator for query string query (AND or OR).
  122. func (s *ExplainService) DefaultOperator(defaultOperator string) *ExplainService {
  123. s.defaultOperator = defaultOperator
  124. return s
  125. }
  126. // Parent is the ID of the parent document.
  127. func (s *ExplainService) Parent(parent string) *ExplainService {
  128. s.parent = parent
  129. return s
  130. }
  131. // Preference specifies the node or shard the operation should be performed on (default: random).
  132. func (s *ExplainService) Preference(preference string) *ExplainService {
  133. s.preference = preference
  134. return s
  135. }
  136. // XSource is true or false to return the _source field or not, or a list of fields to return.
  137. func (s *ExplainService) XSource(xSource ...string) *ExplainService {
  138. s.xSource = append(s.xSource, xSource...)
  139. return s
  140. }
  141. // Pretty indicates that the JSON response be indented and human readable.
  142. func (s *ExplainService) Pretty(pretty bool) *ExplainService {
  143. s.pretty = pretty
  144. return s
  145. }
  146. // Query sets a query definition using the Query DSL.
  147. func (s *ExplainService) Query(query Query) *ExplainService {
  148. src, err := query.Source()
  149. if err != nil {
  150. // Do nothing in case of an error
  151. return s
  152. }
  153. body := make(map[string]interface{})
  154. body["query"] = src
  155. s.bodyJson = body
  156. return s
  157. }
  158. // BodyJson sets the query definition using the Query DSL.
  159. func (s *ExplainService) BodyJson(body interface{}) *ExplainService {
  160. s.bodyJson = body
  161. return s
  162. }
  163. // BodyString sets the query definition using the Query DSL as a string.
  164. func (s *ExplainService) BodyString(body string) *ExplainService {
  165. s.bodyString = body
  166. return s
  167. }
  168. // buildURL builds the URL for the operation.
  169. func (s *ExplainService) buildURL() (string, url.Values, error) {
  170. // Build URL
  171. path, err := uritemplates.Expand("/{index}/{type}/{id}/_explain", map[string]string{
  172. "id": s.id,
  173. "index": s.index,
  174. "type": s.typ,
  175. })
  176. if err != nil {
  177. return "", url.Values{}, err
  178. }
  179. // Add query string parameters
  180. params := url.Values{}
  181. if s.pretty {
  182. params.Set("pretty", "1")
  183. }
  184. if len(s.xSource) > 0 {
  185. params.Set("_source", strings.Join(s.xSource, ","))
  186. }
  187. if s.defaultOperator != "" {
  188. params.Set("default_operator", s.defaultOperator)
  189. }
  190. if s.parent != "" {
  191. params.Set("parent", s.parent)
  192. }
  193. if s.preference != "" {
  194. params.Set("preference", s.preference)
  195. }
  196. if s.source != "" {
  197. params.Set("source", s.source)
  198. }
  199. if len(s.xSourceExclude) > 0 {
  200. params.Set("_source_exclude", strings.Join(s.xSourceExclude, ","))
  201. }
  202. if s.lenient != nil {
  203. params.Set("lenient", fmt.Sprintf("%v", *s.lenient))
  204. }
  205. if s.q != "" {
  206. params.Set("q", s.q)
  207. }
  208. if s.routing != "" {
  209. params.Set("routing", s.routing)
  210. }
  211. if len(s.fields) > 0 {
  212. params.Set("fields", strings.Join(s.fields, ","))
  213. }
  214. if s.lowercaseExpandedTerms != nil {
  215. params.Set("lowercase_expanded_terms", fmt.Sprintf("%v", *s.lowercaseExpandedTerms))
  216. }
  217. if len(s.xSourceInclude) > 0 {
  218. params.Set("_source_include", strings.Join(s.xSourceInclude, ","))
  219. }
  220. if s.analyzeWildcard != nil {
  221. params.Set("analyze_wildcard", fmt.Sprintf("%v", *s.analyzeWildcard))
  222. }
  223. if s.analyzer != "" {
  224. params.Set("analyzer", s.analyzer)
  225. }
  226. if s.df != "" {
  227. params.Set("df", s.df)
  228. }
  229. return path, params, nil
  230. }
  231. // Validate checks if the operation is valid.
  232. func (s *ExplainService) Validate() error {
  233. var invalid []string
  234. if s.index == "" {
  235. invalid = append(invalid, "Index")
  236. }
  237. if s.typ == "" {
  238. invalid = append(invalid, "Type")
  239. }
  240. if s.id == "" {
  241. invalid = append(invalid, "Id")
  242. }
  243. if len(invalid) > 0 {
  244. return fmt.Errorf("missing required fields: %v", invalid)
  245. }
  246. return nil
  247. }
  248. // Do executes the operation.
  249. func (s *ExplainService) Do(ctx context.Context) (*ExplainResponse, error) {
  250. // Check pre-conditions
  251. if err := s.Validate(); err != nil {
  252. return nil, err
  253. }
  254. // Get URL for request
  255. path, params, err := s.buildURL()
  256. if err != nil {
  257. return nil, err
  258. }
  259. // Setup HTTP request body
  260. var body interface{}
  261. if s.bodyJson != nil {
  262. body = s.bodyJson
  263. } else {
  264. body = s.bodyString
  265. }
  266. // Get HTTP response
  267. res, err := s.client.PerformRequest(ctx, "GET", path, params, body)
  268. if err != nil {
  269. return nil, err
  270. }
  271. // Return operation response
  272. ret := new(ExplainResponse)
  273. if err := s.client.decoder.Decode(res.Body, ret); err != nil {
  274. return nil, err
  275. }
  276. return ret, nil
  277. }
  278. // ExplainResponse is the response of ExplainService.Do.
  279. type ExplainResponse struct {
  280. Index string `json:"_index"`
  281. Type string `json:"_type"`
  282. Id string `json:"_id"`
  283. Matched bool `json:"matched"`
  284. Explanation map[string]interface{} `json:"explanation"`
  285. }