bugfixes more search results and dump restore options

This commit is contained in:
ston1th 2016-11-17 21:21:16 +01:00
commit 3cd460318a
124 changed files with 3915 additions and 9579 deletions

View file

@ -18,7 +18,6 @@ import (
)
type BooleanSearcher struct {
initialized bool
indexReader index.IndexReader
mustSearcher search.Searcher
shouldSearcher search.Searcher
@ -27,9 +26,11 @@ type BooleanSearcher struct {
currMust *search.DocumentMatch
currShould *search.DocumentMatch
currMustNot *search.DocumentMatch
currentID string
currentID index.IndexInternalID
min uint64
scorer *scorers.ConjunctionQueryScorer
matches []*search.DocumentMatch
initialized bool
}
func NewBooleanSearcher(indexReader index.IndexReader, mustSearcher search.Searcher, shouldSearcher search.Searcher, mustNotSearcher search.Searcher, explain bool) (*BooleanSearcher, error) {
@ -40,6 +41,7 @@ func NewBooleanSearcher(indexReader index.IndexReader, mustSearcher search.Searc
shouldSearcher: shouldSearcher,
mustNotSearcher: mustNotSearcher,
scorer: scorers.NewConjunctionQueryScorer(explain),
matches: make([]*search.DocumentMatch, 2),
}
rv.computeQueryNorm()
return &rv, nil
@ -66,63 +68,78 @@ func (s *BooleanSearcher) computeQueryNorm() {
}
}
func (s *BooleanSearcher) initSearchers() error {
func (s *BooleanSearcher) initSearchers(ctx *search.SearchContext) error {
var err error
// get all searchers pointing at their first match
if s.mustSearcher != nil {
s.currMust, err = s.mustSearcher.Next()
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 {
s.currShould, err = s.shouldSearcher.Next()
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 {
s.currMustNot, err = s.mustNotSearcher.Next()
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.ID
s.currentID = s.currMust.IndexInternalID
} else if s.mustSearcher == nil && s.currShould != nil {
s.currentID = s.currShould.ID
s.currentID = s.currShould.IndexInternalID
} else {
s.currentID = ""
s.currentID = nil
}
s.initialized = true
return nil
}
func (s *BooleanSearcher) advanceNextMust() error {
func (s *BooleanSearcher) advanceNextMust(ctx *search.SearchContext, skipReturn *search.DocumentMatch) error {
var err error
if s.mustSearcher != nil {
s.currMust, err = s.mustSearcher.Next()
if s.currMust != skipReturn {
ctx.DocumentMatchPool.Put(s.currMust)
}
s.currMust, err = s.mustSearcher.Next(ctx)
if err != nil {
return err
}
} else if s.mustSearcher == nil {
s.currShould, err = s.shouldSearcher.Next()
} 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.ID
s.currentID = s.currMust.IndexInternalID
} else if s.mustSearcher == nil && s.currShould != nil {
s.currentID = s.currShould.ID
s.currentID = s.currShould.IndexInternalID
} else {
s.currentID = ""
s.currentID = nil
}
return nil
}
@ -148,10 +165,10 @@ func (s *BooleanSearcher) SetQueryNorm(qnorm float64) {
}
}
func (s *BooleanSearcher) Next() (*search.DocumentMatch, error) {
func (s *BooleanSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
if !s.initialized {
err := s.initSearchers()
err := s.initSearchers(ctx)
if err != nil {
return nil, err
}
@ -160,94 +177,104 @@ func (s *BooleanSearcher) Next() (*search.DocumentMatch, error) {
var err error
var rv *search.DocumentMatch
for s.currentID != "" {
if s.currMustNot != nil && s.currMustNot.ID < s.currentID {
// advance must not searcher to our candidate entry
s.currMustNot, err = s.mustNotSearcher.Advance(s.currentID)
if err != nil {
return nil, err
}
if s.currMustNot != nil && s.currMustNot.ID == s.currentID {
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()
err = s.advanceNextMust(ctx, nil)
if err != nil {
return nil, err
}
continue
}
} else if s.currMustNot != nil && s.currMustNot.ID == s.currentID {
// the candidate is excluded
err = s.advanceNextMust()
if err != nil {
return nil, err
}
continue
}
if s.currShould != nil && s.currShould.ID < s.currentID {
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(s.currentID)
s.currShould, err = s.shouldSearcher.Advance(ctx, s.currentID)
if err != nil {
return nil, err
}
if s.currShould != nil && s.currShould.ID == s.currentID {
if s.currShould != nil && s.currShould.IndexInternalID.Equals(s.currentID) {
// score bonus matches should
var cons []*search.DocumentMatch
if s.currMust != nil {
cons = []*search.DocumentMatch{
s.currMust,
s.currShould,
}
cons = s.matches
cons[0] = s.currMust
cons[1] = s.currShould
} else {
cons = []*search.DocumentMatch{
s.currShould,
}
cons = s.matches[0:1]
cons[0] = s.currShould
}
rv = s.scorer.Score(cons)
err = s.advanceNextMust()
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
rv = s.scorer.Score([]*search.DocumentMatch{s.currMust})
err = s.advanceNextMust()
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 s.currShould != nil && s.currShould.ID == s.currentID {
} else if shouldCmpOrNil == 0 {
// score bonus matches should
var cons []*search.DocumentMatch
if s.currMust != nil {
cons = []*search.DocumentMatch{
s.currMust,
s.currShould,
}
cons = s.matches
cons[0] = s.currMust
cons[1] = s.currShould
} else {
cons = []*search.DocumentMatch{
s.currShould,
}
cons = s.matches[0:1]
cons[0] = s.currShould
}
rv = s.scorer.Score(cons)
err = s.advanceNextMust()
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
rv = s.scorer.Score([]*search.DocumentMatch{s.currMust})
err = s.advanceNextMust()
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()
err = s.advanceNextMust(ctx, nil)
if err != nil {
return nil, err
}
@ -255,10 +282,10 @@ func (s *BooleanSearcher) Next() (*search.DocumentMatch, error) {
return rv, nil
}
func (s *BooleanSearcher) Advance(ID string) (*search.DocumentMatch, error) {
func (s *BooleanSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
if !s.initialized {
err := s.initSearchers()
err := s.initSearchers(ctx)
if err != nil {
return nil, err
}
@ -266,33 +293,42 @@ func (s *BooleanSearcher) Advance(ID string) (*search.DocumentMatch, error) {
var err error
if s.mustSearcher != nil {
s.currMust, err = s.mustSearcher.Advance(ID)
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 {
s.currShould, err = s.shouldSearcher.Advance(ID)
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 {
s.currMustNot, err = s.mustNotSearcher.Advance(ID)
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.ID
s.currentID = s.currMust.IndexInternalID
} else if s.mustSearcher == nil && s.currShould != nil {
s.currentID = s.currShould.ID
s.currentID = s.currShould.IndexInternalID
} else {
s.currentID = ""
s.currentID = nil
}
return s.Next()
return s.Next(ctx)
}
func (s *BooleanSearcher) Count() uint64 {
@ -333,3 +369,17 @@ func (s *BooleanSearcher) Close() error {
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
}

View file

@ -19,14 +19,14 @@ import (
)
type ConjunctionSearcher struct {
initialized bool
indexReader index.IndexReader
searchers OrderedSearcherList
explain bool
queryNorm float64
currs []*search.DocumentMatch
currentID string
maxIDIdx int
scorer *scorers.ConjunctionQueryScorer
initialized bool
explain bool
}
func NewConjunctionSearcher(indexReader index.IndexReader, qsearchers []search.Searcher, explain bool) (*ConjunctionSearcher, error) {
@ -63,24 +63,18 @@ func (s *ConjunctionSearcher) computeQueryNorm() {
}
}
func (s *ConjunctionSearcher) initSearchers() error {
func (s *ConjunctionSearcher) initSearchers(ctx *search.SearchContext) error {
var err error
// get all searchers pointing at their first match
for i, termSearcher := range s.searchers {
s.currs[i], err = termSearcher.Next()
if s.currs[i] != nil {
ctx.DocumentMatchPool.Put(s.currs[i])
}
s.currs[i], err = termSearcher.Next(ctx)
if err != nil {
return err
}
}
if len(s.currs) > 0 {
if s.currs[0] != nil {
s.currentID = s.currs[0].ID
} else {
s.currentID = ""
}
}
s.initialized = true
return nil
}
@ -99,9 +93,9 @@ func (s *ConjunctionSearcher) SetQueryNorm(qnorm float64) {
}
}
func (s *ConjunctionSearcher) Next() (*search.DocumentMatch, error) {
func (s *ConjunctionSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
if !s.initialized {
err := s.initSearchers()
err := s.initSearchers(ctx)
if err != nil {
return nil, err
}
@ -109,68 +103,96 @@ func (s *ConjunctionSearcher) Next() (*search.DocumentMatch, error) {
var rv *search.DocumentMatch
var err error
OUTER:
for s.currentID != "" {
for i, termSearcher := range s.searchers {
if s.currs[i] != nil && s.currs[i].ID != s.currentID {
if s.currentID < s.currs[i].ID {
s.currentID = s.currs[i].ID
continue OUTER
for s.currs[s.maxIDIdx] != nil {
maxID := s.currs[s.maxIDIdx].IndexInternalID
i := 0
for i < len(s.currs) {
if s.currs[i] == nil {
return nil, nil
}
if i == s.maxIDIdx {
i++
continue
}
cmp := maxID.Compare(s.currs[i].IndexInternalID)
if cmp == 0 {
i++
continue
}
if cmp < 0 {
// maxID < currs[i], so we found a new maxIDIdx
s.maxIDIdx = i
// advance the positions where [0 <= x < i], since we
// know they were equal to the former max entry
maxID = s.currs[s.maxIDIdx].IndexInternalID
for x := 0; x < i; x++ {
err = s.advanceChild(ctx, x, maxID)
if err != nil {
return nil, err
}
}
// this reader doesn't have the currentID, try to advance
s.currs[i], err = termSearcher.Advance(s.currentID)
if err != nil {
return nil, err
}
if s.currs[i] == nil {
s.currentID = ""
continue OUTER
}
if s.currs[i].ID != s.currentID {
// we just advanced, so it doesn't match, it must be greater
// no need to call next
s.currentID = s.currs[i].ID
continue OUTER
}
} else if s.currs[i] == nil {
s.currentID = ""
continue OUTER
}
}
// if we get here, a doc matched all readers, sum the score and add it
rv = s.scorer.Score(s.currs)
// prepare for next entry
s.currs[0], err = s.searchers[0].Next()
if err != nil {
return nil, err
// maxID > currs[i], so need to advance searchers[i]
err = s.advanceChild(ctx, i, maxID)
if err != nil {
return nil, err
}
// don't bump i, so that we'll examine the just-advanced
// currs[i] again
}
if s.currs[0] == nil {
s.currentID = ""
} else {
s.currentID = s.currs[0].ID
// if we get here, a doc matched all readers, so score and add it
rv = s.scorer.Score(ctx, s.currs)
// we know all the searchers are pointing at the same thing
// so they all need to be bumped
for i, termSearcher := range s.searchers {
if s.currs[i] != rv {
ctx.DocumentMatchPool.Put(s.currs[i])
}
s.currs[i], err = termSearcher.Next(ctx)
if err != nil {
return nil, err
}
}
// don't continue now, wait for the next call to Next()
break
}
return rv, nil
}
func (s *ConjunctionSearcher) Advance(ID string) (*search.DocumentMatch, error) {
func (s *ConjunctionSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
if !s.initialized {
err := s.initSearchers()
err := s.initSearchers(ctx)
if err != nil {
return nil, err
}
}
var err error
for i, searcher := range s.searchers {
s.currs[i], err = searcher.Advance(ID)
for i := range s.searchers {
err := s.advanceChild(ctx, i, ID)
if err != nil {
return nil, err
}
}
s.currentID = ID
return s.Next()
return s.Next(ctx)
}
func (s *ConjunctionSearcher) advanceChild(ctx *search.SearchContext, i int, ID index.IndexInternalID) (err error) {
if s.currs[i] != nil {
ctx.DocumentMatchPool.Put(s.currs[i])
}
s.currs[i], err = s.searchers[i].Advance(ctx, ID)
return err
}
func (s *ConjunctionSearcher) Count() uint64 {
@ -195,3 +217,11 @@ func (s *ConjunctionSearcher) Close() error {
func (s *ConjunctionSearcher) Min() int {
return 0
}
func (s *ConjunctionSearcher) DocumentMatchPoolSize() int {
rv := len(s.currs)
for _, s := range s.searchers {
rv += s.DocumentMatchPoolSize()
}
return rv
}

View file

@ -25,19 +25,31 @@ import (
var DisjunctionMaxClauseCount = 0
type DisjunctionSearcher struct {
initialized bool
indexReader index.IndexReader
searchers OrderedSearcherList
queryNorm float64
currs []*search.DocumentMatch
currentID string
scorer *scorers.DisjunctionQueryScorer
min float64
indexReader index.IndexReader
searchers OrderedSearcherList
queryNorm float64
currs []*search.DocumentMatch
scorer *scorers.DisjunctionQueryScorer
min int
matching []*search.DocumentMatch
matchingIdxs []int
initialized bool
}
func tooManyClauses(count int) bool {
if DisjunctionMaxClauseCount != 0 && count > DisjunctionMaxClauseCount {
return true
}
return false
}
func tooManyClausesErr() error {
return fmt.Errorf("TooManyClauses[maxClauseCount is set to %d]", DisjunctionMaxClauseCount)
}
func NewDisjunctionSearcher(indexReader index.IndexReader, qsearchers []search.Searcher, min float64, explain bool) (*DisjunctionSearcher, error) {
if DisjunctionMaxClauseCount != 0 && len(qsearchers) > DisjunctionMaxClauseCount {
return nil, fmt.Errorf("TooManyClauses[maxClauseCount is set to %d]", DisjunctionMaxClauseCount)
if tooManyClauses(len(qsearchers)) {
return nil, tooManyClausesErr()
}
// build the downstream searchers
searchers := make(OrderedSearcherList, len(qsearchers))
@ -48,11 +60,13 @@ func NewDisjunctionSearcher(indexReader index.IndexReader, qsearchers []search.S
sort.Sort(sort.Reverse(searchers))
// build our searcher
rv := DisjunctionSearcher{
indexReader: indexReader,
searchers: searchers,
currs: make([]*search.DocumentMatch, len(searchers)),
scorer: scorers.NewDisjunctionQueryScorer(explain),
min: min,
indexReader: indexReader,
searchers: searchers,
currs: make([]*search.DocumentMatch, len(searchers)),
scorer: scorers.NewDisjunctionQueryScorer(explain),
min: int(min),
matching: make([]*search.DocumentMatch, len(searchers)),
matchingIdxs: make([]int, len(searchers)),
}
rv.computeQueryNorm()
return &rv, nil
@ -72,29 +86,51 @@ func (s *DisjunctionSearcher) computeQueryNorm() {
}
}
func (s *DisjunctionSearcher) initSearchers() error {
func (s *DisjunctionSearcher) initSearchers(ctx *search.SearchContext) error {
var err error
// get all searchers pointing at their first match
for i, termSearcher := range s.searchers {
s.currs[i], err = termSearcher.Next()
if s.currs[i] != nil {
ctx.DocumentMatchPool.Put(s.currs[i])
}
s.currs[i], err = termSearcher.Next(ctx)
if err != nil {
return err
}
}
s.currentID = s.nextSmallestID()
s.updateMatches()
s.initialized = true
return nil
}
func (s *DisjunctionSearcher) nextSmallestID() string {
rv := ""
for _, curr := range s.currs {
if curr != nil && (curr.ID < rv || rv == "") {
rv = curr.ID
func (s *DisjunctionSearcher) updateMatches() {
matching := s.matching[:0]
matchingIdxs := s.matchingIdxs[:0]
for i, curr := range s.currs {
if curr == nil {
continue
}
if len(matching) > 0 {
cmp := curr.IndexInternalID.Compare(matching[0].IndexInternalID)
if cmp > 0 {
continue
}
if cmp < 0 {
matching = matching[:0]
matchingIdxs = matchingIdxs[:0]
}
}
matching = append(matching, curr)
matchingIdxs = append(matchingIdxs, i)
}
return rv
s.matching = matching
s.matchingIdxs = matchingIdxs
}
func (s *DisjunctionSearcher) Weight() float64 {
@ -111,51 +147,44 @@ func (s *DisjunctionSearcher) SetQueryNorm(qnorm float64) {
}
}
func (s *DisjunctionSearcher) Next() (*search.DocumentMatch, error) {
func (s *DisjunctionSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
if !s.initialized {
err := s.initSearchers()
err := s.initSearchers(ctx)
if err != nil {
return nil, err
}
}
var err error
var rv *search.DocumentMatch
matching := make([]*search.DocumentMatch, 0, len(s.searchers))
found := false
for !found && s.currentID != "" {
for _, curr := range s.currs {
if curr != nil && curr.ID == s.currentID {
matching = append(matching, curr)
}
}
if len(matching) >= int(s.min) {
for !found && len(s.matching) > 0 {
if len(s.matching) >= s.min {
found = true
// score this match
rv = s.scorer.Score(matching, len(matching), len(s.searchers))
rv = s.scorer.Score(ctx, s.matching, len(s.matching), len(s.searchers))
}
// reset matching
matching = make([]*search.DocumentMatch, 0)
// invoke next on all the matching searchers
for i, curr := range s.currs {
if curr != nil && curr.ID == s.currentID {
searcher := s.searchers[i]
s.currs[i], err = searcher.Next()
if err != nil {
return nil, err
}
for _, i := range s.matchingIdxs {
searcher := s.searchers[i]
if s.currs[i] != rv {
ctx.DocumentMatchPool.Put(s.currs[i])
}
s.currs[i], err = searcher.Next(ctx)
if err != nil {
return nil, err
}
}
s.currentID = s.nextSmallestID()
s.updateMatches()
}
return rv, nil
}
func (s *DisjunctionSearcher) Advance(ID string) (*search.DocumentMatch, error) {
func (s *DisjunctionSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
if !s.initialized {
err := s.initSearchers()
err := s.initSearchers(ctx)
if err != nil {
return nil, err
}
@ -163,15 +192,18 @@ func (s *DisjunctionSearcher) Advance(ID string) (*search.DocumentMatch, error)
// get all searchers pointing at their first match
var err error
for i, termSearcher := range s.searchers {
s.currs[i], err = termSearcher.Advance(ID)
if s.currs[i] != nil {
ctx.DocumentMatchPool.Put(s.currs[i])
}
s.currs[i], err = termSearcher.Advance(ctx, ID)
if err != nil {
return nil, err
}
}
s.currentID = s.nextSmallestID()
s.updateMatches()
return s.Next()
return s.Next(ctx)
}
func (s *DisjunctionSearcher) Count() uint64 {
@ -194,5 +226,13 @@ func (s *DisjunctionSearcher) Close() error {
}
func (s *DisjunctionSearcher) Min() int {
return int(s.min) // FIXME just make this an int
return s.min
}
func (s *DisjunctionSearcher) DocumentMatchPoolSize() int {
rv := len(s.currs)
for _, s := range s.searchers {
rv += s.DocumentMatchPoolSize()
}
return rv
}

View file

@ -10,8 +10,6 @@
package searchers
import (
"sort"
"github.com/blevesearch/bleve/index"
"github.com/blevesearch/bleve/search"
"github.com/blevesearch/bleve/search/scorers"
@ -19,54 +17,28 @@ import (
// DocIDSearcher returns documents matching a predefined set of identifiers.
type DocIDSearcher struct {
ids []string
current int
scorer *scorers.ConstantScorer
reader index.DocIDReader
scorer *scorers.ConstantScorer
count int
}
func NewDocIDSearcher(indexReader index.IndexReader, ids []string, boost float64,
explain bool) (searcher *DocIDSearcher, err error) {
kept := make([]string, len(ids))
copy(kept, ids)
sort.Strings(kept)
if len(ids) > 0 {
var idReader index.DocIDReader
endTerm := string(incrementBytes([]byte(kept[len(kept)-1])))
idReader, err = indexReader.DocIDReader(kept[0], endTerm)
if err != nil {
return nil, err
}
defer func() {
if cerr := idReader.Close(); err == nil && cerr != nil {
err = cerr
}
}()
j := 0
for _, id := range kept {
doc, err := idReader.Advance(id)
if err != nil {
return nil, err
}
// Non-duplicate match
if doc == id && (j == 0 || kept[j-1] != id) {
kept[j] = id
j++
}
}
kept = kept[:j]
reader, err := indexReader.DocIDReaderOnly(ids)
if err != nil {
return nil, err
}
scorer := scorers.NewConstantScorer(1.0, boost, explain)
return &DocIDSearcher{
ids: kept,
scorer: scorer,
reader: reader,
count: len(ids),
}, nil
}
func (s *DocIDSearcher) Count() uint64 {
return uint64(len(s.ids))
return uint64(s.count)
}
func (s *DocIDSearcher) Weight() float64 {
@ -77,20 +49,30 @@ func (s *DocIDSearcher) SetQueryNorm(qnorm float64) {
s.scorer.SetQueryNorm(qnorm)
}
func (s *DocIDSearcher) Next() (*search.DocumentMatch, error) {
if s.current >= len(s.ids) {
func (s *DocIDSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
docidMatch, err := s.reader.Next()
if err != nil {
return nil, err
}
if docidMatch == nil {
return nil, nil
}
id := s.ids[s.current]
s.current++
docMatch := s.scorer.Score(id)
return docMatch, nil
docMatch := s.scorer.Score(ctx, docidMatch)
return docMatch, nil
}
func (s *DocIDSearcher) Advance(ID string) (*search.DocumentMatch, error) {
s.current = sort.SearchStrings(s.ids, ID)
return s.Next()
func (s *DocIDSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
docidMatch, err := s.reader.Advance(ID)
if err != nil {
return nil, err
}
if docidMatch == nil {
return nil, nil
}
docMatch := s.scorer.Score(ctx, docidMatch)
return docMatch, nil
}
func (s *DocIDSearcher) Close() error {
@ -100,3 +82,7 @@ func (s *DocIDSearcher) Close() error {
func (s *DocIDSearcher) Min() int {
return 0
}
func (s *DocIDSearcher) DocumentMatchPoolSize() int {
return 1
}

View file

@ -32,36 +32,13 @@ func NewFuzzySearcher(indexReader index.IndexReader, term string, prefix, fuzzin
}
}
// find the terms with this prefix
var fieldDict index.FieldDict
var err error
if len(prefixTerm) > 0 {
fieldDict, err = indexReader.FieldDictPrefix(field, []byte(prefixTerm))
} else {
fieldDict, err = indexReader.FieldDict(field)
}
// enumerate terms and check levenshtein distance
candidateTerms := make([]string, 0)
tfd, err := fieldDict.Next()
for err == nil && tfd != nil {
ld, exceeded := search.LevenshteinDistanceMax(&term, &tfd.Term, fuzziness)
if !exceeded && ld <= fuzziness {
candidateTerms = append(candidateTerms, tfd.Term)
}
tfd, err = fieldDict.Next()
}
if err != nil {
return nil, err
}
err = fieldDict.Close()
candidateTerms, err := findFuzzyCandidateTerms(indexReader, &term, fuzziness, field, prefixTerm)
if err != nil {
return nil, err
}
// enumerate all the terms in the range
qsearchers := make([]search.Searcher, 0, 25)
qsearchers := make([]search.Searcher, 0, len(candidateTerms))
for _, cterm := range candidateTerms {
qsearcher, err := NewTermSearcher(indexReader, cterm, field, boost, explain)
@ -87,6 +64,37 @@ func NewFuzzySearcher(indexReader index.IndexReader, term string, prefix, fuzzin
searcher: searcher,
}, nil
}
func findFuzzyCandidateTerms(indexReader index.IndexReader, term *string, fuzziness int, field, prefixTerm string) (rv []string, err error) {
rv = make([]string, 0)
var fieldDict index.FieldDict
if len(prefixTerm) > 0 {
fieldDict, err = indexReader.FieldDictPrefix(field, []byte(prefixTerm))
} else {
fieldDict, err = indexReader.FieldDict(field)
}
defer func() {
if cerr := fieldDict.Close(); cerr != nil && err == nil {
err = cerr
}
}()
// enumerate terms and check levenshtein distance
tfd, err := fieldDict.Next()
for err == nil && tfd != nil {
ld, exceeded := search.LevenshteinDistanceMax(term, &tfd.Term, fuzziness)
if !exceeded && ld <= fuzziness {
rv = append(rv, tfd.Term)
if tooManyClauses(len(rv)) {
return rv, tooManyClausesErr()
}
}
tfd, err = fieldDict.Next()
}
return rv, err
}
func (s *FuzzySearcher) Count() uint64 {
return s.searcher.Count()
}
@ -99,13 +107,13 @@ func (s *FuzzySearcher) SetQueryNorm(qnorm float64) {
s.searcher.SetQueryNorm(qnorm)
}
func (s *FuzzySearcher) Next() (*search.DocumentMatch, error) {
return s.searcher.Next()
func (s *FuzzySearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
return s.searcher.Next(ctx)
}
func (s *FuzzySearcher) Advance(ID string) (*search.DocumentMatch, error) {
return s.searcher.Next()
func (s *FuzzySearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
return s.searcher.Advance(ctx, ID)
}
func (s *FuzzySearcher) Close() error {
@ -115,3 +123,7 @@ func (s *FuzzySearcher) Close() error {
func (s *FuzzySearcher) Min() int {
return 0
}
func (s *FuzzySearcher) DocumentMatchPoolSize() int {
return s.searcher.DocumentMatchPoolSize()
}

View file

@ -19,10 +19,15 @@ type MatchAllSearcher struct {
indexReader index.IndexReader
reader index.DocIDReader
scorer *scorers.ConstantScorer
count uint64
}
func NewMatchAllSearcher(indexReader index.IndexReader, boost float64, explain bool) (*MatchAllSearcher, error) {
reader, err := indexReader.DocIDReader("", "")
reader, err := indexReader.DocIDReaderAll()
if err != nil {
return nil, err
}
count, err := indexReader.DocCount()
if err != nil {
return nil, err
}
@ -31,11 +36,12 @@ func NewMatchAllSearcher(indexReader index.IndexReader, boost float64, explain b
indexReader: indexReader,
reader: reader,
scorer: scorer,
count: count,
}, nil
}
func (s *MatchAllSearcher) Count() uint64 {
return s.indexReader.DocCount()
return s.count
}
func (s *MatchAllSearcher) Weight() float64 {
@ -46,35 +52,35 @@ func (s *MatchAllSearcher) SetQueryNorm(qnorm float64) {
s.scorer.SetQueryNorm(qnorm)
}
func (s *MatchAllSearcher) Next() (*search.DocumentMatch, error) {
func (s *MatchAllSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
id, err := s.reader.Next()
if err != nil {
return nil, err
}
if id == "" {
if id == nil {
return nil, nil
}
// score match
docMatch := s.scorer.Score(id)
docMatch := s.scorer.Score(ctx, id)
// return doc match
return docMatch, nil
}
func (s *MatchAllSearcher) Advance(ID string) (*search.DocumentMatch, error) {
func (s *MatchAllSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
id, err := s.reader.Advance(ID)
if err != nil {
return nil, err
}
if id == "" {
if id == nil {
return nil, nil
}
// score match
docMatch := s.scorer.Score(id)
docMatch := s.scorer.Score(ctx, id)
// return doc match
return docMatch, nil
@ -87,3 +93,7 @@ func (s *MatchAllSearcher) Close() error {
func (s *MatchAllSearcher) Min() int {
return 0
}
func (s *MatchAllSearcher) DocumentMatchPoolSize() int {
return 1
}

View file

@ -36,11 +36,11 @@ func (s *MatchNoneSearcher) SetQueryNorm(qnorm float64) {
}
func (s *MatchNoneSearcher) Next() (*search.DocumentMatch, error) {
func (s *MatchNoneSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
return nil, nil
}
func (s *MatchNoneSearcher) Advance(ID string) (*search.DocumentMatch, error) {
func (s *MatchNoneSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
return nil, nil
}
@ -51,3 +51,7 @@ func (s *MatchNoneSearcher) Close() error {
func (s *MatchNoneSearcher) Min() int {
return 0
}
func (s *MatchNoneSearcher) DocumentMatchPoolSize() int {
return 0
}

View file

@ -57,6 +57,9 @@ func NewNumericRangeSearcher(indexReader index.IndexReader, min *float64, max *f
// FIXME hard-coded precision, should match field declaration
termRanges := splitInt64Range(minInt64, maxInt64, 4)
terms := termRanges.Enumerate()
if tooManyClauses(len(terms)) {
return nil, tooManyClausesErr()
}
// enumerate all the terms in the range
qsearchers := make([]search.Searcher, len(terms))
for i, term := range terms {
@ -93,12 +96,12 @@ func (s *NumericRangeSearcher) SetQueryNorm(qnorm float64) {
s.searcher.SetQueryNorm(qnorm)
}
func (s *NumericRangeSearcher) Next() (*search.DocumentMatch, error) {
return s.searcher.Next()
func (s *NumericRangeSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
return s.searcher.Next(ctx)
}
func (s *NumericRangeSearcher) Advance(ID string) (*search.DocumentMatch, error) {
return s.searcher.Advance(ID)
func (s *NumericRangeSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
return s.searcher.Advance(ctx, ID)
}
func (s *NumericRangeSearcher) Close() error {
@ -212,3 +215,7 @@ func newRangeBytes(minBytes, maxBytes []byte) *termRange {
func (s *NumericRangeSearcher) Min() int {
return 0
}
func (s *NumericRangeSearcher) DocumentMatchPoolSize() int {
return s.searcher.DocumentMatchPoolSize()
}

View file

@ -52,11 +52,11 @@ func (s *PhraseSearcher) computeQueryNorm() {
}
}
func (s *PhraseSearcher) initSearchers() error {
func (s *PhraseSearcher) initSearchers(ctx *search.SearchContext) error {
var err error
// get all searchers pointing at their first match
if s.mustSearcher != nil {
s.currMust, err = s.mustSearcher.Next()
s.currMust, err = s.mustSearcher.Next(ctx)
if err != nil {
return err
}
@ -66,11 +66,11 @@ func (s *PhraseSearcher) initSearchers() error {
return nil
}
func (s *PhraseSearcher) advanceNextMust() error {
func (s *PhraseSearcher) advanceNextMust(ctx *search.SearchContext) error {
var err error
if s.mustSearcher != nil {
s.currMust, err = s.mustSearcher.Next()
s.currMust, err = s.mustSearcher.Next(ctx)
if err != nil {
return err
}
@ -90,9 +90,9 @@ func (s *PhraseSearcher) SetQueryNorm(qnorm float64) {
s.mustSearcher.SetQueryNorm(qnorm)
}
func (s *PhraseSearcher) Next() (*search.DocumentMatch, error) {
func (s *PhraseSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
if !s.initialized {
err := s.initSearchers()
err := s.initSearchers(ctx)
if err != nil {
return nil, err
}
@ -144,14 +144,14 @@ func (s *PhraseSearcher) Next() (*search.DocumentMatch, error) {
// return match
rv = s.currMust
rv.Locations = rvftlm
err := s.advanceNextMust()
err := s.advanceNextMust(ctx)
if err != nil {
return nil, err
}
return rv, nil
}
err := s.advanceNextMust()
err := s.advanceNextMust(ctx)
if err != nil {
return nil, err
}
@ -160,19 +160,19 @@ func (s *PhraseSearcher) Next() (*search.DocumentMatch, error) {
return nil, nil
}
func (s *PhraseSearcher) Advance(ID string) (*search.DocumentMatch, error) {
func (s *PhraseSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
if !s.initialized {
err := s.initSearchers()
err := s.initSearchers(ctx)
if err != nil {
return nil, err
}
}
var err error
s.currMust, err = s.mustSearcher.Advance(ID)
s.currMust, err = s.mustSearcher.Advance(ctx, ID)
if err != nil {
return nil, err
}
return s.Next()
return s.Next(ctx)
}
func (s *PhraseSearcher) Count() uint64 {
@ -195,3 +195,7 @@ func (s *PhraseSearcher) Close() error {
func (s *PhraseSearcher) Min() int {
return 0
}
func (s *PhraseSearcher) DocumentMatchPoolSize() int {
return s.mustSearcher.DocumentMatchPoolSize() + 1
}

View file

@ -27,39 +27,20 @@ type RegexpSearcher struct {
func NewRegexpSearcher(indexReader index.IndexReader, pattern *regexp.Regexp, field string, boost float64, explain bool) (*RegexpSearcher, error) {
prefixTerm, complete := pattern.LiteralPrefix()
candidateTerms := make([]string, 0)
var candidateTerms []string
if complete {
// there is no pattern
candidateTerms = append(candidateTerms, prefixTerm)
candidateTerms = []string{prefixTerm}
} else {
var fieldDict index.FieldDict
var err error
if len(prefixTerm) > 0 {
fieldDict, err = indexReader.FieldDictPrefix(field, []byte(prefixTerm))
} else {
fieldDict, err = indexReader.FieldDict(field)
}
// enumerate the terms and check against regexp
tfd, err := fieldDict.Next()
for err == nil && tfd != nil {
if pattern.MatchString(tfd.Term) {
candidateTerms = append(candidateTerms, tfd.Term)
}
tfd, err = fieldDict.Next()
}
if err != nil {
return nil, err
}
err = fieldDict.Close()
candidateTerms, err = findRegexpCandidateTerms(indexReader, pattern, field, prefixTerm)
if err != nil {
return nil, err
}
}
// enumerate all the terms in the range
qsearchers := make([]search.Searcher, 0, 25)
qsearchers := make([]search.Searcher, 0, len(candidateTerms))
for _, cterm := range candidateTerms {
qsearcher, err := NewTermSearcher(indexReader, cterm, field, boost, explain)
@ -83,6 +64,36 @@ func NewRegexpSearcher(indexReader index.IndexReader, pattern *regexp.Regexp, fi
searcher: searcher,
}, nil
}
func findRegexpCandidateTerms(indexReader index.IndexReader, pattern *regexp.Regexp, field, prefixTerm string) (rv []string, err error) {
rv = make([]string, 0)
var fieldDict index.FieldDict
if len(prefixTerm) > 0 {
fieldDict, err = indexReader.FieldDictPrefix(field, []byte(prefixTerm))
} else {
fieldDict, err = indexReader.FieldDict(field)
}
defer func() {
if cerr := fieldDict.Close(); cerr != nil && err == nil {
err = cerr
}
}()
// enumerate the terms and check against regexp
tfd, err := fieldDict.Next()
for err == nil && tfd != nil {
if pattern.MatchString(tfd.Term) {
rv = append(rv, tfd.Term)
if tooManyClauses(len(rv)) {
return rv, tooManyClausesErr()
}
}
tfd, err = fieldDict.Next()
}
return rv, err
}
func (s *RegexpSearcher) Count() uint64 {
return s.searcher.Count()
}
@ -95,13 +106,13 @@ func (s *RegexpSearcher) SetQueryNorm(qnorm float64) {
s.searcher.SetQueryNorm(qnorm)
}
func (s *RegexpSearcher) Next() (*search.DocumentMatch, error) {
return s.searcher.Next()
func (s *RegexpSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
return s.searcher.Next(ctx)
}
func (s *RegexpSearcher) Advance(ID string) (*search.DocumentMatch, error) {
return s.searcher.Next()
func (s *RegexpSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
return s.searcher.Advance(ctx, ID)
}
func (s *RegexpSearcher) Close() error {
@ -111,3 +122,7 @@ func (s *RegexpSearcher) Close() error {
func (s *RegexpSearcher) Min() int {
return 0
}
func (s *RegexpSearcher) DocumentMatchPoolSize() int {
return s.searcher.DocumentMatchPoolSize()
}

View file

@ -19,17 +19,22 @@ type TermSearcher struct {
indexReader index.IndexReader
term string
field string
explain bool
reader index.TermFieldReader
scorer *scorers.TermQueryScorer
tfd index.TermFieldDoc
explain bool
}
func NewTermSearcher(indexReader index.IndexReader, term string, field string, boost float64, explain bool) (*TermSearcher, error) {
reader, err := indexReader.TermFieldReader([]byte(term), field)
reader, err := indexReader.TermFieldReader([]byte(term), field, true, true, true)
if err != nil {
return nil, err
}
scorer := scorers.NewTermQueryScorer(term, field, boost, indexReader.DocCount(), reader.Count(), explain)
count, err := indexReader.DocCount()
if err != nil {
return nil, err
}
scorer := scorers.NewTermQueryScorer(term, field, boost, count, reader.Count(), explain)
return &TermSearcher{
indexReader: indexReader,
term: term,
@ -52,8 +57,8 @@ func (s *TermSearcher) SetQueryNorm(qnorm float64) {
s.scorer.SetQueryNorm(qnorm)
}
func (s *TermSearcher) Next() (*search.DocumentMatch, error) {
termMatch, err := s.reader.Next()
func (s *TermSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
termMatch, err := s.reader.Next(s.tfd.Reset())
if err != nil {
return nil, err
}
@ -63,14 +68,14 @@ func (s *TermSearcher) Next() (*search.DocumentMatch, error) {
}
// score match
docMatch := s.scorer.Score(termMatch)
docMatch := s.scorer.Score(ctx, termMatch)
// return doc match
return docMatch, nil
}
func (s *TermSearcher) Advance(ID string) (*search.DocumentMatch, error) {
termMatch, err := s.reader.Advance(ID)
func (s *TermSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
termMatch, err := s.reader.Advance(ID, s.tfd.Reset())
if err != nil {
return nil, err
}
@ -80,7 +85,7 @@ func (s *TermSearcher) Advance(ID string) (*search.DocumentMatch, error) {
}
// score match
docMatch := s.scorer.Score(termMatch)
docMatch := s.scorer.Score(ctx, termMatch)
// return doc match
return docMatch, nil
@ -93,3 +98,7 @@ func (s *TermSearcher) Close() error {
func (s *TermSearcher) Min() int {
return 0
}
func (s *TermSearcher) DocumentMatchPoolSize() int {
return 1
}

View file

@ -70,13 +70,13 @@ func (s *TermPrefixSearcher) SetQueryNorm(qnorm float64) {
s.searcher.SetQueryNorm(qnorm)
}
func (s *TermPrefixSearcher) Next() (*search.DocumentMatch, error) {
return s.searcher.Next()
func (s *TermPrefixSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) {
return s.searcher.Next(ctx)
}
func (s *TermPrefixSearcher) Advance(ID string) (*search.DocumentMatch, error) {
return s.searcher.Next()
func (s *TermPrefixSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) {
return s.searcher.Advance(ctx, ID)
}
func (s *TermPrefixSearcher) Close() error {
@ -86,3 +86,7 @@ func (s *TermPrefixSearcher) Close() error {
func (s *TermPrefixSearcher) Min() int {
return 0
}
func (s *TermPrefixSearcher) DocumentMatchPoolSize() int {
return s.searcher.DocumentMatchPoolSize()
}