1
// Copyright (c) 2014 Couchbase, Inc.
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
7
// http://www.apache.org/licenses/LICENSE-2.0
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.
21
"github.com/blevesearch/bleve/index"
22
"github.com/blevesearch/bleve/search"
23
"github.com/blevesearch/bleve/search/scorer"
26
type ConjunctionSearcher struct {
27
indexReader index.IndexReader
28
searchers OrderedSearcherList
30
currs []*search.DocumentMatch
32
scorer *scorer.ConjunctionQueryScorer
34
options search.SearcherOptions
37
func NewConjunctionSearcher(indexReader index.IndexReader, qsearchers []search.Searcher, options search.SearcherOptions) (*ConjunctionSearcher, error) {
38
// build the downstream searchers
39
searchers := make(OrderedSearcherList, len(qsearchers))
40
for i, searcher := range qsearchers {
41
searchers[i] = searcher
46
rv := ConjunctionSearcher{
47
indexReader: indexReader,
50
currs: make([]*search.DocumentMatch, len(searchers)),
51
scorer: scorer.NewConjunctionQueryScorer(options),
57
func (s *ConjunctionSearcher) computeQueryNorm() {
58
// first calculate sum of squared weights
59
sumOfSquaredWeights := 0.0
60
for _, termSearcher := range s.searchers {
61
sumOfSquaredWeights += termSearcher.Weight()
63
// now compute query norm from this
64
s.queryNorm = 1.0 / math.Sqrt(sumOfSquaredWeights)
65
// finally tell all the downstream searchers the norm
66
for _, termSearcher := range s.searchers {
67
termSearcher.SetQueryNorm(s.queryNorm)
71
func (s *ConjunctionSearcher) initSearchers(ctx *search.SearchContext) error {
73
// get all searchers pointing at their first match
74
for i, termSearcher := range s.searchers {
75
if s.currs[i] != nil {
76
ctx.DocumentMatchPool.Put(s.currs[i])
78
s.currs[i], err = termSearcher.Next(ctx)
87
func (s *ConjunctionSearcher) Weight() float64 {
89
for _, searcher := range s.searchers {
90
rv += searcher.Weight()
95
func (s *ConjunctionSearcher) SetQueryNorm(qnorm float64) {
96
for _, searcher := range s.searchers {
97
searcher.SetQueryNorm(qnorm)
101
func (s *ConjunctionSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
103
err := s.initSearchers(ctx)
108
var rv *search.DocumentMatch
111
for s.currs[s.maxIDIdx] != nil {
112
maxID := s.currs[s.maxIDIdx].IndexInternalID
115
for i < len(s.currs) {
116
if s.currs[i] == nil {
125
cmp := maxID.Compare(s.currs[i].IndexInternalID)
132
// maxID < currs[i], so we found a new maxIDIdx
135
// advance the positions where [0 <= x < i], since we
136
// know they were equal to the former max entry
137
maxID = s.currs[s.maxIDIdx].IndexInternalID
138
for x := 0; x < i; x++ {
139
err = s.advanceChild(ctx, x, maxID)
148
// maxID > currs[i], so need to advance searchers[i]
149
err = s.advanceChild(ctx, i, maxID)
154
// don't bump i, so that we'll examine the just-advanced
158
// if we get here, a doc matched all readers, so score and add it
159
rv = s.scorer.Score(ctx, s.currs)
161
// we know all the searchers are pointing at the same thing
162
// so they all need to be bumped
163
for i, termSearcher := range s.searchers {
164
if s.currs[i] != rv {
165
ctx.DocumentMatchPool.Put(s.currs[i])
167
s.currs[i], err = termSearcher.Next(ctx)
173
// don't continue now, wait for the next call to Next()
179
func (s *ConjunctionSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
181
err := s.initSearchers(ctx)
186
for i := range s.searchers {
187
err := s.advanceChild(ctx, i, ID)
195
func (s *ConjunctionSearcher) advanceChild(ctx *search.SearchContext, i int, ID index.IndexInternalID) (err error) {
196
if s.currs[i] != nil {
197
ctx.DocumentMatchPool.Put(s.currs[i])
199
s.currs[i], err = s.searchers[i].Advance(ctx, ID)
203
func (s *ConjunctionSearcher) Count() uint64 {
204
// for now return a worst case
206
for _, searcher := range s.searchers {
207
sum += searcher.Count()
212
func (s *ConjunctionSearcher) Close() (rv error) {
213
for _, searcher := range s.searchers {
214
err := searcher.Close()
215
if err != nil && rv == nil {
222
func (s *ConjunctionSearcher) Min() int {
226
func (s *ConjunctionSearcher) DocumentMatchPoolSize() int {
228
for _, s := range s.searchers {
229
rv += s.DocumentMatchPoolSize()