~ubuntu-branches/debian/sid/golang-github-blevesearch-bleve/sid

« back to all changes in this revision

Viewing changes to search/searcher/search_conjunction.go

  • Committer: Package Import Robot
  • Author(s): Michael Lustfield
  • Date: 2017-03-30 16:06:03 UTC
  • Revision ID: package-import@ubuntu.com-20170330160603-0oogmb960l7918jx
Tags: upstream-0.5.0+git20170324.202.4702785f
ImportĀ upstreamĀ versionĀ 0.5.0+git20170324.202.4702785f

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//  Copyright (c) 2014 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
 
 
15
package searcher
 
16
 
 
17
import (
 
18
        "math"
 
19
        "sort"
 
20
 
 
21
        "github.com/blevesearch/bleve/index"
 
22
        "github.com/blevesearch/bleve/search"
 
23
        "github.com/blevesearch/bleve/search/scorer"
 
24
)
 
25
 
 
26
type ConjunctionSearcher struct {
 
27
        indexReader index.IndexReader
 
28
        searchers   OrderedSearcherList
 
29
        queryNorm   float64
 
30
        currs       []*search.DocumentMatch
 
31
        maxIDIdx    int
 
32
        scorer      *scorer.ConjunctionQueryScorer
 
33
        initialized bool
 
34
        options     search.SearcherOptions
 
35
}
 
36
 
 
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
 
42
        }
 
43
        // sort the searchers
 
44
        sort.Sort(searchers)
 
45
        // build our searcher
 
46
        rv := ConjunctionSearcher{
 
47
                indexReader: indexReader,
 
48
                options:     options,
 
49
                searchers:   searchers,
 
50
                currs:       make([]*search.DocumentMatch, len(searchers)),
 
51
                scorer:      scorer.NewConjunctionQueryScorer(options),
 
52
        }
 
53
        rv.computeQueryNorm()
 
54
        return &rv, nil
 
55
}
 
56
 
 
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()
 
62
        }
 
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)
 
68
        }
 
69
}
 
70
 
 
71
func (s *ConjunctionSearcher) initSearchers(ctx *search.SearchContext) error {
 
72
        var err 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])
 
77
                }
 
78
                s.currs[i], err = termSearcher.Next(ctx)
 
79
                if err != nil {
 
80
                        return err
 
81
                }
 
82
        }
 
83
        s.initialized = true
 
84
        return nil
 
85
}
 
86
 
 
87
func (s *ConjunctionSearcher) Weight() float64 {
 
88
        var rv float64
 
89
        for _, searcher := range s.searchers {
 
90
                rv += searcher.Weight()
 
91
        }
 
92
        return rv
 
93
}
 
94
 
 
95
func (s *ConjunctionSearcher) SetQueryNorm(qnorm float64) {
 
96
        for _, searcher := range s.searchers {
 
97
                searcher.SetQueryNorm(qnorm)
 
98
        }
 
99
}
 
100
 
 
101
func (s *ConjunctionSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
 
102
        if !s.initialized {
 
103
                err := s.initSearchers(ctx)
 
104
                if err != nil {
 
105
                        return nil, err
 
106
                }
 
107
        }
 
108
        var rv *search.DocumentMatch
 
109
        var err error
 
110
OUTER:
 
111
        for s.currs[s.maxIDIdx] != nil {
 
112
                maxID := s.currs[s.maxIDIdx].IndexInternalID
 
113
 
 
114
                i := 0
 
115
                for i < len(s.currs) {
 
116
                        if s.currs[i] == nil {
 
117
                                return nil, nil
 
118
                        }
 
119
 
 
120
                        if i == s.maxIDIdx {
 
121
                                i++
 
122
                                continue
 
123
                        }
 
124
 
 
125
                        cmp := maxID.Compare(s.currs[i].IndexInternalID)
 
126
                        if cmp == 0 {
 
127
                                i++
 
128
                                continue
 
129
                        }
 
130
 
 
131
                        if cmp < 0 {
 
132
                                // maxID < currs[i], so we found a new maxIDIdx
 
133
                                s.maxIDIdx = i
 
134
 
 
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)
 
140
                                        if err != nil {
 
141
                                                return nil, err
 
142
                                        }
 
143
                                }
 
144
 
 
145
                                continue OUTER
 
146
                        }
 
147
 
 
148
                        // maxID > currs[i], so need to advance searchers[i]
 
149
                        err = s.advanceChild(ctx, i, maxID)
 
150
                        if err != nil {
 
151
                                return nil, err
 
152
                        }
 
153
 
 
154
                        // don't bump i, so that we'll examine the just-advanced
 
155
                        // currs[i] again
 
156
                }
 
157
 
 
158
                // if we get here, a doc matched all readers, so score and add it
 
159
                rv = s.scorer.Score(ctx, s.currs)
 
160
 
 
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])
 
166
                        }
 
167
                        s.currs[i], err = termSearcher.Next(ctx)
 
168
                        if err != nil {
 
169
                                return nil, err
 
170
                        }
 
171
                }
 
172
 
 
173
                // don't continue now, wait for the next call to Next()
 
174
                break
 
175
        }
 
176
        return rv, nil
 
177
}
 
178
 
 
179
func (s *ConjunctionSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
 
180
        if !s.initialized {
 
181
                err := s.initSearchers(ctx)
 
182
                if err != nil {
 
183
                        return nil, err
 
184
                }
 
185
        }
 
186
        for i := range s.searchers {
 
187
                err := s.advanceChild(ctx, i, ID)
 
188
                if err != nil {
 
189
                        return nil, err
 
190
                }
 
191
        }
 
192
        return s.Next(ctx)
 
193
}
 
194
 
 
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])
 
198
        }
 
199
        s.currs[i], err = s.searchers[i].Advance(ctx, ID)
 
200
        return err
 
201
}
 
202
 
 
203
func (s *ConjunctionSearcher) Count() uint64 {
 
204
        // for now return a worst case
 
205
        var sum uint64
 
206
        for _, searcher := range s.searchers {
 
207
                sum += searcher.Count()
 
208
        }
 
209
        return sum
 
210
}
 
211
 
 
212
func (s *ConjunctionSearcher) Close() (rv error) {
 
213
        for _, searcher := range s.searchers {
 
214
                err := searcher.Close()
 
215
                if err != nil && rv == nil {
 
216
                        rv = err
 
217
                }
 
218
        }
 
219
        return rv
 
220
}
 
221
 
 
222
func (s *ConjunctionSearcher) Min() int {
 
223
        return 0
 
224
}
 
225
 
 
226
func (s *ConjunctionSearcher) DocumentMatchPoolSize() int {
 
227
        rv := len(s.currs)
 
228
        for _, s := range s.searchers {
 
229
                rv += s.DocumentMatchPoolSize()
 
230
        }
 
231
        return rv
 
232
}