You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

215 lines
6.0 KiB

  1. // Copyright (c) 2017 Couchbase, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package searcher
  15. import (
  16. "fmt"
  17. "github.com/blevesearch/bleve/index"
  18. "github.com/blevesearch/bleve/search"
  19. )
  20. func NewMultiTermSearcher(indexReader index.IndexReader, terms []string,
  21. field string, boost float64, options search.SearcherOptions, limit bool) (
  22. search.Searcher, error) {
  23. if tooManyClauses(len(terms)) {
  24. if optionsDisjunctionOptimizable(options) {
  25. return optimizeMultiTermSearcher(indexReader, terms, field, boost, options)
  26. }
  27. if limit {
  28. return nil, tooManyClausesErr(field, len(terms))
  29. }
  30. }
  31. qsearchers, err := makeBatchSearchers(indexReader, terms, field, boost, options)
  32. if err != nil {
  33. return nil, err
  34. }
  35. // build disjunction searcher of these ranges
  36. return newMultiTermSearcherInternal(indexReader, qsearchers, field, boost,
  37. options, limit)
  38. }
  39. func NewMultiTermSearcherBytes(indexReader index.IndexReader, terms [][]byte,
  40. field string, boost float64, options search.SearcherOptions, limit bool) (
  41. search.Searcher, error) {
  42. if tooManyClauses(len(terms)) {
  43. if optionsDisjunctionOptimizable(options) {
  44. return optimizeMultiTermSearcherBytes(indexReader, terms, field, boost, options)
  45. }
  46. if limit {
  47. return nil, tooManyClausesErr(field, len(terms))
  48. }
  49. }
  50. qsearchers, err := makeBatchSearchersBytes(indexReader, terms, field, boost, options)
  51. if err != nil {
  52. return nil, err
  53. }
  54. // build disjunction searcher of these ranges
  55. return newMultiTermSearcherInternal(indexReader, qsearchers, field, boost,
  56. options, limit)
  57. }
  58. func newMultiTermSearcherInternal(indexReader index.IndexReader,
  59. searchers []search.Searcher, field string, boost float64,
  60. options search.SearcherOptions, limit bool) (
  61. search.Searcher, error) {
  62. // build disjunction searcher of these ranges
  63. searcher, err := newDisjunctionSearcher(indexReader, searchers, 0, options,
  64. limit)
  65. if err != nil {
  66. for _, s := range searchers {
  67. _ = s.Close()
  68. }
  69. return nil, err
  70. }
  71. return searcher, nil
  72. }
  73. func optimizeMultiTermSearcher(indexReader index.IndexReader, terms []string,
  74. field string, boost float64, options search.SearcherOptions) (
  75. search.Searcher, error) {
  76. var finalSearcher search.Searcher
  77. for len(terms) > 0 {
  78. var batchTerms []string
  79. if len(terms) > DisjunctionMaxClauseCount {
  80. batchTerms = terms[:DisjunctionMaxClauseCount]
  81. terms = terms[DisjunctionMaxClauseCount:]
  82. } else {
  83. batchTerms = terms
  84. terms = nil
  85. }
  86. batch, err := makeBatchSearchers(indexReader, batchTerms, field, boost, options)
  87. if err != nil {
  88. return nil, err
  89. }
  90. if finalSearcher != nil {
  91. batch = append(batch, finalSearcher)
  92. }
  93. cleanup := func() {
  94. for _, searcher := range batch {
  95. if searcher != nil {
  96. _ = searcher.Close()
  97. }
  98. }
  99. }
  100. finalSearcher, err = optimizeCompositeSearcher("disjunction:unadorned",
  101. indexReader, batch, options)
  102. // all searchers in batch should be closed, regardless of error or optimization failure
  103. // either we're returning, or continuing and only finalSearcher is needed for next loop
  104. cleanup()
  105. if err != nil {
  106. return nil, err
  107. }
  108. if finalSearcher == nil {
  109. return nil, fmt.Errorf("unable to optimize")
  110. }
  111. }
  112. return finalSearcher, nil
  113. }
  114. func makeBatchSearchers(indexReader index.IndexReader, terms []string, field string,
  115. boost float64, options search.SearcherOptions) ([]search.Searcher, error) {
  116. qsearchers := make([]search.Searcher, len(terms))
  117. qsearchersClose := func() {
  118. for _, searcher := range qsearchers {
  119. if searcher != nil {
  120. _ = searcher.Close()
  121. }
  122. }
  123. }
  124. for i, term := range terms {
  125. var err error
  126. qsearchers[i], err = NewTermSearcher(indexReader, term, field, boost, options)
  127. if err != nil {
  128. qsearchersClose()
  129. return nil, err
  130. }
  131. }
  132. return qsearchers, nil
  133. }
  134. func optimizeMultiTermSearcherBytes(indexReader index.IndexReader, terms [][]byte,
  135. field string, boost float64, options search.SearcherOptions) (
  136. search.Searcher, error) {
  137. var finalSearcher search.Searcher
  138. for len(terms) > 0 {
  139. var batchTerms [][]byte
  140. if len(terms) > DisjunctionMaxClauseCount {
  141. batchTerms = terms[:DisjunctionMaxClauseCount]
  142. terms = terms[DisjunctionMaxClauseCount:]
  143. } else {
  144. batchTerms = terms
  145. terms = nil
  146. }
  147. batch, err := makeBatchSearchersBytes(indexReader, batchTerms, field, boost, options)
  148. if err != nil {
  149. return nil, err
  150. }
  151. if finalSearcher != nil {
  152. batch = append(batch, finalSearcher)
  153. }
  154. cleanup := func() {
  155. for _, searcher := range batch {
  156. if searcher != nil {
  157. _ = searcher.Close()
  158. }
  159. }
  160. }
  161. finalSearcher, err = optimizeCompositeSearcher("disjunction:unadorned",
  162. indexReader, batch, options)
  163. // all searchers in batch should be closed, regardless of error or optimization failure
  164. // either we're returning, or continuing and only finalSearcher is needed for next loop
  165. cleanup()
  166. if err != nil {
  167. return nil, err
  168. }
  169. if finalSearcher == nil {
  170. return nil, fmt.Errorf("unable to optimize")
  171. }
  172. }
  173. return finalSearcher, nil
  174. }
  175. func makeBatchSearchersBytes(indexReader index.IndexReader, terms [][]byte, field string,
  176. boost float64, options search.SearcherOptions) ([]search.Searcher, error) {
  177. qsearchers := make([]search.Searcher, len(terms))
  178. qsearchersClose := func() {
  179. for _, searcher := range qsearchers {
  180. if searcher != nil {
  181. _ = searcher.Close()
  182. }
  183. }
  184. }
  185. for i, term := range terms {
  186. var err error
  187. qsearchers[i], err = NewTermSearcherBytes(indexReader, term, field, boost, options)
  188. if err != nil {
  189. qsearchersClose()
  190. return nil, err
  191. }
  192. }
  193. return qsearchers, nil
  194. }