explain.go 8.6 KB

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