123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- // Copyright (c) 2014 Couchbase, Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- package searcher
-
- import (
- "math"
-
- "github.com/blevesearch/bleve/index"
- "github.com/blevesearch/bleve/search"
- "github.com/blevesearch/bleve/search/scorer"
- )
-
- type BooleanSearcher struct {
- indexReader index.IndexReader
- mustSearcher search.Searcher
- shouldSearcher search.Searcher
- mustNotSearcher search.Searcher
- queryNorm float64
- currMust *search.DocumentMatch
- currShould *search.DocumentMatch
- currMustNot *search.DocumentMatch
- currentID index.IndexInternalID
- min uint64
- scorer *scorer.ConjunctionQueryScorer
- matches []*search.DocumentMatch
- initialized bool
- }
-
- func NewBooleanSearcher(indexReader index.IndexReader, mustSearcher search.Searcher, shouldSearcher search.Searcher, mustNotSearcher search.Searcher, options search.SearcherOptions) (*BooleanSearcher, error) {
- // build our searcher
- rv := BooleanSearcher{
- indexReader: indexReader,
- mustSearcher: mustSearcher,
- shouldSearcher: shouldSearcher,
- mustNotSearcher: mustNotSearcher,
- scorer: scorer.NewConjunctionQueryScorer(options),
- matches: make([]*search.DocumentMatch, 2),
- }
- rv.computeQueryNorm()
- return &rv, nil
- }
-
- func (s *BooleanSearcher) computeQueryNorm() {
- // first calculate sum of squared weights
- sumOfSquaredWeights := 0.0
- if s.mustSearcher != nil {
- sumOfSquaredWeights += s.mustSearcher.Weight()
- }
- if s.shouldSearcher != nil {
- sumOfSquaredWeights += s.shouldSearcher.Weight()
- }
-
- // now compute query norm from this
- s.queryNorm = 1.0 / math.Sqrt(sumOfSquaredWeights)
- // finally tell all the downstream searchers the norm
- if s.mustSearcher != nil {
- s.mustSearcher.SetQueryNorm(s.queryNorm)
- }
- if s.shouldSearcher != nil {
- s.shouldSearcher.SetQueryNorm(s.queryNorm)
- }
- }
-
- func (s *BooleanSearcher) initSearchers(ctx *search.SearchContext) error {
- var err error
- // get all searchers pointing at their first match
- if s.mustSearcher != nil {
- if s.currMust != nil {
- ctx.DocumentMatchPool.Put(s.currMust)
- }
- s.currMust, err = s.mustSearcher.Next(ctx)
- if err != nil {
- return err
- }
- }
-
- if s.shouldSearcher != nil {
- if s.currShould != nil {
- ctx.DocumentMatchPool.Put(s.currShould)
- }
- s.currShould, err = s.shouldSearcher.Next(ctx)
- if err != nil {
- return err
- }
- }
-
- if s.mustNotSearcher != nil {
- if s.currMustNot != nil {
- ctx.DocumentMatchPool.Put(s.currMustNot)
- }
- s.currMustNot, err = s.mustNotSearcher.Next(ctx)
- if err != nil {
- return err
- }
- }
-
- if s.mustSearcher != nil && s.currMust != nil {
- s.currentID = s.currMust.IndexInternalID
- } else if s.mustSearcher == nil && s.currShould != nil {
- s.currentID = s.currShould.IndexInternalID
- } else {
- s.currentID = nil
- }
-
- s.initialized = true
- return nil
- }
-
- func (s *BooleanSearcher) advanceNextMust(ctx *search.SearchContext, skipReturn *search.DocumentMatch) error {
- var err error
-
- if s.mustSearcher != nil {
- if s.currMust != skipReturn {
- ctx.DocumentMatchPool.Put(s.currMust)
- }
- s.currMust, err = s.mustSearcher.Next(ctx)
- if err != nil {
- return err
- }
- } else {
- if s.currShould != skipReturn {
- ctx.DocumentMatchPool.Put(s.currShould)
- }
- s.currShould, err = s.shouldSearcher.Next(ctx)
- if err != nil {
- return err
- }
- }
-
- if s.mustSearcher != nil && s.currMust != nil {
- s.currentID = s.currMust.IndexInternalID
- } else if s.mustSearcher == nil && s.currShould != nil {
- s.currentID = s.currShould.IndexInternalID
- } else {
- s.currentID = nil
- }
- return nil
- }
-
- func (s *BooleanSearcher) Weight() float64 {
- var rv float64
- if s.mustSearcher != nil {
- rv += s.mustSearcher.Weight()
- }
- if s.shouldSearcher != nil {
- rv += s.shouldSearcher.Weight()
- }
-
- return rv
- }
-
- func (s *BooleanSearcher) SetQueryNorm(qnorm float64) {
- if s.mustSearcher != nil {
- s.mustSearcher.SetQueryNorm(qnorm)
- }
- if s.shouldSearcher != nil {
- s.shouldSearcher.SetQueryNorm(qnorm)
- }
- }
-
- func (s *BooleanSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
-
- if !s.initialized {
- err := s.initSearchers(ctx)
- if err != nil {
- return nil, err
- }
- }
-
- var err error
- var rv *search.DocumentMatch
-
- for s.currentID != nil {
- if s.currMustNot != nil {
- cmp := s.currMustNot.IndexInternalID.Compare(s.currentID)
- if cmp < 0 {
- ctx.DocumentMatchPool.Put(s.currMustNot)
- // advance must not searcher to our candidate entry
- s.currMustNot, err = s.mustNotSearcher.Advance(ctx, s.currentID)
- if err != nil {
- return nil, err
- }
- if s.currMustNot != nil && s.currMustNot.IndexInternalID.Equals(s.currentID) {
- // the candidate is excluded
- err = s.advanceNextMust(ctx, nil)
- if err != nil {
- return nil, err
- }
- continue
- }
- } else if cmp == 0 {
- // the candidate is excluded
- err = s.advanceNextMust(ctx, nil)
- if err != nil {
- return nil, err
- }
- continue
- }
- }
-
- shouldCmpOrNil := 1 // NOTE: shouldCmp will also be 1 when currShould == nil.
- if s.currShould != nil {
- shouldCmpOrNil = s.currShould.IndexInternalID.Compare(s.currentID)
- }
-
- if shouldCmpOrNil < 0 {
- ctx.DocumentMatchPool.Put(s.currShould)
- // advance should searcher to our candidate entry
- s.currShould, err = s.shouldSearcher.Advance(ctx, s.currentID)
- if err != nil {
- return nil, err
- }
- if s.currShould != nil && s.currShould.IndexInternalID.Equals(s.currentID) {
- // score bonus matches should
- var cons []*search.DocumentMatch
- if s.currMust != nil {
- cons = s.matches
- cons[0] = s.currMust
- cons[1] = s.currShould
- } else {
- cons = s.matches[0:1]
- cons[0] = s.currShould
- }
- rv = s.scorer.Score(ctx, cons)
- err = s.advanceNextMust(ctx, rv)
- if err != nil {
- return nil, err
- }
- break
- } else if s.shouldSearcher.Min() == 0 {
- // match is OK anyway
- cons := s.matches[0:1]
- cons[0] = s.currMust
- rv = s.scorer.Score(ctx, cons)
- err = s.advanceNextMust(ctx, rv)
- if err != nil {
- return nil, err
- }
- break
- }
- } else if shouldCmpOrNil == 0 {
- // score bonus matches should
- var cons []*search.DocumentMatch
- if s.currMust != nil {
- cons = s.matches
- cons[0] = s.currMust
- cons[1] = s.currShould
- } else {
- cons = s.matches[0:1]
- cons[0] = s.currShould
- }
- rv = s.scorer.Score(ctx, cons)
- err = s.advanceNextMust(ctx, rv)
- if err != nil {
- return nil, err
- }
- break
- } else if s.shouldSearcher == nil || s.shouldSearcher.Min() == 0 {
- // match is OK anyway
- cons := s.matches[0:1]
- cons[0] = s.currMust
- rv = s.scorer.Score(ctx, cons)
- err = s.advanceNextMust(ctx, rv)
- if err != nil {
- return nil, err
- }
- break
- }
-
- err = s.advanceNextMust(ctx, nil)
- if err != nil {
- return nil, err
- }
- }
- return rv, nil
- }
-
- func (s *BooleanSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
-
- if !s.initialized {
- err := s.initSearchers(ctx)
- if err != nil {
- return nil, err
- }
- }
-
- var err error
- if s.mustSearcher != nil {
- if s.currMust != nil {
- ctx.DocumentMatchPool.Put(s.currMust)
- }
- s.currMust, err = s.mustSearcher.Advance(ctx, ID)
- if err != nil {
- return nil, err
- }
- }
- if s.shouldSearcher != nil {
- if s.currShould != nil {
- ctx.DocumentMatchPool.Put(s.currShould)
- }
- s.currShould, err = s.shouldSearcher.Advance(ctx, ID)
- if err != nil {
- return nil, err
- }
- }
- if s.mustNotSearcher != nil {
- if s.currMustNot != nil {
- ctx.DocumentMatchPool.Put(s.currMustNot)
- }
- s.currMustNot, err = s.mustNotSearcher.Advance(ctx, ID)
- if err != nil {
- return nil, err
- }
- }
-
- if s.mustSearcher != nil && s.currMust != nil {
- s.currentID = s.currMust.IndexInternalID
- } else if s.mustSearcher == nil && s.currShould != nil {
- s.currentID = s.currShould.IndexInternalID
- } else {
- s.currentID = nil
- }
-
- return s.Next(ctx)
- }
-
- func (s *BooleanSearcher) Count() uint64 {
-
- // for now return a worst case
- var sum uint64
- if s.mustSearcher != nil {
- sum += s.mustSearcher.Count()
- }
- if s.shouldSearcher != nil {
- sum += s.shouldSearcher.Count()
- }
- return sum
- }
-
- func (s *BooleanSearcher) Close() error {
- var err0, err1, err2 error
- if s.mustSearcher != nil {
- err0 = s.mustSearcher.Close()
- }
- if s.shouldSearcher != nil {
- err1 = s.shouldSearcher.Close()
- }
- if s.mustNotSearcher != nil {
- err2 = s.mustNotSearcher.Close()
- }
- if err0 != nil {
- return err0
- }
- if err1 != nil {
- return err1
- }
- if err2 != nil {
- return err2
- }
- return nil
- }
-
- func (s *BooleanSearcher) Min() int {
- return 0
- }
-
- func (s *BooleanSearcher) DocumentMatchPoolSize() int {
- rv := 3
- if s.mustSearcher != nil {
- rv += s.mustSearcher.DocumentMatchPoolSize()
- }
- if s.shouldSearcher != nil {
- rv += s.shouldSearcher.DocumentMatchPoolSize()
- }
- if s.mustNotSearcher != nil {
- rv += s.mustNotSearcher.DocumentMatchPoolSize()
- }
- return rv
- }
|