diff options
Diffstat (limited to 'vendor/github.com/blevesearch/bleve/search')
61 files changed, 2099 insertions, 772 deletions
diff --git a/vendor/github.com/blevesearch/bleve/search/collector/heap.go b/vendor/github.com/blevesearch/bleve/search/collector/heap.go index 19f3a05982..bdf72eade3 100644 --- a/vendor/github.com/blevesearch/bleve/search/collector/heap.go +++ b/vendor/github.com/blevesearch/bleve/search/collector/heap.go @@ -34,11 +34,20 @@ func newStoreHeap(cap int, compare collectorCompare) *collectStoreHeap { return rv } -func (c *collectStoreHeap) Add(doc *search.DocumentMatch) { +func (c *collectStoreHeap) AddNotExceedingSize(doc *search.DocumentMatch, + size int) *search.DocumentMatch { + c.add(doc) + if c.Len() > size { + return c.removeLast() + } + return nil +} + +func (c *collectStoreHeap) add(doc *search.DocumentMatch) { heap.Push(c, doc) } -func (c *collectStoreHeap) RemoveLast() *search.DocumentMatch { +func (c *collectStoreHeap) removeLast() *search.DocumentMatch { return heap.Pop(c).(*search.DocumentMatch) } @@ -49,17 +58,12 @@ func (c *collectStoreHeap) Final(skip int, fixup collectorFixup) (search.Documen return make(search.DocumentMatchCollection, 0), nil } rv := make(search.DocumentMatchCollection, size) - for count > 0 { - count-- - - if count >= skip { - size-- - doc := heap.Pop(c).(*search.DocumentMatch) - rv[size] = doc - err := fixup(doc) - if err != nil { - return nil, err - } + for i := size - 1; i >= 0; i-- { + doc := heap.Pop(c).(*search.DocumentMatch) + rv[i] = doc + err := fixup(doc) + if err != nil { + return nil, err } } return rv, nil diff --git a/vendor/github.com/blevesearch/bleve/search/collector/list.go b/vendor/github.com/blevesearch/bleve/search/collector/list.go index 51b47b12da..ec2f69cb82 100644 --- a/vendor/github.com/blevesearch/bleve/search/collector/list.go +++ b/vendor/github.com/blevesearch/bleve/search/collector/list.go @@ -34,7 +34,16 @@ func newStoreList(cap int, compare collectorCompare) *collectStoreList { return rv } -func (c *collectStoreList) Add(doc *search.DocumentMatch) { +func (c *collectStoreList) AddNotExceedingSize(doc *search.DocumentMatch, + size int) *search.DocumentMatch { + c.add(doc) + if c.len() > size { + return c.removeLast() + } + return nil +} + +func (c *collectStoreList) add(doc *search.DocumentMatch) { for e := c.results.Front(); e != nil; e = e.Next() { curr := e.Value.(*search.DocumentMatch) if c.compare(doc, curr) >= 0 { @@ -46,7 +55,7 @@ func (c *collectStoreList) Add(doc *search.DocumentMatch) { c.results.PushBack(doc) } -func (c *collectStoreList) RemoveLast() *search.DocumentMatch { +func (c *collectStoreList) removeLast() *search.DocumentMatch { return c.results.Remove(c.results.Front()).(*search.DocumentMatch) } @@ -73,6 +82,6 @@ func (c *collectStoreList) Final(skip int, fixup collectorFixup) (search.Documen return search.DocumentMatchCollection{}, nil } -func (c *collectStoreList) Len() int { +func (c *collectStoreList) len() int { return c.results.Len() } diff --git a/vendor/github.com/blevesearch/bleve/search/collector/slice.go b/vendor/github.com/blevesearch/bleve/search/collector/slice.go index b061643ee4..32cb862447 100644 --- a/vendor/github.com/blevesearch/bleve/search/collector/slice.go +++ b/vendor/github.com/blevesearch/bleve/search/collector/slice.go @@ -29,7 +29,16 @@ func newStoreSlice(cap int, compare collectorCompare) *collectStoreSlice { return rv } -func (c *collectStoreSlice) Add(doc *search.DocumentMatch) { +func (c *collectStoreSlice) AddNotExceedingSize(doc *search.DocumentMatch, + size int) *search.DocumentMatch { + c.add(doc) + if c.len() > size { + return c.removeLast() + } + return nil +} + +func (c *collectStoreSlice) add(doc *search.DocumentMatch) { // find where to insert, starting at end (lowest) i := len(c.slice) for ; i > 0; i-- { @@ -44,7 +53,7 @@ func (c *collectStoreSlice) Add(doc *search.DocumentMatch) { c.slice[i] = doc } -func (c *collectStoreSlice) RemoveLast() *search.DocumentMatch { +func (c *collectStoreSlice) removeLast() *search.DocumentMatch { var rv *search.DocumentMatch rv, c.slice = c.slice[len(c.slice)-1], c.slice[:len(c.slice)-1] return rv @@ -63,6 +72,6 @@ func (c *collectStoreSlice) Final(skip int, fixup collectorFixup) (search.Docume return search.DocumentMatchCollection{}, nil } -func (c *collectStoreSlice) Len() int { +func (c *collectStoreSlice) len() int { return len(c.slice) } diff --git a/vendor/github.com/blevesearch/bleve/search/collector/topn.go b/vendor/github.com/blevesearch/bleve/search/collector/topn.go index 152d92f732..2c7c6752df 100644 --- a/vendor/github.com/blevesearch/bleve/search/collector/topn.go +++ b/vendor/github.com/blevesearch/bleve/search/collector/topn.go @@ -22,6 +22,15 @@ import ( "golang.org/x/net/context" ) +type collectorStore interface { + // Add the document, and if the new store size exceeds the provided size + // the last element is removed and returned. If the size has not been + // exceeded, nil is returned. + AddNotExceedingSize(doc *search.DocumentMatch, size int) *search.DocumentMatch + + Final(skip int, fixup collectorFixup) (search.DocumentMatchCollection, error) +} + // PreAllocSizeSkipCap will cap preallocation to this amount when // size+skip exceeds this value var PreAllocSizeSkipCap = 1000 @@ -41,7 +50,7 @@ type TopNCollector struct { results search.DocumentMatchCollection facetsBuilder *search.FacetsBuilder - store *collectStoreSlice + store collectorStore needDocIds bool neededFields []string @@ -68,9 +77,15 @@ func NewTopNCollector(size int, skip int, sort search.SortOrder) *TopNCollector backingSize = PreAllocSizeSkipCap + 1 } - hc.store = newStoreSlice(backingSize, func(i, j *search.DocumentMatch) int { - return hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, i, j) - }) + if size+skip > 10 { + hc.store = newStoreHeap(backingSize, func(i, j *search.DocumentMatch) int { + return hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, i, j) + }) + } else { + hc.store = newStoreSlice(backingSize, func(i, j *search.DocumentMatch) int { + return hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, i, j) + }) + } // these lookups traverse an interface, so do once up-front if sort.RequiresDocID() { @@ -114,12 +129,6 @@ func (hc *TopNCollector) Collect(ctx context.Context, searcher search.Searcher, default: } } - if hc.facetsBuilder != nil { - err = hc.facetsBuilder.Update(next) - if err != nil { - break - } - } err = hc.collectSingle(searchContext, reader, next) if err != nil { @@ -144,6 +153,16 @@ func (hc *TopNCollector) Collect(ctx context.Context, searcher search.Searcher, var sortByScoreOpt = []string{"_score"} func (hc *TopNCollector) collectSingle(ctx *search.SearchContext, reader index.IndexReader, d *search.DocumentMatch) error { + var err error + + // visit field terms for features that require it (sort, facets) + if len(hc.neededFields) > 0 { + err = hc.visitFieldTerms(reader, d) + if err != nil { + return err + } + } + // increment total hits hc.total++ d.HitNumber = hc.total @@ -153,7 +172,6 @@ func (hc *TopNCollector) collectSingle(ctx *search.SearchContext, reader index.I hc.maxScore = d.Score } - var err error // see if we need to load ID (at this early stage, for example to sort on it) if hc.needDocIds { d.ID, err = reader.ExternalID(d.IndexInternalID) @@ -162,22 +180,6 @@ func (hc *TopNCollector) collectSingle(ctx *search.SearchContext, reader index.I } } - // see if we need to load the stored fields - if len(hc.neededFields) > 0 { - // find out which fields haven't been loaded yet - fieldsToLoad := d.CachedFieldTerms.FieldsNotYetCached(hc.neededFields) - // look them up - fieldTerms, err := reader.DocumentFieldTerms(d.IndexInternalID, fieldsToLoad) - if err != nil { - return err - } - // cache these as well - if d.CachedFieldTerms == nil { - d.CachedFieldTerms = make(map[string][]string) - } - d.CachedFieldTerms.Merge(fieldTerms) - } - // compute this hits sort value if len(hc.sort) == 1 && hc.cachedScoring[0] { d.Sort = sortByScoreOpt @@ -197,9 +199,8 @@ func (hc *TopNCollector) collectSingle(ctx *search.SearchContext, reader index.I } } - hc.store.Add(d) - if hc.store.Len() > hc.size+hc.skip { - removed := hc.store.RemoveLast() + removed := hc.store.AddNotExceedingSize(d, hc.size+hc.skip) + if removed != nil { if hc.lowestMatchOutsideResults == nil { hc.lowestMatchOutsideResults = removed } else { @@ -215,9 +216,31 @@ func (hc *TopNCollector) collectSingle(ctx *search.SearchContext, reader index.I return nil } +// visitFieldTerms is responsible for visiting the field terms of the +// search hit, and passing visited terms to the sort and facet builder +func (hc *TopNCollector) visitFieldTerms(reader index.IndexReader, d *search.DocumentMatch) error { + if hc.facetsBuilder != nil { + hc.facetsBuilder.StartDoc() + } + + err := reader.DocumentVisitFieldTerms(d.IndexInternalID, hc.neededFields, func(field string, term []byte) { + if hc.facetsBuilder != nil { + hc.facetsBuilder.UpdateVisitor(field, term) + } + hc.sort.UpdateVisitor(field, term) + }) + + if hc.facetsBuilder != nil { + hc.facetsBuilder.EndDoc() + } + + return err +} + // SetFacetsBuilder registers a facet builder for this collector func (hc *TopNCollector) SetFacetsBuilder(facetsBuilder *search.FacetsBuilder) { hc.facetsBuilder = facetsBuilder + hc.neededFields = append(hc.neededFields, hc.facetsBuilder.RequiredFields()...) } // finalizeResults starts with the heap containing the final top size+skip diff --git a/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_datetime.go b/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_datetime.go index c3184ac6e3..8657a553a9 100644 --- a/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_datetime.go +++ b/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_datetime.go @@ -18,7 +18,6 @@ import ( "sort" "time" - "github.com/blevesearch/bleve/index" "github.com/blevesearch/bleve/numeric" "github.com/blevesearch/bleve/search" ) @@ -35,6 +34,7 @@ type DateTimeFacetBuilder struct { total int missing int ranges map[string]*dateTimeRange + sawValue bool } func NewDateTimeFacetBuilder(field string, size int) *DateTimeFacetBuilder { @@ -58,36 +58,35 @@ func (fb *DateTimeFacetBuilder) Field() string { return fb.field } -func (fb *DateTimeFacetBuilder) Update(ft index.FieldTerms) { - terms, ok := ft[fb.field] - if ok { - for _, term := range terms { - // only consider the values which are shifted 0 - prefixCoded := numeric.PrefixCoded(term) - shift, err := prefixCoded.Shift() - if err == nil && shift == 0 { - i64, err := prefixCoded.Int64() - if err == nil { - t := time.Unix(0, i64) - - // look at each of the ranges for a match - for rangeName, r := range fb.ranges { - - if (r.start.IsZero() || t.After(r.start) || t.Equal(r.start)) && (r.end.IsZero() || t.Before(r.end)) { - - existingCount, existed := fb.termsCount[rangeName] - if existed { - fb.termsCount[rangeName] = existingCount + 1 - } else { - fb.termsCount[rangeName] = 1 - } - fb.total++ - } +func (fb *DateTimeFacetBuilder) UpdateVisitor(field string, term []byte) { + if field == fb.field { + fb.sawValue = true + // only consider the values which are shifted 0 + prefixCoded := numeric.PrefixCoded(term) + shift, err := prefixCoded.Shift() + if err == nil && shift == 0 { + i64, err := prefixCoded.Int64() + if err == nil { + t := time.Unix(0, i64) + + // look at each of the ranges for a match + for rangeName, r := range fb.ranges { + if (r.start.IsZero() || t.After(r.start) || t.Equal(r.start)) && (r.end.IsZero() || t.Before(r.end)) { + fb.termsCount[rangeName] = fb.termsCount[rangeName] + 1 + fb.total++ } } } } - } else { + } +} + +func (fb *DateTimeFacetBuilder) StartDoc() { + fb.sawValue = false +} + +func (fb *DateTimeFacetBuilder) EndDoc() { + if !fb.sawValue { fb.missing++ } } diff --git a/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_numeric.go b/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_numeric.go index 19beb3c2d9..2ab5f27893 100644 --- a/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_numeric.go +++ b/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_numeric.go @@ -17,7 +17,6 @@ package facet import ( "sort" - "github.com/blevesearch/bleve/index" "github.com/blevesearch/bleve/numeric" "github.com/blevesearch/bleve/search" ) @@ -34,6 +33,7 @@ type NumericFacetBuilder struct { total int missing int ranges map[string]*numericRange + sawValue bool } func NewNumericFacetBuilder(field string, size int) *NumericFacetBuilder { @@ -57,36 +57,35 @@ func (fb *NumericFacetBuilder) Field() string { return fb.field } -func (fb *NumericFacetBuilder) Update(ft index.FieldTerms) { - terms, ok := ft[fb.field] - if ok { - for _, term := range terms { - // only consider the values which are shifted 0 - prefixCoded := numeric.PrefixCoded(term) - shift, err := prefixCoded.Shift() - if err == nil && shift == 0 { - i64, err := prefixCoded.Int64() - if err == nil { - f64 := numeric.Int64ToFloat64(i64) - - // look at each of the ranges for a match - for rangeName, r := range fb.ranges { - - if (r.min == nil || f64 >= *r.min) && (r.max == nil || f64 < *r.max) { - - existingCount, existed := fb.termsCount[rangeName] - if existed { - fb.termsCount[rangeName] = existingCount + 1 - } else { - fb.termsCount[rangeName] = 1 - } - fb.total++ - } +func (fb *NumericFacetBuilder) UpdateVisitor(field string, term []byte) { + if field == fb.field { + fb.sawValue = true + // only consider the values which are shifted 0 + prefixCoded := numeric.PrefixCoded(term) + shift, err := prefixCoded.Shift() + if err == nil && shift == 0 { + i64, err := prefixCoded.Int64() + if err == nil { + f64 := numeric.Int64ToFloat64(i64) + + // look at each of the ranges for a match + for rangeName, r := range fb.ranges { + if (r.min == nil || f64 >= *r.min) && (r.max == nil || f64 < *r.max) { + fb.termsCount[rangeName] = fb.termsCount[rangeName] + 1 + fb.total++ } } } } - } else { + } +} + +func (fb *NumericFacetBuilder) StartDoc() { + fb.sawValue = false +} + +func (fb *NumericFacetBuilder) EndDoc() { + if !fb.sawValue { fb.missing++ } } diff --git a/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_terms.go b/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_terms.go index 1664af24bc..a41e475a91 100644 --- a/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_terms.go +++ b/vendor/github.com/blevesearch/bleve/search/facet/facet_builder_terms.go @@ -17,7 +17,6 @@ package facet import ( "sort" - "github.com/blevesearch/bleve/index" "github.com/blevesearch/bleve/search" ) @@ -27,6 +26,7 @@ type TermsFacetBuilder struct { termsCount map[string]int total int missing int + sawValue bool } func NewTermsFacetBuilder(field string, size int) *TermsFacetBuilder { @@ -41,19 +41,20 @@ func (fb *TermsFacetBuilder) Field() string { return fb.field } -func (fb *TermsFacetBuilder) Update(ft index.FieldTerms) { - terms, ok := ft[fb.field] - if ok { - for _, term := range terms { - existingCount, existed := fb.termsCount[term] - if existed { - fb.termsCount[term] = existingCount + 1 - } else { - fb.termsCount[term] = 1 - } - fb.total++ - } - } else { +func (fb *TermsFacetBuilder) UpdateVisitor(field string, term []byte) { + if field == fb.field { + fb.sawValue = true + fb.termsCount[string(term)] = fb.termsCount[string(term)] + 1 + fb.total++ + } +} + +func (fb *TermsFacetBuilder) StartDoc() { + fb.sawValue = false +} + +func (fb *TermsFacetBuilder) EndDoc() { + if !fb.sawValue { fb.missing++ } } diff --git a/vendor/github.com/blevesearch/bleve/search/facets_builder.go b/vendor/github.com/blevesearch/bleve/search/facets_builder.go index 2cb6e5c897..05e270413a 100644 --- a/vendor/github.com/blevesearch/bleve/search/facets_builder.go +++ b/vendor/github.com/blevesearch/bleve/search/facets_builder.go @@ -21,7 +21,10 @@ import ( ) type FacetBuilder interface { - Update(index.FieldTerms) + StartDoc() + UpdateVisitor(field string, term []byte) + EndDoc() + Result() *FacetResult Field() string } @@ -41,33 +44,29 @@ func NewFacetsBuilder(indexReader index.IndexReader) *FacetsBuilder { func (fb *FacetsBuilder) Add(name string, facetBuilder FacetBuilder) { fb.facets[name] = facetBuilder + fb.fields = append(fb.fields, facetBuilder.Field()) } -func (fb *FacetsBuilder) Update(docMatch *DocumentMatch) error { - if fb.fields == nil { - for _, facetBuilder := range fb.facets { - fb.fields = append(fb.fields, facetBuilder.Field()) - } +func (fb *FacetsBuilder) RequiredFields() []string { + return fb.fields +} + +func (fb *FacetsBuilder) StartDoc() { + for _, facetBuilder := range fb.facets { + facetBuilder.StartDoc() } +} - if len(fb.fields) > 0 { - // find out which fields haven't been loaded yet - fieldsToLoad := docMatch.CachedFieldTerms.FieldsNotYetCached(fb.fields) - // look them up - fieldTerms, err := fb.indexReader.DocumentFieldTerms(docMatch.IndexInternalID, fieldsToLoad) - if err != nil { - return err - } - // cache these as well - if docMatch.CachedFieldTerms == nil { - docMatch.CachedFieldTerms = make(map[string][]string) - } - docMatch.CachedFieldTerms.Merge(fieldTerms) +func (fb *FacetsBuilder) EndDoc() { + for _, facetBuilder := range fb.facets { + facetBuilder.EndDoc() } +} + +func (fb *FacetsBuilder) UpdateVisitor(field string, term []byte) { for _, facetBuilder := range fb.facets { - facetBuilder.Update(docMatch.CachedFieldTerms) + facetBuilder.UpdateVisitor(field, term) } - return nil } type TermFacet struct { diff --git a/vendor/github.com/blevesearch/bleve/search/highlight/format/html/html.go b/vendor/github.com/blevesearch/bleve/search/highlight/format/html/html.go index 2ff4e9ba60..8154e790b0 100644 --- a/vendor/github.com/blevesearch/bleve/search/highlight/format/html/html.go +++ b/vendor/github.com/blevesearch/bleve/search/highlight/format/html/html.go @@ -44,7 +44,7 @@ func (a *FragmentFormatter) Format(f *highlight.Fragment, orderedTermLocations h continue } // make sure the array positions match - if !highlight.SameArrayPositions(f.ArrayPositions, termLocation.ArrayPositions) { + if !termLocation.ArrayPositions.Equals(f.ArrayPositions) { continue } if termLocation.Start < curr { diff --git a/vendor/github.com/blevesearch/bleve/search/highlight/highlighter/simple/fragment_scorer_simple.go b/vendor/github.com/blevesearch/bleve/search/highlight/highlighter/simple/fragment_scorer_simple.go index 2a6ce68f48..3ec4c3d28e 100644 --- a/vendor/github.com/blevesearch/bleve/search/highlight/highlighter/simple/fragment_scorer_simple.go +++ b/vendor/github.com/blevesearch/bleve/search/highlight/highlighter/simple/fragment_scorer_simple.go @@ -37,7 +37,7 @@ func (s *FragmentScorer) Score(f *highlight.Fragment) { OUTER: for _, locations := range s.tlm { for _, location := range locations { - if highlight.SameArrayPositions(f.ArrayPositions, location.ArrayPositions) && int(location.Start) >= f.Start && int(location.End) <= f.End { + if location.ArrayPositions.Equals(f.ArrayPositions) && int(location.Start) >= f.Start && int(location.End) <= f.End { score += 1.0 // once we find a term in the fragment // don't care about additional matches diff --git a/vendor/github.com/blevesearch/bleve/search/highlight/highlighter/simple/highlighter_simple.go b/vendor/github.com/blevesearch/bleve/search/highlight/highlighter/simple/highlighter_simple.go index 3815c8e12d..4849516bad 100644 --- a/vendor/github.com/blevesearch/bleve/search/highlight/highlighter/simple/highlighter_simple.go +++ b/vendor/github.com/blevesearch/bleve/search/highlight/highlighter/simple/highlighter_simple.go @@ -87,7 +87,7 @@ func (s *Highlighter) BestFragmentsInField(dm *search.DocumentMatch, doc *docume if ok { termLocationsSameArrayPosition := make(highlight.TermLocations, 0) for _, otl := range orderedTermLocations { - if highlight.SameArrayPositions(f.ArrayPositions(), otl.ArrayPositions) { + if otl.ArrayPositions.Equals(f.ArrayPositions()) { termLocationsSameArrayPosition = append(termLocationsSameArrayPosition, otl) } } diff --git a/vendor/github.com/blevesearch/bleve/search/highlight/term_locations.go b/vendor/github.com/blevesearch/bleve/search/highlight/term_locations.go index c53c2dba38..6d2cb133ee 100644 --- a/vendor/github.com/blevesearch/bleve/search/highlight/term_locations.go +++ b/vendor/github.com/blevesearch/bleve/search/highlight/term_locations.go @@ -23,7 +23,7 @@ import ( type TermLocation struct { Term string - ArrayPositions []float64 + ArrayPositions search.ArrayPositions Pos int Start int End int @@ -103,15 +103,3 @@ func OrderTermLocations(tlm search.TermLocationMap) TermLocations { sort.Sort(rv) return rv } - -func SameArrayPositions(fieldArrayPositions []uint64, termLocationArrayPositions []float64) bool { - if len(fieldArrayPositions) != len(termLocationArrayPositions) { - return false - } - for i := 0; i < len(fieldArrayPositions); i++ { - if fieldArrayPositions[i] != uint64(termLocationArrayPositions[i]) { - return false - } - } - return true -} diff --git a/vendor/github.com/blevesearch/bleve/search/pool.go b/vendor/github.com/blevesearch/bleve/search/pool.go index 043e53f82f..b9b52a613f 100644 --- a/vendor/github.com/blevesearch/bleve/search/pool.go +++ b/vendor/github.com/blevesearch/bleve/search/pool.go @@ -37,13 +37,17 @@ func defaultDocumentMatchPoolTooSmall(p *DocumentMatchPool) *DocumentMatch { // pre-allocated to accommodate the requested number of DocumentMatch // instances func NewDocumentMatchPool(size, sortsize int) *DocumentMatchPool { - avail := make(DocumentMatchCollection, 0, size) + avail := make(DocumentMatchCollection, size) // pre-allocate the expected number of instances startBlock := make([]DocumentMatch, size) + startSorts := make([]string, size*sortsize) // make these initial instances available - for i := range startBlock { - startBlock[i].Sort = make([]string, 0, sortsize) - avail = append(avail, &startBlock[i]) + i, j := 0, 0 + for i < size { + avail[i] = &startBlock[i] + avail[i].Sort = startSorts[j:j] + i += 1 + j += sortsize } return &DocumentMatchPool{ avail: avail, diff --git a/vendor/github.com/blevesearch/bleve/search/query/bool_field.go b/vendor/github.com/blevesearch/bleve/search/query/bool_field.go index 402724ce01..b7b5a3d363 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/bool_field.go +++ b/vendor/github.com/blevesearch/bleve/search/query/bool_field.go @@ -22,7 +22,7 @@ import ( ) type BoolFieldQuery struct { - Bool bool `json:"bool"` + Bool bool `json:"bool"` FieldVal string `json:"field,omitempty"` BoostVal *Boost `json:"boost,omitempty"` } @@ -39,20 +39,19 @@ func (q *BoolFieldQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *BoolFieldQuery) Boost() float64{ - return q.BoostVal.Value() +func (q *BoolFieldQuery) Boost() float64 { + return q.BoostVal.Value() } func (q *BoolFieldQuery) SetField(f string) { q.FieldVal = f } -func (q *BoolFieldQuery) Field() string{ +func (q *BoolFieldQuery) Field() string { return q.FieldVal } - -func (q *BoolFieldQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *BoolFieldQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -61,5 +60,5 @@ func (q *BoolFieldQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, e if q.Bool { term = "T" } - return searcher.NewTermSearcher(i, term, field, q.BoostVal.Value(), explain) + return searcher.NewTermSearcher(i, term, field, q.BoostVal.Value(), options) } diff --git a/vendor/github.com/blevesearch/bleve/search/query/boolean.go b/vendor/github.com/blevesearch/bleve/search/query/boolean.go index 2fe75ff994..3cfa1d99f3 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/boolean.go +++ b/vendor/github.com/blevesearch/bleve/search/query/boolean.go @@ -25,10 +25,11 @@ import ( ) type BooleanQuery struct { - Must Query `json:"must,omitempty"` - Should Query `json:"should,omitempty"` - MustNot Query `json:"must_not,omitempty"` - BoostVal *Boost `json:"boost,omitempty"` + Must Query `json:"must,omitempty"` + Should Query `json:"should,omitempty"` + MustNot Query `json:"must_not,omitempty"` + BoostVal *Boost `json:"boost,omitempty"` + queryStringMode bool } // NewBooleanQuery creates a compound Query composed @@ -55,6 +56,15 @@ func NewBooleanQuery(must []Query, should []Query, mustNot []Query) *BooleanQuer return &rv } +func NewBooleanQueryForQueryString(must []Query, should []Query, mustNot []Query) *BooleanQuery { + rv := NewBooleanQuery(nil, nil, nil) + rv.queryStringMode = true + rv.AddMust(must...) + rv.AddShould(should...) + rv.AddMustNot(mustNot...) + return rv +} + // SetMinShould requires that at least minShould of the // should Queries must be satisfied. func (q *BooleanQuery) SetMinShould(minShould float64) { @@ -63,7 +73,9 @@ func (q *BooleanQuery) SetMinShould(minShould float64) { func (q *BooleanQuery) AddMust(m ...Query) { if q.Must == nil { - q.Must = NewConjunctionQuery([]Query{}) + tmp := NewConjunctionQuery([]Query{}) + tmp.queryStringMode = q.queryStringMode + q.Must = tmp } for _, mq := range m { q.Must.(*ConjunctionQuery).AddQuery(mq) @@ -72,7 +84,9 @@ func (q *BooleanQuery) AddMust(m ...Query) { func (q *BooleanQuery) AddShould(m ...Query) { if q.Should == nil { - q.Should = NewDisjunctionQuery([]Query{}) + tmp := NewDisjunctionQuery([]Query{}) + tmp.queryStringMode = q.queryStringMode + q.Should = tmp } for _, mq := range m { q.Should.(*DisjunctionQuery).AddQuery(mq) @@ -81,7 +95,9 @@ func (q *BooleanQuery) AddShould(m ...Query) { func (q *BooleanQuery) AddMustNot(m ...Query) { if q.MustNot == nil { - q.MustNot = NewDisjunctionQuery([]Query{}) + tmp := NewDisjunctionQuery([]Query{}) + tmp.queryStringMode = q.queryStringMode + q.MustNot = tmp } for _, mq := range m { q.MustNot.(*DisjunctionQuery).AddQuery(mq) @@ -93,44 +109,67 @@ func (q *BooleanQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *BooleanQuery) Boost() float64{ +func (q *BooleanQuery) Boost() float64 { return q.BoostVal.Value() } -func (q *BooleanQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *BooleanQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { var err error var mustNotSearcher search.Searcher if q.MustNot != nil { - mustNotSearcher, err = q.MustNot.Searcher(i, m, explain) + mustNotSearcher, err = q.MustNot.Searcher(i, m, options) if err != nil { return nil, err } - if q.Must == nil && q.Should == nil { - q.Must = NewMatchAllQuery() + // if must not is MatchNone, reset it to nil + if _, ok := mustNotSearcher.(*searcher.MatchNoneSearcher); ok { + mustNotSearcher = nil } } var mustSearcher search.Searcher if q.Must != nil { - mustSearcher, err = q.Must.Searcher(i, m, explain) + mustSearcher, err = q.Must.Searcher(i, m, options) if err != nil { return nil, err } + // if must searcher is MatchNone, reset it to nil + if _, ok := mustSearcher.(*searcher.MatchNoneSearcher); ok { + mustSearcher = nil + } } var shouldSearcher search.Searcher if q.Should != nil { - shouldSearcher, err = q.Should.Searcher(i, m, explain) + shouldSearcher, err = q.Should.Searcher(i, m, options) + if err != nil { + return nil, err + } + // if should searcher is MatchNone, reset it to nil + if _, ok := shouldSearcher.(*searcher.MatchNoneSearcher); ok { + shouldSearcher = nil + } + } + + // if all 3 are nil, return MatchNone + if mustSearcher == nil && shouldSearcher == nil && mustNotSearcher == nil { + return searcher.NewMatchNoneSearcher(i) + } + + // if only mustNotSearcher, start with MatchAll + if mustSearcher == nil && shouldSearcher == nil && mustNotSearcher != nil { + mustSearcher, err = searcher.NewMatchAllSearcher(i, 1.0, options) if err != nil { return nil, err } } + // optimization, if only should searcher, just return it instead if mustSearcher == nil && shouldSearcher != nil && mustNotSearcher == nil { return shouldSearcher, nil } - return searcher.NewBooleanSearcher(i, mustSearcher, shouldSearcher, mustNotSearcher, explain) + return searcher.NewBooleanSearcher(i, mustSearcher, shouldSearcher, mustNotSearcher, options) } func (q *BooleanQuery) Validate() error { diff --git a/vendor/github.com/blevesearch/bleve/search/query/conjunction.go b/vendor/github.com/blevesearch/bleve/search/query/conjunction.go index e819d69760..39cc312de8 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/conjunction.go +++ b/vendor/github.com/blevesearch/bleve/search/query/conjunction.go @@ -24,8 +24,9 @@ import ( ) type ConjunctionQuery struct { - Conjuncts []Query `json:"conjuncts"` - BoostVal *Boost `json:"boost,omitempty"` + Conjuncts []Query `json:"conjuncts"` + BoostVal *Boost `json:"boost,omitempty"` + queryStringMode bool } // NewConjunctionQuery creates a new compound Query. @@ -41,7 +42,7 @@ func (q *ConjunctionQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *ConjunctionQuery) Boost() float64{ +func (q *ConjunctionQuery) Boost() float64 { return q.BoostVal.Value() } @@ -51,11 +52,10 @@ func (q *ConjunctionQuery) AddQuery(aq ...Query) { } } -func (q *ConjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { - ss := make([]search.Searcher, len(q.Conjuncts)) - for in, conjunct := range q.Conjuncts { - var err error - ss[in], err = conjunct.Searcher(i, m, explain) +func (q *ConjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { + ss := make([]search.Searcher, 0, len(q.Conjuncts)) + for _, conjunct := range q.Conjuncts { + sr, err := conjunct.Searcher(i, m, options) if err != nil { for _, searcher := range ss { if searcher != nil { @@ -64,8 +64,16 @@ func (q *ConjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, } return nil, err } + if _, ok := sr.(*searcher.MatchNoneSearcher); ok && q.queryStringMode { + // in query string mode, skip match none + continue + } + ss = append(ss, sr) + } + if len(ss) < 1 { + return searcher.NewMatchNoneSearcher(i) } - return searcher.NewConjunctionSearcher(i, ss, explain) + return searcher.NewConjunctionSearcher(i, ss, options) } func (q *ConjunctionQuery) Validate() error { diff --git a/vendor/github.com/blevesearch/bleve/search/query/date_range.go b/vendor/github.com/blevesearch/bleve/search/query/date_range.go index 8c5811f151..ff67a7bb70 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/date_range.go +++ b/vendor/github.com/blevesearch/bleve/search/query/date_range.go @@ -113,20 +113,19 @@ func (q *DateRangeQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *DateRangeQuery) Boost() float64{ +func (q *DateRangeQuery) Boost() float64 { return q.BoostVal.Value() } - func (q *DateRangeQuery) SetField(f string) { q.FieldVal = f } -func (q *DateRangeQuery) Field() string{ +func (q *DateRangeQuery) Field() string { return q.FieldVal } -func (q *DateRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *DateRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { min, max, err := q.parseEndpoints() if err != nil { return nil, err @@ -137,7 +136,7 @@ func (q *DateRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, e field = m.DefaultSearchField() } - return searcher.NewNumericRangeSearcher(i, min, max, q.InclusiveStart, q.InclusiveEnd, field, q.BoostVal.Value(), explain) + return searcher.NewNumericRangeSearcher(i, min, max, q.InclusiveStart, q.InclusiveEnd, field, q.BoostVal.Value(), options) } func (q *DateRangeQuery) parseEndpoints() (*float64, *float64, error) { diff --git a/vendor/github.com/blevesearch/bleve/search/query/disjunction.go b/vendor/github.com/blevesearch/bleve/search/query/disjunction.go index 08f47c4110..dacc3a75b1 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/disjunction.go +++ b/vendor/github.com/blevesearch/bleve/search/query/disjunction.go @@ -25,9 +25,10 @@ import ( ) type DisjunctionQuery struct { - Disjuncts []Query `json:"disjuncts"` - BoostVal *Boost `json:"boost,omitempty"` - Min float64 `json:"min"` + Disjuncts []Query `json:"disjuncts"` + BoostVal *Boost `json:"boost,omitempty"` + Min float64 `json:"min"` + queryStringMode bool } // NewDisjunctionQuery creates a new compound Query. @@ -43,11 +44,10 @@ func (q *DisjunctionQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *DisjunctionQuery) Boost() float64{ +func (q *DisjunctionQuery) Boost() float64 { return q.BoostVal.Value() } - func (q *DisjunctionQuery) AddQuery(aq ...Query) { for _, aaq := range aq { q.Disjuncts = append(q.Disjuncts, aaq) @@ -58,11 +58,10 @@ func (q *DisjunctionQuery) SetMin(m float64) { q.Min = m } -func (q *DisjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { - ss := make([]search.Searcher, len(q.Disjuncts)) - for in, disjunct := range q.Disjuncts { - var err error - ss[in], err = disjunct.Searcher(i, m, explain) +func (q *DisjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { + ss := make([]search.Searcher, 0, len(q.Disjuncts)) + for _, disjunct := range q.Disjuncts { + sr, err := disjunct.Searcher(i, m, options) if err != nil { for _, searcher := range ss { if searcher != nil { @@ -71,8 +70,16 @@ func (q *DisjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, } return nil, err } + if _, ok := sr.(*searcher.MatchNoneSearcher); ok && q.queryStringMode { + // in query string mode, skip match none + continue + } + ss = append(ss, sr) + } + if len(ss) < 1 { + return searcher.NewMatchNoneSearcher(i) } - return searcher.NewDisjunctionSearcher(i, ss, q.Min, explain) + return searcher.NewDisjunctionSearcher(i, ss, q.Min, options) } func (q *DisjunctionQuery) Validate() error { diff --git a/vendor/github.com/blevesearch/bleve/search/query/docid.go b/vendor/github.com/blevesearch/bleve/search/query/docid.go index c633d947ee..3b865f93ac 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/docid.go +++ b/vendor/github.com/blevesearch/bleve/search/query/docid.go @@ -40,10 +40,10 @@ func (q *DocIDQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *DocIDQuery) Boost() float64{ +func (q *DocIDQuery) Boost() float64 { return q.BoostVal.Value() } -func (q *DocIDQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { - return searcher.NewDocIDSearcher(i, q.IDs, q.BoostVal.Value(), explain) +func (q *DocIDQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { + return searcher.NewDocIDSearcher(i, q.IDs, q.BoostVal.Value(), options) } diff --git a/vendor/github.com/blevesearch/bleve/search/query/fuzzy.go b/vendor/github.com/blevesearch/bleve/search/query/fuzzy.go index 6effd3438f..f18982d407 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/fuzzy.go +++ b/vendor/github.com/blevesearch/bleve/search/query/fuzzy.go @@ -48,7 +48,7 @@ func (q *FuzzyQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *FuzzyQuery) Boost() float64{ +func (q *FuzzyQuery) Boost() float64 { return q.BoostVal.Value() } @@ -56,7 +56,7 @@ func (q *FuzzyQuery) SetField(f string) { q.FieldVal = f } -func (q *FuzzyQuery) Field() string{ +func (q *FuzzyQuery) Field() string { return q.FieldVal } @@ -68,10 +68,10 @@ func (q *FuzzyQuery) SetPrefix(p int) { q.Prefix = p } -func (q *FuzzyQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *FuzzyQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() } - return searcher.NewFuzzySearcher(i, q.Term, q.Prefix, q.Fuzziness, field, q.BoostVal.Value(), explain) + return searcher.NewFuzzySearcher(i, q.Term, q.Prefix, q.Fuzziness, field, q.BoostVal.Value(), options) } diff --git a/vendor/github.com/blevesearch/bleve/search/query/geo_boundingbox.go b/vendor/github.com/blevesearch/bleve/search/query/geo_boundingbox.go new file mode 100644 index 0000000000..de6be4a583 --- /dev/null +++ b/vendor/github.com/blevesearch/bleve/search/query/geo_boundingbox.go @@ -0,0 +1,113 @@ +// Copyright (c) 2017 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 query + +import ( + "encoding/json" + "fmt" + + "github.com/blevesearch/bleve/geo" + "github.com/blevesearch/bleve/index" + "github.com/blevesearch/bleve/mapping" + "github.com/blevesearch/bleve/search" + "github.com/blevesearch/bleve/search/searcher" +) + +type GeoBoundingBoxQuery struct { + TopLeft []float64 `json:"top_left,omitempty"` + BottomRight []float64 `json:"bottom_right,omitempty"` + FieldVal string `json:"field,omitempty"` + BoostVal *Boost `json:"boost,omitempty"` +} + +func NewGeoBoundingBoxQuery(topLeftLon, topLeftLat, bottomRightLon, bottomRightLat float64) *GeoBoundingBoxQuery { + return &GeoBoundingBoxQuery{ + TopLeft: []float64{topLeftLon, topLeftLat}, + BottomRight: []float64{bottomRightLon, bottomRightLat}, + } +} + +func (q *GeoBoundingBoxQuery) SetBoost(b float64) { + boost := Boost(b) + q.BoostVal = &boost +} + +func (q *GeoBoundingBoxQuery) Boost() float64 { + return q.BoostVal.Value() +} + +func (q *GeoBoundingBoxQuery) SetField(f string) { + q.FieldVal = f +} + +func (q *GeoBoundingBoxQuery) Field() string { + return q.FieldVal +} + +func (q *GeoBoundingBoxQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { + field := q.FieldVal + if q.FieldVal == "" { + field = m.DefaultSearchField() + } + + if q.BottomRight[0] < q.TopLeft[0] { + // cross date line, rewrite as two parts + + leftSearcher, err := searcher.NewGeoBoundingBoxSearcher(i, -180, q.BottomRight[1], q.BottomRight[0], q.TopLeft[1], field, q.BoostVal.Value(), options, true) + if err != nil { + return nil, err + } + rightSearcher, err := searcher.NewGeoBoundingBoxSearcher(i, q.TopLeft[0], q.BottomRight[1], 180, q.TopLeft[1], field, q.BoostVal.Value(), options, true) + if err != nil { + _ = leftSearcher.Close() + return nil, err + } + + return searcher.NewDisjunctionSearcher(i, []search.Searcher{leftSearcher, rightSearcher}, 0, options) + } + + return searcher.NewGeoBoundingBoxSearcher(i, q.TopLeft[0], q.BottomRight[1], q.BottomRight[0], q.TopLeft[1], field, q.BoostVal.Value(), options, true) +} + +func (q *GeoBoundingBoxQuery) Validate() error { + return nil +} + +func (q *GeoBoundingBoxQuery) UnmarshalJSON(data []byte) error { + tmp := struct { + TopLeft interface{} `json:"top_left,omitempty"` + BottomRight interface{} `json:"bottom_right,omitempty"` + FieldVal string `json:"field,omitempty"` + BoostVal *Boost `json:"boost,omitempty"` + }{} + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + // now use our generic point parsing code from the geo package + lon, lat, found := geo.ExtractGeoPoint(tmp.TopLeft) + if !found { + return fmt.Errorf("geo location top_left not in a valid format") + } + q.TopLeft = []float64{lon, lat} + lon, lat, found = geo.ExtractGeoPoint(tmp.BottomRight) + if !found { + return fmt.Errorf("geo location bottom_right not in a valid format") + } + q.BottomRight = []float64{lon, lat} + q.FieldVal = tmp.FieldVal + q.BoostVal = tmp.BoostVal + return nil +} diff --git a/vendor/github.com/blevesearch/bleve/search/query/geo_distance.go b/vendor/github.com/blevesearch/bleve/search/query/geo_distance.go new file mode 100644 index 0000000000..ef3aa88c99 --- /dev/null +++ b/vendor/github.com/blevesearch/bleve/search/query/geo_distance.go @@ -0,0 +1,100 @@ +// Copyright (c) 2017 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 query + +import ( + "encoding/json" + "fmt" + + "github.com/blevesearch/bleve/geo" + "github.com/blevesearch/bleve/index" + "github.com/blevesearch/bleve/mapping" + "github.com/blevesearch/bleve/search" + "github.com/blevesearch/bleve/search/searcher" +) + +type GeoDistanceQuery struct { + Location []float64 `json:"location,omitempty"` + Distance string `json:"distance,omitempty"` + FieldVal string `json:"field,omitempty"` + BoostVal *Boost `json:"boost,omitempty"` +} + +func NewGeoDistanceQuery(lon, lat float64, distance string) *GeoDistanceQuery { + return &GeoDistanceQuery{ + Location: []float64{lon, lat}, + Distance: distance, + } +} + +func (q *GeoDistanceQuery) SetBoost(b float64) { + boost := Boost(b) + q.BoostVal = &boost +} + +func (q *GeoDistanceQuery) Boost() float64 { + return q.BoostVal.Value() +} + +func (q *GeoDistanceQuery) SetField(f string) { + q.FieldVal = f +} + +func (q *GeoDistanceQuery) Field() string { + return q.FieldVal +} + +func (q *GeoDistanceQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, + options search.SearcherOptions) (search.Searcher, error) { + field := q.FieldVal + if q.FieldVal == "" { + field = m.DefaultSearchField() + } + + dist, err := geo.ParseDistance(q.Distance) + if err != nil { + return nil, err + } + + return searcher.NewGeoPointDistanceSearcher(i, q.Location[0], q.Location[1], + dist, field, q.BoostVal.Value(), options) +} + +func (q *GeoDistanceQuery) Validate() error { + return nil +} + +func (q *GeoDistanceQuery) UnmarshalJSON(data []byte) error { + tmp := struct { + Location interface{} `json:"location,omitempty"` + Distance string `json:"distance,omitempty"` + FieldVal string `json:"field,omitempty"` + BoostVal *Boost `json:"boost,omitempty"` + }{} + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + // now use our generic point parsing code from the geo package + lon, lat, found := geo.ExtractGeoPoint(tmp.Location) + if !found { + return fmt.Errorf("geo location not in a valid format") + } + q.Location = []float64{lon, lat} + q.Distance = tmp.Distance + q.FieldVal = tmp.FieldVal + q.BoostVal = tmp.BoostVal + return nil +} diff --git a/vendor/github.com/blevesearch/bleve/search/query/match.go b/vendor/github.com/blevesearch/bleve/search/query/match.go index 6d0b005333..36c9ee4a47 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/match.go +++ b/vendor/github.com/blevesearch/bleve/search/query/match.go @@ -90,7 +90,7 @@ func (q *MatchQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *MatchQuery) Boost() float64{ +func (q *MatchQuery) Boost() float64 { return q.BoostVal.Value() } @@ -98,7 +98,7 @@ func (q *MatchQuery) SetField(f string) { q.FieldVal = f } -func (q *MatchQuery) Field() string{ +func (q *MatchQuery) Field() string { return q.FieldVal } @@ -114,7 +114,7 @@ func (q *MatchQuery) SetOperator(operator MatchQueryOperator) { q.Operator = operator } -func (q *MatchQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *MatchQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { @@ -160,17 +160,17 @@ func (q *MatchQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, expla shouldQuery := NewDisjunctionQuery(tqs) shouldQuery.SetMin(1) shouldQuery.SetBoost(q.BoostVal.Value()) - return shouldQuery.Searcher(i, m, explain) + return shouldQuery.Searcher(i, m, options) case MatchQueryOperatorAnd: mustQuery := NewConjunctionQuery(tqs) mustQuery.SetBoost(q.BoostVal.Value()) - return mustQuery.Searcher(i, m, explain) + return mustQuery.Searcher(i, m, options) default: return nil, fmt.Errorf("unhandled operator %d", q.Operator) } } noneQuery := NewMatchNoneQuery() - return noneQuery.Searcher(i, m, explain) + return noneQuery.Searcher(i, m, options) } diff --git a/vendor/github.com/blevesearch/bleve/search/query/match_all.go b/vendor/github.com/blevesearch/bleve/search/query/match_all.go index 9eb7eb95f7..7fbe1f99db 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/match_all.go +++ b/vendor/github.com/blevesearch/bleve/search/query/match_all.go @@ -38,14 +38,12 @@ func (q *MatchAllQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *MatchAllQuery) Boost() float64{ +func (q *MatchAllQuery) Boost() float64 { return q.BoostVal.Value() } - - -func (q *MatchAllQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { - return searcher.NewMatchAllSearcher(i, q.BoostVal.Value(), explain) +func (q *MatchAllQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { + return searcher.NewMatchAllSearcher(i, q.BoostVal.Value(), options) } func (q *MatchAllQuery) MarshalJSON() ([]byte, error) { diff --git a/vendor/github.com/blevesearch/bleve/search/query/match_none.go b/vendor/github.com/blevesearch/bleve/search/query/match_none.go index 5264c5a6bd..dc2ea780c1 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/match_none.go +++ b/vendor/github.com/blevesearch/bleve/search/query/match_none.go @@ -38,11 +38,11 @@ func (q *MatchNoneQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *MatchNoneQuery) Boost() float64{ +func (q *MatchNoneQuery) Boost() float64 { return q.BoostVal.Value() } -func (q *MatchNoneQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *MatchNoneQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { return searcher.NewMatchNoneSearcher(i) } diff --git a/vendor/github.com/blevesearch/bleve/search/query/match_phrase.go b/vendor/github.com/blevesearch/bleve/search/query/match_phrase.go index a148808ff3..51be35526f 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/match_phrase.go +++ b/vendor/github.com/blevesearch/bleve/search/query/match_phrase.go @@ -49,7 +49,7 @@ func (q *MatchPhraseQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *MatchPhraseQuery) Boost() float64{ +func (q *MatchPhraseQuery) Boost() float64 { return q.BoostVal.Value() } @@ -57,11 +57,11 @@ func (q *MatchPhraseQuery) SetField(f string) { q.FieldVal = f } -func (q *MatchPhraseQuery) Field() string{ +func (q *MatchPhraseQuery) Field() string { return q.FieldVal } -func (q *MatchPhraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *MatchPhraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -81,15 +81,15 @@ func (q *MatchPhraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, tokens := analyzer.Analyze([]byte(q.MatchPhrase)) if len(tokens) > 0 { phrase := tokenStreamToPhrase(tokens) - phraseQuery := NewPhraseQuery(phrase, field) + phraseQuery := NewMultiPhraseQuery(phrase, field) phraseQuery.SetBoost(q.BoostVal.Value()) - return phraseQuery.Searcher(i, m, explain) + return phraseQuery.Searcher(i, m, options) } noneQuery := NewMatchNoneQuery() - return noneQuery.Searcher(i, m, explain) + return noneQuery.Searcher(i, m, options) } -func tokenStreamToPhrase(tokens analysis.TokenStream) []string { +func tokenStreamToPhrase(tokens analysis.TokenStream) [][]string { firstPosition := int(^uint(0) >> 1) lastPosition := 0 for _, token := range tokens { @@ -102,13 +102,10 @@ func tokenStreamToPhrase(tokens analysis.TokenStream) []string { } phraseLen := lastPosition - firstPosition + 1 if phraseLen > 0 { - rv := make([]string, phraseLen) - for i := 0; i < phraseLen; i++ { - rv[i] = "" - } + rv := make([][]string, phraseLen) for _, token := range tokens { pos := token.Position - firstPosition - rv[pos] = string(token.Term) + rv[pos] = append(rv[pos], string(token.Term)) } return rv } diff --git a/vendor/github.com/blevesearch/bleve/search/query/multi_phrase.go b/vendor/github.com/blevesearch/bleve/search/query/multi_phrase.go new file mode 100644 index 0000000000..8a7c9b6ade --- /dev/null +++ b/vendor/github.com/blevesearch/bleve/search/query/multi_phrase.go @@ -0,0 +1,80 @@ +// 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 query + +import ( + "encoding/json" + "fmt" + + "github.com/blevesearch/bleve/index" + "github.com/blevesearch/bleve/mapping" + "github.com/blevesearch/bleve/search" + "github.com/blevesearch/bleve/search/searcher" +) + +type MultiPhraseQuery struct { + Terms [][]string `json:"terms"` + Field string `json:"field,omitempty"` + BoostVal *Boost `json:"boost,omitempty"` +} + +// NewMultiPhraseQuery creates a new Query for finding +// term phrases in the index. +// It is like PhraseQuery, but each position in the +// phrase may be satisfied by a list of terms +// as opposed to just one. +// At least one of the terms must exist in the correct +// order, at the correct index offsets, in the +// specified field. Queried field must have been indexed with +// IncludeTermVectors set to true. +func NewMultiPhraseQuery(terms [][]string, field string) *MultiPhraseQuery { + return &MultiPhraseQuery{ + Terms: terms, + Field: field, + } +} + +func (q *MultiPhraseQuery) SetBoost(b float64) { + boost := Boost(b) + q.BoostVal = &boost +} + +func (q *MultiPhraseQuery) Boost() float64 { + return q.BoostVal.Value() +} + +func (q *MultiPhraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { + return searcher.NewMultiPhraseSearcher(i, q.Terms, q.Field, options) +} + +func (q *MultiPhraseQuery) Validate() error { + if len(q.Terms) < 1 { + return fmt.Errorf("phrase query must contain at least one term") + } + return nil +} + +func (q *MultiPhraseQuery) UnmarshalJSON(data []byte) error { + type _mphraseQuery MultiPhraseQuery + tmp := _mphraseQuery{} + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + q.Terms = tmp.Terms + q.Field = tmp.Field + q.BoostVal = tmp.BoostVal + return nil +} diff --git a/vendor/github.com/blevesearch/bleve/search/query/numeric_range.go b/vendor/github.com/blevesearch/bleve/search/query/numeric_range.go index 93f7fd6b27..ea3f068862 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/numeric_range.go +++ b/vendor/github.com/blevesearch/bleve/search/query/numeric_range.go @@ -59,7 +59,7 @@ func (q *NumericRangeQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *NumericRangeQuery) Boost() float64{ +func (q *NumericRangeQuery) Boost() float64 { return q.BoostVal.Value() } @@ -67,16 +67,16 @@ func (q *NumericRangeQuery) SetField(f string) { q.FieldVal = f } -func (q *NumericRangeQuery) Field() string{ +func (q *NumericRangeQuery) Field() string { return q.FieldVal } -func (q *NumericRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *NumericRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() } - return searcher.NewNumericRangeSearcher(i, q.Min, q.Max, q.InclusiveMin, q.InclusiveMax, field, q.BoostVal.Value(), explain) + return searcher.NewNumericRangeSearcher(i, q.Min, q.Max, q.InclusiveMin, q.InclusiveMax, field, q.BoostVal.Value(), options) } func (q *NumericRangeQuery) Validate() error { diff --git a/vendor/github.com/blevesearch/bleve/search/query/phrase.go b/vendor/github.com/blevesearch/bleve/search/query/phrase.go index 0cd608b8bd..dff1a02d4b 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/phrase.go +++ b/vendor/github.com/blevesearch/bleve/search/query/phrase.go @@ -25,10 +25,9 @@ import ( ) type PhraseQuery struct { - Terms []string `json:"terms"` - Field string `json:"field,omitempty"` - BoostVal *Boost `json:"boost,omitempty"` - termQueries []Query + Terms []string `json:"terms"` + Field string `json:"field,omitempty"` + BoostVal *Boost `json:"boost,omitempty"` } // NewPhraseQuery creates a new Query for finding @@ -38,18 +37,9 @@ type PhraseQuery struct { // specified field. Queried field must have been indexed with // IncludeTermVectors set to true. func NewPhraseQuery(terms []string, field string) *PhraseQuery { - termQueries := make([]Query, 0) - for _, term := range terms { - if term != "" { - tq := NewTermQuery(term) - tq.SetField(field) - termQueries = append(termQueries, tq) - } - } return &PhraseQuery{ - Terms: terms, - Field: field, - termQueries: termQueries, + Terms: terms, + Field: field, } } @@ -58,22 +48,16 @@ func (q *PhraseQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *PhraseQuery) Boost() float64{ +func (q *PhraseQuery) Boost() float64 { return q.BoostVal.Value() } -func (q *PhraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { - - conjunctionQuery := NewConjunctionQuery(q.termQueries) - conjunctionSearcher, err := conjunctionQuery.Searcher(i, m, explain) - if err != nil { - return nil, err - } - return searcher.NewPhraseSearcher(i, conjunctionSearcher.(*searcher.ConjunctionSearcher), q.Terms) +func (q *PhraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { + return searcher.NewPhraseSearcher(i, q.Terms, q.Field, options) } func (q *PhraseQuery) Validate() error { - if len(q.termQueries) < 1 { + if len(q.Terms) < 1 { return fmt.Errorf("phrase query must contain at least one term") } return nil @@ -89,9 +73,5 @@ func (q *PhraseQuery) UnmarshalJSON(data []byte) error { q.Terms = tmp.Terms q.Field = tmp.Field q.BoostVal = tmp.BoostVal - q.termQueries = make([]Query, len(q.Terms)) - for i, term := range q.Terms { - q.termQueries[i] = &TermQuery{Term: term, FieldVal: q.Field, BoostVal: q.BoostVal} - } return nil } diff --git a/vendor/github.com/blevesearch/bleve/search/query/prefix.go b/vendor/github.com/blevesearch/bleve/search/query/prefix.go index d8c2bb2f92..4f5be2b26e 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/prefix.go +++ b/vendor/github.com/blevesearch/bleve/search/query/prefix.go @@ -41,7 +41,7 @@ func (q *PrefixQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *PrefixQuery) Boost() float64{ +func (q *PrefixQuery) Boost() float64 { return q.BoostVal.Value() } @@ -49,14 +49,14 @@ func (q *PrefixQuery) SetField(f string) { q.FieldVal = f } -func (q *PrefixQuery) Field() string{ +func (q *PrefixQuery) Field() string { return q.FieldVal } -func (q *PrefixQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *PrefixQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() } - return searcher.NewTermPrefixSearcher(i, q.Prefix, field, q.BoostVal.Value(), explain) + return searcher.NewTermPrefixSearcher(i, q.Prefix, field, q.BoostVal.Value(), options) } diff --git a/vendor/github.com/blevesearch/bleve/search/query/query.go b/vendor/github.com/blevesearch/bleve/search/query/query.go index bb65a07526..1b0d94c012 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/query.go +++ b/vendor/github.com/blevesearch/bleve/search/query/query.go @@ -36,7 +36,8 @@ func SetLog(l *log.Logger) { // A Query represents a description of the type // and parameters for a query into the index. type Query interface { - Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) + Searcher(i index.IndexReader, m mapping.IndexMapping, + options search.SearcherOptions) (search.Searcher, error) } // A BoostableQuery represents a Query which can be boosted @@ -122,7 +123,13 @@ func ParseQuery(input []byte) (Query, error) { var rv PhraseQuery err := json.Unmarshal(input, &rv) if err != nil { - return nil, err + // now try multi-phrase + var rv2 MultiPhraseQuery + err = json.Unmarshal(input, &rv2) + if err != nil { + return nil, err + } + return &rv2, nil } return &rv, nil } @@ -154,8 +161,8 @@ func ParseQuery(input []byte) (Query, error) { } return &rv, nil } - _, hasMin := tmp["min"] - _, hasMax := tmp["max"] + _, hasMin := tmp["min"].(float64) + _, hasMax := tmp["max"].(float64) if hasMin || hasMax { var rv NumericRangeQuery err := json.Unmarshal(input, &rv) @@ -164,6 +171,16 @@ func ParseQuery(input []byte) (Query, error) { } return &rv, nil } + _, hasMinStr := tmp["min"].(string) + _, hasMaxStr := tmp["max"].(string) + if hasMinStr || hasMaxStr { + var rv TermRangeQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + return &rv, nil + } _, hasStart := tmp["start"] _, hasEnd := tmp["end"] if hasStart || hasEnd { @@ -237,6 +254,25 @@ func ParseQuery(input []byte) (Query, error) { } return &rv, nil } + _, hasTopLeft := tmp["top_left"] + _, hasBottomRight := tmp["bottom_right"] + if hasTopLeft && hasBottomRight { + var rv GeoBoundingBoxQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + return &rv, nil + } + _, hasDistance := tmp["distance"] + if hasDistance { + var rv GeoDistanceQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + return &rv, nil + } return nil, fmt.Errorf("unknown query type") } @@ -300,14 +336,6 @@ func expandQuery(m mapping.IndexMapping, query Query) (Query, error) { return nil, err } return &q, nil - case *PhraseQuery: - q := *query.(*PhraseQuery) - children, err := expandSlice(q.termQueries) - if err != nil { - return nil, err - } - q.termQueries = children - return &q, nil default: return query, nil } diff --git a/vendor/github.com/blevesearch/bleve/search/query/query_string.go b/vendor/github.com/blevesearch/bleve/search/query/query_string.go index d8f421f451..ecafe6bc05 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/query_string.go +++ b/vendor/github.com/blevesearch/bleve/search/query/query_string.go @@ -39,16 +39,20 @@ func (q *QueryStringQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *QueryStringQuery) Boost() float64{ +func (q *QueryStringQuery) Boost() float64 { return q.BoostVal.Value() } -func (q *QueryStringQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *QueryStringQuery) Parse() (Query, error) { + return parseQuerySyntax(q.Query) +} + +func (q *QueryStringQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { newQuery, err := parseQuerySyntax(q.Query) if err != nil { return nil, err } - return newQuery.Searcher(i, m, explain) + return newQuery.Searcher(i, m, options) } func (q *QueryStringQuery) Validate() error { diff --git a/vendor/github.com/blevesearch/bleve/search/query/query_string.y b/vendor/github.com/blevesearch/bleve/search/query/query_string.y index d703be28c1..d3e5ac9d7e 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/query_string.y +++ b/vendor/github.com/blevesearch/bleve/search/query/query_string.y @@ -27,6 +27,7 @@ tEQUAL tTILDE %type <s> tSTRING %type <s> tPHRASE %type <s> tNUMBER +%type <s> posOrNegNumber %type <s> tTILDE %type <s> tBOOST %type <q> searchBase @@ -127,7 +128,15 @@ tSTRING tCOLON tSTRING tTILDE { tNUMBER { str := $1 logDebugGrammar("STRING - %s", str) - q := NewMatchQuery(str) + q1 := NewMatchQuery(str) + val, err := strconv.ParseFloat($1, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } + inclusive := true + q2 := NewNumericRangeInclusiveQuery(&val, &val, &inclusive, &inclusive) + q := NewDisjunctionQuery([]Query{q1,q2}) + q.queryStringMode = true $$ = q } | @@ -154,12 +163,21 @@ tSTRING tCOLON tSTRING { $$ = q } | -tSTRING tCOLON tNUMBER { +tSTRING tCOLON posOrNegNumber { field := $1 str := $3 logDebugGrammar("FIELD - %s STRING - %s", field, str) - q := NewMatchQuery(str) - q.SetField(field) + q1 := NewMatchQuery(str) + q1.SetField(field) + val, err := strconv.ParseFloat($3, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } + inclusive := true + q2 := NewNumericRangeInclusiveQuery(&val, &val, &inclusive, &inclusive) + q2.SetField(field) + q := NewDisjunctionQuery([]Query{q1,q2}) + q.queryStringMode = true $$ = q } | @@ -172,9 +190,12 @@ tSTRING tCOLON tPHRASE { $$ = q } | -tSTRING tCOLON tGREATER tNUMBER { +tSTRING tCOLON tGREATER posOrNegNumber { field := $1 - min, _ := strconv.ParseFloat($4, 64) + min, err := strconv.ParseFloat($4, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } minInclusive := false logDebugGrammar("FIELD - GREATER THAN %f", min) q := NewNumericRangeInclusiveQuery(&min, nil, &minInclusive, nil) @@ -182,9 +203,12 @@ tSTRING tCOLON tGREATER tNUMBER { $$ = q } | -tSTRING tCOLON tGREATER tEQUAL tNUMBER { +tSTRING tCOLON tGREATER tEQUAL posOrNegNumber { field := $1 - min, _ := strconv.ParseFloat($5, 64) + min, err := strconv.ParseFloat($5, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } minInclusive := true logDebugGrammar("FIELD - GREATER THAN OR EQUAL %f", min) q := NewNumericRangeInclusiveQuery(&min, nil, &minInclusive, nil) @@ -192,9 +216,12 @@ tSTRING tCOLON tGREATER tEQUAL tNUMBER { $$ = q } | -tSTRING tCOLON tLESS tNUMBER { +tSTRING tCOLON tLESS posOrNegNumber { field := $1 - max, _ := strconv.ParseFloat($4, 64) + max, err := strconv.ParseFloat($4, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } maxInclusive := false logDebugGrammar("FIELD - LESS THAN %f", max) q := NewNumericRangeInclusiveQuery(nil, &max, nil, &maxInclusive) @@ -202,9 +229,12 @@ tSTRING tCOLON tLESS tNUMBER { $$ = q } | -tSTRING tCOLON tLESS tEQUAL tNUMBER { +tSTRING tCOLON tLESS tEQUAL posOrNegNumber { field := $1 - max, _ := strconv.ParseFloat($5, 64) + max, err := strconv.ParseFloat($5, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } maxInclusive := true logDebugGrammar("FIELD - LESS THAN OR EQUAL %f", max) q := NewNumericRangeInclusiveQuery(nil, &max, nil, &maxInclusive) @@ -287,3 +317,12 @@ tBOOST { } logDebugGrammar("BOOST %f", boost) }; + +posOrNegNumber: +tNUMBER { + $$ = $1 +} +| +tMINUS tNUMBER { + $$ = "-" + $2 +}; diff --git a/vendor/github.com/blevesearch/bleve/search/query/query_string.y.go b/vendor/github.com/blevesearch/bleve/search/query/query_string.y.go index 74ea00fc72..ac2d322263 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/query_string.y.go +++ b/vendor/github.com/blevesearch/bleve/search/query/query_string.y.go @@ -70,57 +70,58 @@ var yyExca = [...]int{ -2, 5, } -const yyNprod = 26 +const yyNprod = 28 const yyPrivate = 57344 var yyTokenNames []string var yyStates []string -const yyLast = 31 +const yyLast = 42 var yyAct = [...]int{ - 16, 18, 21, 13, 27, 24, 17, 19, 20, 25, - 22, 15, 26, 23, 9, 11, 31, 14, 29, 3, - 10, 30, 2, 28, 5, 6, 7, 1, 4, 12, - 8, + 17, 16, 18, 23, 22, 30, 3, 21, 19, 20, + 29, 26, 22, 22, 1, 21, 21, 15, 28, 25, + 24, 27, 34, 14, 22, 13, 31, 21, 32, 33, + 22, 9, 11, 21, 5, 6, 2, 10, 4, 12, + 7, 8, } var yyPact = [...]int{ - 18, -1000, -1000, 18, 10, -1000, -1000, -1000, -6, 3, - -1000, -1000, -1000, -1000, -1000, -4, -12, -1000, -1000, 0, - -1, -1000, -1000, 13, -1000, -1000, 11, -1000, -1000, -1000, - -1000, -1000, + 28, -1000, -1000, 28, 27, -1000, -1000, -1000, 16, 9, + -1000, -1000, -1000, -1000, -1000, -3, -11, -1000, -1000, 6, + 5, -1000, -5, -1000, -1000, 23, -1000, -1000, 17, -1000, + -1000, -1000, -1000, -1000, -1000, } var yyPgo = [...]int{ - 0, 30, 29, 28, 27, 22, 19, + 0, 0, 41, 39, 38, 14, 36, 6, } var yyR1 = [...]int{ - 0, 4, 5, 5, 6, 3, 3, 3, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, + 0, 5, 6, 6, 7, 4, 4, 4, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 1, 1, } var yyR2 = [...]int{ 0, 1, 2, 1, 3, 0, 1, 1, 1, 2, 4, 1, 1, 3, 3, 3, 4, 5, 4, 5, - 4, 5, 4, 5, 0, 1, + 4, 5, 4, 5, 0, 1, 1, 2, } var yyChk = [...]int{ - -1000, -4, -5, -6, -3, 6, 7, -5, -1, 4, - 10, 5, -2, 9, 14, 8, 4, 10, 5, 11, - 12, 14, 10, 13, 5, 10, 13, 5, 10, 5, - 10, 5, + -1000, -5, -6, -7, -4, 6, 7, -6, -2, 4, + 10, 5, -3, 9, 14, 8, 4, -1, 5, 11, + 12, 10, 7, 14, -1, 13, 5, -1, 13, 5, + 10, -1, 5, -1, 5, } var yyDef = [...]int{ 5, -2, 1, -2, 0, 6, 7, 2, 24, 8, 11, 12, 4, 25, 9, 0, 13, 14, 15, 0, - 0, 10, 16, 0, 20, 18, 0, 22, 17, 21, - 19, 23, + 0, 26, 0, 10, 16, 0, 20, 18, 0, 22, + 27, 17, 21, 19, 23, } var yyTok1 = [...]int{ @@ -474,25 +475,25 @@ yydefault: case 1: yyDollar = yyS[yypt-1 : yypt+1] - //line query_string.y:39 + //line query_string.y:40 { logDebugGrammar("INPUT") } case 2: yyDollar = yyS[yypt-2 : yypt+1] - //line query_string.y:44 + //line query_string.y:45 { logDebugGrammar("SEARCH PARTS") } case 3: yyDollar = yyS[yypt-1 : yypt+1] - //line query_string.y:48 + //line query_string.y:49 { logDebugGrammar("SEARCH PART") } case 4: yyDollar = yyS[yypt-3 : yypt+1] - //line query_string.y:53 + //line query_string.y:54 { query := yyDollar[2].q if yyDollar[3].pf != nil { @@ -511,27 +512,27 @@ yydefault: } case 5: yyDollar = yyS[yypt-0 : yypt+1] - //line query_string.y:72 + //line query_string.y:73 { yyVAL.n = queryShould } case 6: yyDollar = yyS[yypt-1 : yypt+1] - //line query_string.y:76 + //line query_string.y:77 { logDebugGrammar("PLUS") yyVAL.n = queryMust } case 7: yyDollar = yyS[yypt-1 : yypt+1] - //line query_string.y:81 + //line query_string.y:82 { logDebugGrammar("MINUS") yyVAL.n = queryMustNot } case 8: yyDollar = yyS[yypt-1 : yypt+1] - //line query_string.y:87 + //line query_string.y:88 { str := yyDollar[1].s logDebugGrammar("STRING - %s", str) @@ -547,7 +548,7 @@ yydefault: } case 9: yyDollar = yyS[yypt-2 : yypt+1] - //line query_string.y:101 + //line query_string.y:102 { str := yyDollar[1].s fuzziness, err := strconv.ParseFloat(yyDollar[2].s, 64) @@ -561,7 +562,7 @@ yydefault: } case 10: yyDollar = yyS[yypt-4 : yypt+1] - //line query_string.y:113 + //line query_string.y:114 { field := yyDollar[1].s str := yyDollar[3].s @@ -577,16 +578,24 @@ yydefault: } case 11: yyDollar = yyS[yypt-1 : yypt+1] - //line query_string.y:127 + //line query_string.y:128 { str := yyDollar[1].s logDebugGrammar("STRING - %s", str) - q := NewMatchQuery(str) + q1 := NewMatchQuery(str) + val, err := strconv.ParseFloat(yyDollar[1].s, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } + inclusive := true + q2 := NewNumericRangeInclusiveQuery(&val, &val, &inclusive, &inclusive) + q := NewDisjunctionQuery([]Query{q1, q2}) + q.queryStringMode = true yyVAL.q = q } case 12: yyDollar = yyS[yypt-1 : yypt+1] - //line query_string.y:134 + //line query_string.y:143 { phrase := yyDollar[1].s logDebugGrammar("PHRASE - %s", phrase) @@ -595,7 +604,7 @@ yydefault: } case 13: yyDollar = yyS[yypt-3 : yypt+1] - //line query_string.y:141 + //line query_string.y:150 { field := yyDollar[1].s str := yyDollar[3].s @@ -613,18 +622,27 @@ yydefault: } case 14: yyDollar = yyS[yypt-3 : yypt+1] - //line query_string.y:157 + //line query_string.y:166 { field := yyDollar[1].s str := yyDollar[3].s logDebugGrammar("FIELD - %s STRING - %s", field, str) - q := NewMatchQuery(str) - q.SetField(field) + q1 := NewMatchQuery(str) + q1.SetField(field) + val, err := strconv.ParseFloat(yyDollar[3].s, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } + inclusive := true + q2 := NewNumericRangeInclusiveQuery(&val, &val, &inclusive, &inclusive) + q2.SetField(field) + q := NewDisjunctionQuery([]Query{q1, q2}) + q.queryStringMode = true yyVAL.q = q } case 15: yyDollar = yyS[yypt-3 : yypt+1] - //line query_string.y:166 + //line query_string.y:184 { field := yyDollar[1].s phrase := yyDollar[3].s @@ -635,10 +653,13 @@ yydefault: } case 16: yyDollar = yyS[yypt-4 : yypt+1] - //line query_string.y:175 + //line query_string.y:193 { field := yyDollar[1].s - min, _ := strconv.ParseFloat(yyDollar[4].s, 64) + min, err := strconv.ParseFloat(yyDollar[4].s, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } minInclusive := false logDebugGrammar("FIELD - GREATER THAN %f", min) q := NewNumericRangeInclusiveQuery(&min, nil, &minInclusive, nil) @@ -647,10 +668,13 @@ yydefault: } case 17: yyDollar = yyS[yypt-5 : yypt+1] - //line query_string.y:185 + //line query_string.y:206 { field := yyDollar[1].s - min, _ := strconv.ParseFloat(yyDollar[5].s, 64) + min, err := strconv.ParseFloat(yyDollar[5].s, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } minInclusive := true logDebugGrammar("FIELD - GREATER THAN OR EQUAL %f", min) q := NewNumericRangeInclusiveQuery(&min, nil, &minInclusive, nil) @@ -659,10 +683,13 @@ yydefault: } case 18: yyDollar = yyS[yypt-4 : yypt+1] - //line query_string.y:195 + //line query_string.y:219 { field := yyDollar[1].s - max, _ := strconv.ParseFloat(yyDollar[4].s, 64) + max, err := strconv.ParseFloat(yyDollar[4].s, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } maxInclusive := false logDebugGrammar("FIELD - LESS THAN %f", max) q := NewNumericRangeInclusiveQuery(nil, &max, nil, &maxInclusive) @@ -671,10 +698,13 @@ yydefault: } case 19: yyDollar = yyS[yypt-5 : yypt+1] - //line query_string.y:205 + //line query_string.y:232 { field := yyDollar[1].s - max, _ := strconv.ParseFloat(yyDollar[5].s, 64) + max, err := strconv.ParseFloat(yyDollar[5].s, 64) + if err != nil { + yylex.(*lexerWrapper).lex.Error(fmt.Sprintf("error parsing number: %v", err)) + } maxInclusive := true logDebugGrammar("FIELD - LESS THAN OR EQUAL %f", max) q := NewNumericRangeInclusiveQuery(nil, &max, nil, &maxInclusive) @@ -683,7 +713,7 @@ yydefault: } case 20: yyDollar = yyS[yypt-4 : yypt+1] - //line query_string.y:215 + //line query_string.y:245 { field := yyDollar[1].s minInclusive := false @@ -700,7 +730,7 @@ yydefault: } case 21: yyDollar = yyS[yypt-5 : yypt+1] - //line query_string.y:230 + //line query_string.y:260 { field := yyDollar[1].s minInclusive := true @@ -717,7 +747,7 @@ yydefault: } case 22: yyDollar = yyS[yypt-4 : yypt+1] - //line query_string.y:245 + //line query_string.y:275 { field := yyDollar[1].s maxInclusive := false @@ -734,7 +764,7 @@ yydefault: } case 23: yyDollar = yyS[yypt-5 : yypt+1] - //line query_string.y:260 + //line query_string.y:290 { field := yyDollar[1].s maxInclusive := true @@ -751,13 +781,13 @@ yydefault: } case 24: yyDollar = yyS[yypt-0 : yypt+1] - //line query_string.y:276 + //line query_string.y:306 { yyVAL.pf = nil } case 25: yyDollar = yyS[yypt-1 : yypt+1] - //line query_string.y:280 + //line query_string.y:310 { yyVAL.pf = nil boost, err := strconv.ParseFloat(yyDollar[1].s, 64) @@ -768,6 +798,18 @@ yydefault: } logDebugGrammar("BOOST %f", boost) } + case 26: + yyDollar = yyS[yypt-1 : yypt+1] + //line query_string.y:322 + { + yyVAL.s = yyDollar[1].s + } + case 27: + yyDollar = yyS[yypt-2 : yypt+1] + //line query_string.y:326 + { + yyVAL.s = "-" + yyDollar[2].s + } } goto yystack /* stack new state and value */ } diff --git a/vendor/github.com/blevesearch/bleve/search/query/query_string_parser.go b/vendor/github.com/blevesearch/bleve/search/query/query_string_parser.go index eb3b61e9ba..3fb7731b88 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/query_string_parser.go +++ b/vendor/github.com/blevesearch/bleve/search/query/query_string_parser.go @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:generate go tool yacc -o query_string.y.go query_string.y +// as of Go 1.8 this requires the goyacc external tool +// available from golang.org/x/tools/cmd/goyacc + +//go:generate goyacc -o query_string.y.go query_string.y //go:generate sed -i.tmp -e 1d query_string.y.go //go:generate rm query_string.y.go.tmp @@ -31,6 +34,9 @@ var debugParser bool var debugLexer bool func parseQuerySyntax(query string) (rq Query, err error) { + if query == "" { + return NewMatchNoneQuery(), nil + } lex := newLexerWrapper(newQueryStringLex(strings.NewReader(query))) doParse(lex) @@ -66,7 +72,7 @@ type lexerWrapper struct { func newLexerWrapper(lex yyLexer) *lexerWrapper { return &lexerWrapper{ lex: lex, - query: NewBooleanQuery(nil, nil, nil), + query: NewBooleanQueryForQueryString(nil, nil, nil), } } diff --git a/vendor/github.com/blevesearch/bleve/search/query/regexp.go b/vendor/github.com/blevesearch/bleve/search/query/regexp.go index 21d29b71d9..09544fcf1b 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/regexp.go +++ b/vendor/github.com/blevesearch/bleve/search/query/regexp.go @@ -33,7 +33,9 @@ type RegexpQuery struct { // NewRegexpQuery creates a new Query which finds // documents containing terms that match the -// specified regular expression. +// specified regular expression. The regexp pattern +// SHOULD NOT include ^ or $ modifiers, the search +// will only match entire terms even without them. func NewRegexpQuery(regexp string) *RegexpQuery { return &RegexpQuery{ Regexp: regexp, @@ -45,7 +47,7 @@ func (q *RegexpQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *RegexpQuery) Boost() float64{ +func (q *RegexpQuery) Boost() float64 { return q.BoostVal.Value() } @@ -53,11 +55,11 @@ func (q *RegexpQuery) SetField(f string) { q.FieldVal = f } -func (q *RegexpQuery) Field() string{ +func (q *RegexpQuery) Field() string { return q.FieldVal } -func (q *RegexpQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *RegexpQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -67,7 +69,7 @@ func (q *RegexpQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, expl return nil, err } - return searcher.NewRegexpSearcher(i, q.compiled, field, q.BoostVal.Value(), explain) + return searcher.NewRegexpSearcher(i, q.compiled, field, q.BoostVal.Value(), options) } func (q *RegexpQuery) Validate() error { @@ -76,14 +78,14 @@ func (q *RegexpQuery) Validate() error { func (q *RegexpQuery) compile() error { if q.compiled == nil { - // require that pattern be anchored to start and end of term + // require that pattern NOT be anchored to start and end of term actualRegexp := q.Regexp - if !strings.HasPrefix(actualRegexp, "^") { - actualRegexp = "^" + actualRegexp - } - if !strings.HasSuffix(actualRegexp, "$") { - actualRegexp = actualRegexp + "$" + if strings.HasPrefix(actualRegexp, "^") { + actualRegexp = actualRegexp[1:] // remove leading ^ } + // do not attempt to remove trailing $, it's presence is not + // known to interfere with LiteralPrefix() the way ^ does + // and removing $ introduces possible ambiguities with escaped \$, \\$, etc var err error q.compiled, err = regexp.Compile(actualRegexp) if err != nil { diff --git a/vendor/github.com/blevesearch/bleve/search/query/term.go b/vendor/github.com/blevesearch/bleve/search/query/term.go index 0e939ad32a..2eeb5a37a4 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/term.go +++ b/vendor/github.com/blevesearch/bleve/search/query/term.go @@ -40,7 +40,7 @@ func (q *TermQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *TermQuery) Boost() float64{ +func (q *TermQuery) Boost() float64 { return q.BoostVal.Value() } @@ -48,14 +48,14 @@ func (q *TermQuery) SetField(f string) { q.FieldVal = f } -func (q *TermQuery) Field() string{ +func (q *TermQuery) Field() string { return q.FieldVal } -func (q *TermQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *TermQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() } - return searcher.NewTermSearcher(i, q.Term, field, q.BoostVal.Value(), explain) + return searcher.NewTermSearcher(i, q.Term, field, q.BoostVal.Value(), options) } diff --git a/vendor/github.com/blevesearch/bleve/search/query/term_range.go b/vendor/github.com/blevesearch/bleve/search/query/term_range.go new file mode 100644 index 0000000000..8f8ca8444f --- /dev/null +++ b/vendor/github.com/blevesearch/bleve/search/query/term_range.go @@ -0,0 +1,95 @@ +// Copyright (c) 2017 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 query + +import ( + "fmt" + + "github.com/blevesearch/bleve/index" + "github.com/blevesearch/bleve/mapping" + "github.com/blevesearch/bleve/search" + "github.com/blevesearch/bleve/search/searcher" +) + +type TermRangeQuery struct { + Min string `json:"min,omitempty"` + Max string `json:"max,omitempty"` + InclusiveMin *bool `json:"inclusive_min,omitempty"` + InclusiveMax *bool `json:"inclusive_max,omitempty"` + FieldVal string `json:"field,omitempty"` + BoostVal *Boost `json:"boost,omitempty"` +} + +// NewTermRangeQuery creates a new Query for ranges +// of text term values. +// Either, but not both endpoints can be nil. +// The minimum value is inclusive. +// The maximum value is exclusive. +func NewTermRangeQuery(min, max string) *TermRangeQuery { + return NewTermRangeInclusiveQuery(min, max, nil, nil) +} + +// NewTermRangeInclusiveQuery creates a new Query for ranges +// of numeric values. +// Either, but not both endpoints can be nil. +// Control endpoint inclusion with inclusiveMin, inclusiveMax. +func NewTermRangeInclusiveQuery(min, max string, minInclusive, maxInclusive *bool) *TermRangeQuery { + return &TermRangeQuery{ + Min: min, + Max: max, + InclusiveMin: minInclusive, + InclusiveMax: maxInclusive, + } +} + +func (q *TermRangeQuery) SetBoost(b float64) { + boost := Boost(b) + q.BoostVal = &boost +} + +func (q *TermRangeQuery) Boost() float64 { + return q.BoostVal.Value() +} + +func (q *TermRangeQuery) SetField(f string) { + q.FieldVal = f +} + +func (q *TermRangeQuery) Field() string { + return q.FieldVal +} + +func (q *TermRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { + field := q.FieldVal + if q.FieldVal == "" { + field = m.DefaultSearchField() + } + var minTerm []byte + if q.Min != "" { + minTerm = []byte(q.Min) + } + var maxTerm []byte + if q.Max != "" { + maxTerm = []byte(q.Max) + } + return searcher.NewTermRangeSearcher(i, minTerm, maxTerm, q.InclusiveMin, q.InclusiveMax, field, q.BoostVal.Value(), options) +} + +func (q *TermRangeQuery) Validate() error { + if q.Min == "" && q.Min == q.Max { + return fmt.Errorf("term range query must specify min or max") + } + return nil +} diff --git a/vendor/github.com/blevesearch/bleve/search/query/wildcard.go b/vendor/github.com/blevesearch/bleve/search/query/wildcard.go index 51a8cdef18..7fd7482c4d 100644 --- a/vendor/github.com/blevesearch/bleve/search/query/wildcard.go +++ b/vendor/github.com/blevesearch/bleve/search/query/wildcard.go @@ -66,7 +66,7 @@ func (q *WildcardQuery) SetBoost(b float64) { q.BoostVal = &boost } -func (q *WildcardQuery) Boost() float64{ +func (q *WildcardQuery) Boost() float64 { return q.BoostVal.Value() } @@ -74,11 +74,11 @@ func (q *WildcardQuery) SetField(f string) { q.FieldVal = f } -func (q *WildcardQuery) Field() string{ +func (q *WildcardQuery) Field() string { return q.FieldVal } -func (q *WildcardQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *WildcardQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -91,7 +91,7 @@ func (q *WildcardQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, ex } } - return searcher.NewRegexpSearcher(i, q.compiled, field, q.BoostVal.Value(), explain) + return searcher.NewRegexpSearcher(i, q.compiled, field, q.BoostVal.Value(), options) } func (q *WildcardQuery) Validate() error { @@ -101,6 +101,6 @@ func (q *WildcardQuery) Validate() error { } func (q *WildcardQuery) convertToRegexp() (*regexp.Regexp, error) { - regexpString := "^" + wildcardRegexpReplacer.Replace(q.Wildcard) + "$" + regexpString := wildcardRegexpReplacer.Replace(q.Wildcard) return regexp.Compile(regexpString) } diff --git a/vendor/github.com/blevesearch/bleve/search/scorer/scorer_conjunction.go b/vendor/github.com/blevesearch/bleve/search/scorer/scorer_conjunction.go index 0042f73ff4..aad6f9c160 100644 --- a/vendor/github.com/blevesearch/bleve/search/scorer/scorer_conjunction.go +++ b/vendor/github.com/blevesearch/bleve/search/scorer/scorer_conjunction.go @@ -19,26 +19,26 @@ import ( ) type ConjunctionQueryScorer struct { - explain bool + options search.SearcherOptions } -func NewConjunctionQueryScorer(explain bool) *ConjunctionQueryScorer { +func NewConjunctionQueryScorer(options search.SearcherOptions) *ConjunctionQueryScorer { return &ConjunctionQueryScorer{ - explain: explain, + options: options, } } func (s *ConjunctionQueryScorer) Score(ctx *search.SearchContext, constituents []*search.DocumentMatch) *search.DocumentMatch { var sum float64 var childrenExplanations []*search.Explanation - if s.explain { + if s.options.Explain { childrenExplanations = make([]*search.Explanation, len(constituents)) } locations := []search.FieldTermLocationMap{} for i, docMatch := range constituents { sum += docMatch.Score - if s.explain { + if s.options.Explain { childrenExplanations[i] = docMatch.Expl } if docMatch.Locations != nil { @@ -47,7 +47,7 @@ func (s *ConjunctionQueryScorer) Score(ctx *search.SearchContext, constituents [ } newScore := sum var newExpl *search.Explanation - if s.explain { + if s.options.Explain { newExpl = &search.Explanation{Value: sum, Message: "sum of:", Children: childrenExplanations} } diff --git a/vendor/github.com/blevesearch/bleve/search/scorer/scorer_constant.go b/vendor/github.com/blevesearch/bleve/search/scorer/scorer_constant.go index dcaba2b17d..a65a826f2d 100644 --- a/vendor/github.com/blevesearch/bleve/search/scorer/scorer_constant.go +++ b/vendor/github.com/blevesearch/bleve/search/scorer/scorer_constant.go @@ -24,15 +24,15 @@ import ( type ConstantScorer struct { constant float64 boost float64 - explain bool + options search.SearcherOptions queryNorm float64 queryWeight float64 queryWeightExplanation *search.Explanation } -func NewConstantScorer(constant float64, boost float64, explain bool) *ConstantScorer { +func NewConstantScorer(constant float64, boost float64, options search.SearcherOptions) *ConstantScorer { rv := ConstantScorer{ - explain: explain, + options: options, queryWeight: 1.0, constant: constant, boost: boost, @@ -52,7 +52,7 @@ func (s *ConstantScorer) SetQueryNorm(qnorm float64) { // update the query weight s.queryWeight = s.boost * s.queryNorm - if s.explain { + if s.options.Explain { childrenExplanations := make([]*search.Explanation, 2) childrenExplanations[0] = &search.Explanation{ Value: s.boost, @@ -75,7 +75,7 @@ func (s *ConstantScorer) Score(ctx *search.SearchContext, id index.IndexInternal score := s.constant - if s.explain { + if s.options.Explain { scoreExplanation = &search.Explanation{ Value: score, Message: fmt.Sprintf("ConstantScore()"), @@ -85,7 +85,7 @@ func (s *ConstantScorer) Score(ctx *search.SearchContext, id index.IndexInternal // if the query weight isn't 1, multiply if s.queryWeight != 1.0 { score = score * s.queryWeight - if s.explain { + if s.options.Explain { childExplanations := make([]*search.Explanation, 2) childExplanations[0] = s.queryWeightExplanation childExplanations[1] = scoreExplanation @@ -100,7 +100,7 @@ func (s *ConstantScorer) Score(ctx *search.SearchContext, id index.IndexInternal rv := ctx.DocumentMatchPool.Get() rv.IndexInternalID = id rv.Score = score - if s.explain { + if s.options.Explain { rv.Expl = scoreExplanation } diff --git a/vendor/github.com/blevesearch/bleve/search/scorer/scorer_disjunction.go b/vendor/github.com/blevesearch/bleve/search/scorer/scorer_disjunction.go index 30cbac2f5d..184a15d276 100644 --- a/vendor/github.com/blevesearch/bleve/search/scorer/scorer_disjunction.go +++ b/vendor/github.com/blevesearch/bleve/search/scorer/scorer_disjunction.go @@ -21,26 +21,26 @@ import ( ) type DisjunctionQueryScorer struct { - explain bool + options search.SearcherOptions } -func NewDisjunctionQueryScorer(explain bool) *DisjunctionQueryScorer { +func NewDisjunctionQueryScorer(options search.SearcherOptions) *DisjunctionQueryScorer { return &DisjunctionQueryScorer{ - explain: explain, + options: options, } } func (s *DisjunctionQueryScorer) Score(ctx *search.SearchContext, constituents []*search.DocumentMatch, countMatch, countTotal int) *search.DocumentMatch { var sum float64 var childrenExplanations []*search.Explanation - if s.explain { + if s.options.Explain { childrenExplanations = make([]*search.Explanation, len(constituents)) } var locations []search.FieldTermLocationMap for i, docMatch := range constituents { sum += docMatch.Score - if s.explain { + if s.options.Explain { childrenExplanations[i] = docMatch.Expl } if docMatch.Locations != nil { @@ -49,14 +49,14 @@ func (s *DisjunctionQueryScorer) Score(ctx *search.SearchContext, constituents [ } var rawExpl *search.Explanation - if s.explain { + if s.options.Explain { rawExpl = &search.Explanation{Value: sum, Message: "sum of:", Children: childrenExplanations} } coord := float64(countMatch) / float64(countTotal) newScore := sum * coord var newExpl *search.Explanation - if s.explain { + if s.options.Explain { ce := make([]*search.Explanation, 2) ce[0] = rawExpl ce[1] = &search.Explanation{Value: coord, Message: fmt.Sprintf("coord(%d/%d)", countMatch, countTotal)} diff --git a/vendor/github.com/blevesearch/bleve/search/scorer/scorer_term.go b/vendor/github.com/blevesearch/bleve/search/scorer/scorer_term.go index 3a5b1be4d6..b5f46322ca 100644 --- a/vendor/github.com/blevesearch/bleve/search/scorer/scorer_term.go +++ b/vendor/github.com/blevesearch/bleve/search/scorer/scorer_term.go @@ -23,20 +23,20 @@ import ( ) type TermQueryScorer struct { - queryTerm string + queryTerm []byte queryField string queryBoost float64 docTerm uint64 docTotal uint64 idf float64 - explain bool + options search.SearcherOptions idfExplanation *search.Explanation queryNorm float64 queryWeight float64 queryWeightExplanation *search.Explanation } -func NewTermQueryScorer(queryTerm string, queryField string, queryBoost float64, docTotal, docTerm uint64, explain bool) *TermQueryScorer { +func NewTermQueryScorer(queryTerm []byte, queryField string, queryBoost float64, docTotal, docTerm uint64, options search.SearcherOptions) *TermQueryScorer { rv := TermQueryScorer{ queryTerm: queryTerm, queryField: queryField, @@ -44,11 +44,11 @@ func NewTermQueryScorer(queryTerm string, queryField string, queryBoost float64, docTerm: docTerm, docTotal: docTotal, idf: 1.0 + math.Log(float64(docTotal)/float64(docTerm+1.0)), - explain: explain, + options: options, queryWeight: 1.0, } - if explain { + if options.Explain { rv.idfExplanation = &search.Explanation{ Value: rv.idf, Message: fmt.Sprintf("idf(docFreq=%d, maxDocs=%d)", docTerm, docTotal), @@ -69,7 +69,7 @@ func (s *TermQueryScorer) SetQueryNorm(qnorm float64) { // update the query weight s.queryWeight = s.queryBoost * s.idf * s.queryNorm - if s.explain { + if s.options.Explain { childrenExplanations := make([]*search.Explanation, 3) childrenExplanations[0] = &search.Explanation{ Value: s.queryBoost, @@ -100,7 +100,7 @@ func (s *TermQueryScorer) Score(ctx *search.SearchContext, termMatch *index.Term } score := tf * termMatch.Norm * s.idf - if s.explain { + if s.options.Explain { childrenExplanations := make([]*search.Explanation, 3) childrenExplanations[0] = &search.Explanation{ Value: tf, @@ -121,7 +121,7 @@ func (s *TermQueryScorer) Score(ctx *search.SearchContext, termMatch *index.Term // if the query weight isn't 1, multiply if s.queryWeight != 1.0 { score = score * s.queryWeight - if s.explain { + if s.options.Explain { childExplanations := make([]*search.Explanation, 2) childExplanations[0] = s.queryWeightExplanation childExplanations[1] = scoreExplanation @@ -136,44 +136,46 @@ func (s *TermQueryScorer) Score(ctx *search.SearchContext, termMatch *index.Term rv := ctx.DocumentMatchPool.Get() rv.IndexInternalID = append(rv.IndexInternalID, termMatch.ID...) rv.Score = score - if s.explain { + if s.options.Explain { rv.Expl = scoreExplanation } if termMatch.Vectors != nil && len(termMatch.Vectors) > 0 { + locs := make([]search.Location, len(termMatch.Vectors)) + locsUsed := 0 + + totalPositions := 0 + for _, v := range termMatch.Vectors { + totalPositions += len(v.ArrayPositions) + } + positions := make(search.ArrayPositions, totalPositions) + positionsUsed := 0 rv.Locations = make(search.FieldTermLocationMap) for _, v := range termMatch.Vectors { tlm := rv.Locations[v.Field] if tlm == nil { tlm = make(search.TermLocationMap) + rv.Locations[v.Field] = tlm } - loc := search.Location{ - Pos: float64(v.Pos), - Start: float64(v.Start), - End: float64(v.End), - } + loc := &locs[locsUsed] + locsUsed++ + + loc.Pos = v.Pos + loc.Start = v.Start + loc.End = v.End if len(v.ArrayPositions) > 0 { - loc.ArrayPositions = make([]float64, len(v.ArrayPositions)) + loc.ArrayPositions = positions[positionsUsed : positionsUsed+len(v.ArrayPositions)] for i, ap := range v.ArrayPositions { - loc.ArrayPositions[i] = float64(ap) + loc.ArrayPositions[i] = ap } + positionsUsed += len(v.ArrayPositions) } - locations := tlm[s.queryTerm] - if locations == nil { - locations = make(search.Locations, 1) - locations[0] = &loc - } else { - locations = append(locations, &loc) - } - tlm[s.queryTerm] = locations - - rv.Locations[v.Field] = tlm + tlm[string(s.queryTerm)] = append(tlm[string(s.queryTerm)], loc) } - } return rv diff --git a/vendor/github.com/blevesearch/bleve/search/search.go b/vendor/github.com/blevesearch/bleve/search/search.go index e3e0a5b694..cbbcfbfd66 100644 --- a/vendor/github.com/blevesearch/bleve/search/search.go +++ b/vendor/github.com/blevesearch/bleve/search/search.go @@ -21,27 +21,32 @@ import ( "github.com/blevesearch/bleve/index" ) -type Location struct { - Pos float64 `json:"pos"` - Start float64 `json:"start"` - End float64 `json:"end"` - ArrayPositions []float64 `json:"array_positions"` -} +type ArrayPositions []uint64 -// SameArrayElement returns true if two locations are point to -// the same array element -func (l *Location) SameArrayElement(other *Location) bool { - if len(l.ArrayPositions) != len(other.ArrayPositions) { +func (ap ArrayPositions) Equals(other ArrayPositions) bool { + if len(ap) != len(other) { return false } - for i, elem := range l.ArrayPositions { - if other.ArrayPositions[i] != elem { + for i := range ap { + if ap[i] != other[i] { return false } } return true } +type Location struct { + // Pos is the position of the term within the field, starting at 1 + Pos uint64 `json:"pos"` + + // Start and End are the byte offsets of the term in the field + Start uint64 `json:"start"` + End uint64 `json:"end"` + + // ArrayPositions contains the positions of the term within any elements. + ArrayPositions ArrayPositions `json:"array_positions"` +} + type Locations []*Location type TermLocationMap map[string]Locations @@ -69,10 +74,6 @@ type DocumentMatch struct { // fields as float64s and date fields as time.RFC3339 formatted strings. Fields map[string]interface{} `json:"fields,omitempty"` - // as we learn field terms, we can cache important ones for later use - // for example, sorting and building facets need these values - CachedFieldTerms index.FieldTerms `json:"-"` - // if we load the document for this hit, remember it so we dont load again Document *document.Document `json:"-"` @@ -138,6 +139,11 @@ type Searcher interface { DocumentMatchPoolSize() int } +type SearcherOptions struct { + Explain bool + IncludeTermVectors bool +} + // SearchContext represents the context around a single search type SearchContext struct { DocumentMatchPool *DocumentMatchPool diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_boolean.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_boolean.go index 4dbd604bc7..a905c29e50 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_boolean.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_boolean.go @@ -38,14 +38,14 @@ type BooleanSearcher struct { initialized bool } -func NewBooleanSearcher(indexReader index.IndexReader, mustSearcher search.Searcher, shouldSearcher search.Searcher, mustNotSearcher search.Searcher, explain bool) (*BooleanSearcher, error) { +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(explain), + scorer: scorer.NewConjunctionQueryScorer(options), matches: make([]*search.DocumentMatch, 2), } rv.computeQueryNorm() diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_conjunction.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_conjunction.go index 7ec0d2837e..9ab0e7fa4c 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_conjunction.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_conjunction.go @@ -31,10 +31,10 @@ type ConjunctionSearcher struct { maxIDIdx int scorer *scorer.ConjunctionQueryScorer initialized bool - explain bool + options search.SearcherOptions } -func NewConjunctionSearcher(indexReader index.IndexReader, qsearchers []search.Searcher, explain bool) (*ConjunctionSearcher, error) { +func NewConjunctionSearcher(indexReader index.IndexReader, qsearchers []search.Searcher, options search.SearcherOptions) (*ConjunctionSearcher, error) { // build the downstream searchers searchers := make(OrderedSearcherList, len(qsearchers)) for i, searcher := range qsearchers { @@ -45,10 +45,10 @@ func NewConjunctionSearcher(indexReader index.IndexReader, qsearchers []search.S // build our searcher rv := ConjunctionSearcher{ indexReader: indexReader, - explain: explain, + options: options, searchers: searchers, currs: make([]*search.DocumentMatch, len(searchers)), - scorer: scorer.NewConjunctionQueryScorer(explain), + scorer: scorer.NewConjunctionQueryScorer(options), } rv.computeQueryNorm() return &rv, nil diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_disjunction.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_disjunction.go index 1d08803adf..96bd544747 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_disjunction.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_disjunction.go @@ -50,11 +50,22 @@ func tooManyClauses(count int) bool { } func tooManyClausesErr() error { - return fmt.Errorf("TooManyClauses[maxClauseCount is set to %d]", DisjunctionMaxClauseCount) + 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 tooManyClauses(len(qsearchers)) { +func NewDisjunctionSearcher(indexReader index.IndexReader, + qsearchers []search.Searcher, min float64, options search.SearcherOptions) ( + *DisjunctionSearcher, error) { + return newDisjunctionSearcher(indexReader, qsearchers, min, options, + true) +} + +func newDisjunctionSearcher(indexReader index.IndexReader, + qsearchers []search.Searcher, min float64, options search.SearcherOptions, + limit bool) ( + *DisjunctionSearcher, error) { + if limit && tooManyClauses(len(qsearchers)) { return nil, tooManyClausesErr() } // build the downstream searchers @@ -70,7 +81,7 @@ func NewDisjunctionSearcher(indexReader index.IndexReader, qsearchers []search.S searchers: searchers, numSearchers: len(searchers), currs: make([]*search.DocumentMatch, len(searchers)), - scorer: scorer.NewDisjunctionQueryScorer(explain), + scorer: scorer.NewDisjunctionQueryScorer(options), min: int(min), matching: make([]*search.DocumentMatch, len(searchers)), matchingIdxs: make([]int, len(searchers)), @@ -161,7 +172,8 @@ func (s *DisjunctionSearcher) SetQueryNorm(qnorm float64) { } } -func (s *DisjunctionSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) { +func (s *DisjunctionSearcher) Next(ctx *search.SearchContext) ( + *search.DocumentMatch, error) { if !s.initialized { err := s.initSearchers(ctx) if err != nil { @@ -199,7 +211,8 @@ func (s *DisjunctionSearcher) Next(ctx *search.SearchContext) (*search.DocumentM return rv, nil } -func (s *DisjunctionSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) { +func (s *DisjunctionSearcher) Advance(ctx *search.SearchContext, + ID index.IndexInternalID) (*search.DocumentMatch, error) { if !s.initialized { err := s.initSearchers(ctx) if err != nil { diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_docid.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_docid.go index d0794823b0..06351b4a0d 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_docid.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_docid.go @@ -28,13 +28,13 @@ type DocIDSearcher struct { } func NewDocIDSearcher(indexReader index.IndexReader, ids []string, boost float64, - explain bool) (searcher *DocIDSearcher, err error) { + options search.SearcherOptions) (searcher *DocIDSearcher, err error) { reader, err := indexReader.DocIDReaderOnly(ids) if err != nil { return nil, err } - scorer := scorer.NewConstantScorer(1.0, boost, explain) + scorer := scorer.NewConstantScorer(1.0, boost, options) return &DocIDSearcher{ scorer: scorer, reader: reader, diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_filter.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_filter.go new file mode 100644 index 0000000000..219f2ee7eb --- /dev/null +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_filter.go @@ -0,0 +1,88 @@ +// Copyright (c) 2017 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 ( + "github.com/blevesearch/bleve/index" + "github.com/blevesearch/bleve/search" +) + +// FilterFunc defines a function which can filter documents +// returning true means keep the document +// returning false means do not keep the document +type FilterFunc func(d *search.DocumentMatch) bool + +// FilteringSearcher wraps any other searcher, but checks any Next/Advance +// call against the supplied FilterFunc +type FilteringSearcher struct { + child search.Searcher + accept FilterFunc +} + +func NewFilteringSearcher(s search.Searcher, filter FilterFunc) *FilteringSearcher { + return &FilteringSearcher{ + child: s, + accept: filter, + } +} + +func (f *FilteringSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) { + next, err := f.child.Next(ctx) + for next != nil && err == nil { + if f.accept(next) { + return next, nil + } + next, err = f.child.Next(ctx) + } + return nil, err +} + +func (f *FilteringSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) { + adv, err := f.child.Advance(ctx, ID) + if err != nil { + return nil, err + } + if adv == nil { + return nil, nil + } + if f.accept(adv) { + return adv, nil + } + return f.Next(ctx) +} + +func (f *FilteringSearcher) Close() error { + return f.child.Close() +} + +func (f *FilteringSearcher) Weight() float64 { + return f.child.Weight() +} + +func (f *FilteringSearcher) SetQueryNorm(n float64) { + f.child.SetQueryNorm(n) +} + +func (f *FilteringSearcher) Count() uint64 { + return f.child.Count() +} + +func (f *FilteringSearcher) Min() int { + return f.child.Min() +} + +func (f *FilteringSearcher) DocumentMatchPoolSize() int { + return f.child.DocumentMatchPoolSize() +} diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_fuzzy.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_fuzzy.go index c8c405828c..90abaa0a85 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_fuzzy.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_fuzzy.go @@ -19,17 +19,9 @@ import ( "github.com/blevesearch/bleve/search" ) -type FuzzySearcher struct { - indexReader index.IndexReader - term string - prefix int - fuzziness int - field string - explain bool - searcher *DisjunctionSearcher -} - -func NewFuzzySearcher(indexReader index.IndexReader, term string, prefix, fuzziness int, field string, boost float64, explain bool) (*FuzzySearcher, error) { +func NewFuzzySearcher(indexReader index.IndexReader, term string, + prefix, fuzziness int, field string, boost float64, + options search.SearcherOptions) (search.Searcher, error) { // Note: we don't byte slice the term for a prefix because of runes. prefixTerm := "" for i, r := range term { @@ -40,46 +32,18 @@ func NewFuzzySearcher(indexReader index.IndexReader, term string, prefix, fuzzin } } - 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, len(candidateTerms)) - qsearchersClose := func() { - for _, searcher := range qsearchers { - _ = searcher.Close() - } - } - for _, cterm := range candidateTerms { - qsearcher, err := NewTermSearcher(indexReader, cterm, field, boost, explain) - if err != nil { - qsearchersClose() - return nil, err - } - qsearchers = append(qsearchers, qsearcher) - } - - // build disjunction searcher of these ranges - searcher, err := NewDisjunctionSearcher(indexReader, qsearchers, 0, explain) + candidateTerms, err := findFuzzyCandidateTerms(indexReader, term, fuzziness, + field, prefixTerm) if err != nil { - qsearchersClose() return nil, err } - return &FuzzySearcher{ - indexReader: indexReader, - term: term, - prefix: prefix, - fuzziness: fuzziness, - field: field, - explain: explain, - searcher: searcher, - }, nil + return NewMultiTermSearcher(indexReader, candidateTerms, field, + boost, options, true) } -func findFuzzyCandidateTerms(indexReader index.IndexReader, term string, fuzziness int, field, prefixTerm string) (rv []string, err error) { +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 { @@ -108,36 +72,3 @@ func findFuzzyCandidateTerms(indexReader index.IndexReader, term string, fuzzine return rv, err } - -func (s *FuzzySearcher) Count() uint64 { - return s.searcher.Count() -} - -func (s *FuzzySearcher) Weight() float64 { - return s.searcher.Weight() -} - -func (s *FuzzySearcher) SetQueryNorm(qnorm float64) { - s.searcher.SetQueryNorm(qnorm) -} - -func (s *FuzzySearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) { - return s.searcher.Next(ctx) - -} - -func (s *FuzzySearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) { - return s.searcher.Advance(ctx, ID) -} - -func (s *FuzzySearcher) Close() error { - return s.searcher.Close() -} - -func (s *FuzzySearcher) Min() int { - return 0 -} - -func (s *FuzzySearcher) DocumentMatchPoolSize() int { - return s.searcher.DocumentMatchPoolSize() -} diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_geoboundingbox.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_geoboundingbox.go new file mode 100644 index 0000000000..f8b1b4cf7a --- /dev/null +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_geoboundingbox.go @@ -0,0 +1,173 @@ +// Copyright (c) 2017 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 ( + "github.com/blevesearch/bleve/document" + "github.com/blevesearch/bleve/geo" + "github.com/blevesearch/bleve/index" + "github.com/blevesearch/bleve/numeric" + "github.com/blevesearch/bleve/search" +) + +func NewGeoBoundingBoxSearcher(indexReader index.IndexReader, minLon, minLat, + maxLon, maxLat float64, field string, boost float64, + options search.SearcherOptions, checkBoundaries bool) ( + search.Searcher, error) { + + // track list of opened searchers, for cleanup on early exit + var openedSearchers []search.Searcher + cleanupOpenedSearchers := func() { + for _, s := range openedSearchers { + _ = s.Close() + } + } + + // do math to produce list of terms needed for this search + onBoundaryTerms, notOnBoundaryTerms := ComputeGeoRange(0, (geo.GeoBits<<1)-1, + minLon, minLat, maxLon, maxLat, checkBoundaries) + + var onBoundarySearcher search.Searcher + if len(onBoundaryTerms) > 0 { + rawOnBoundarySearcher, err := NewMultiTermSearcherBytes(indexReader, + onBoundaryTerms, field, boost, options, false) + if err != nil { + return nil, err + } + // add filter to check points near the boundary + onBoundarySearcher = NewFilteringSearcher(rawOnBoundarySearcher, + buildRectFilter(indexReader, field, minLon, minLat, maxLon, maxLat)) + openedSearchers = append(openedSearchers, onBoundarySearcher) + } + + var notOnBoundarySearcher search.Searcher + if len(notOnBoundaryTerms) > 0 { + var err error + notOnBoundarySearcher, err = NewMultiTermSearcherBytes(indexReader, + notOnBoundaryTerms, field, boost, options, false) + if err != nil { + cleanupOpenedSearchers() + return nil, err + } + openedSearchers = append(openedSearchers, notOnBoundarySearcher) + } + + if onBoundarySearcher != nil && notOnBoundarySearcher != nil { + rv, err := NewDisjunctionSearcher(indexReader, + []search.Searcher{ + onBoundarySearcher, + notOnBoundarySearcher, + }, + 0, options) + if err != nil { + cleanupOpenedSearchers() + return nil, err + } + return rv, nil + } else if onBoundarySearcher != nil { + return onBoundarySearcher, nil + } else if notOnBoundarySearcher != nil { + return notOnBoundarySearcher, nil + } + + return NewMatchNoneSearcher(indexReader) +} + +var geoMaxShift = document.GeoPrecisionStep * 4 +var geoDetailLevel = ((geo.GeoBits << 1) - geoMaxShift) / 2 + +func ComputeGeoRange(term uint64, shift uint, + sminLon, sminLat, smaxLon, smaxLat float64, + checkBoundaries bool) ( + onBoundary [][]byte, notOnBoundary [][]byte) { + split := term | uint64(0x1)<<shift + var upperMax uint64 + if shift < 63 { + upperMax = term | ((uint64(1) << (shift + 1)) - 1) + } else { + upperMax = 0xffffffffffffffff + } + lowerMax := split - 1 + onBoundary, notOnBoundary = relateAndRecurse(term, lowerMax, shift, + sminLon, sminLat, smaxLon, smaxLat, checkBoundaries) + plusOnBoundary, plusNotOnBoundary := relateAndRecurse(split, upperMax, shift, + sminLon, sminLat, smaxLon, smaxLat, checkBoundaries) + onBoundary = append(onBoundary, plusOnBoundary...) + notOnBoundary = append(notOnBoundary, plusNotOnBoundary...) + return +} + +func relateAndRecurse(start, end uint64, res uint, + sminLon, sminLat, smaxLon, smaxLat float64, + checkBoundaries bool) ( + onBoundary [][]byte, notOnBoundary [][]byte) { + minLon := geo.MortonUnhashLon(start) + minLat := geo.MortonUnhashLat(start) + maxLon := geo.MortonUnhashLon(end) + maxLat := geo.MortonUnhashLat(end) + + level := ((geo.GeoBits << 1) - res) >> 1 + + within := res%document.GeoPrecisionStep == 0 && + geo.RectWithin(minLon, minLat, maxLon, maxLat, + sminLon, sminLat, smaxLon, smaxLat) + if within || (level == geoDetailLevel && + geo.RectIntersects(minLon, minLat, maxLon, maxLat, + sminLon, sminLat, smaxLon, smaxLat)) { + if !within && checkBoundaries { + return [][]byte{ + numeric.MustNewPrefixCodedInt64(int64(start), res), + }, nil + } + return nil, + [][]byte{ + numeric.MustNewPrefixCodedInt64(int64(start), res), + } + } else if level < geoDetailLevel && + geo.RectIntersects(minLon, minLat, maxLon, maxLat, + sminLon, sminLat, smaxLon, smaxLat) { + return ComputeGeoRange(start, res-1, sminLon, sminLat, smaxLon, smaxLat, + checkBoundaries) + } + return nil, nil +} + +func buildRectFilter(indexReader index.IndexReader, field string, + minLon, minLat, maxLon, maxLat float64) FilterFunc { + return func(d *search.DocumentMatch) bool { + var lon, lat float64 + var found bool + err := indexReader.DocumentVisitFieldTerms(d.IndexInternalID, + []string{field}, func(field string, term []byte) { + // only consider the values which are shifted 0 + prefixCoded := numeric.PrefixCoded(term) + shift, err := prefixCoded.Shift() + if err == nil && shift == 0 { + var i64 int64 + i64, err = prefixCoded.Int64() + if err == nil { + lon = geo.MortonUnhashLon(uint64(i64)) + lat = geo.MortonUnhashLat(uint64(i64)) + found = true + } + } + }) + if err == nil && found { + return geo.BoundingBoxContains(lon, lat, + minLon, minLat, maxLon, maxLat) + } + return false + } +} diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_geopointdistance.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_geopointdistance.go new file mode 100644 index 0000000000..fd559766fd --- /dev/null +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_geopointdistance.go @@ -0,0 +1,117 @@ +// Copyright (c) 2017 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 ( + "github.com/blevesearch/bleve/geo" + "github.com/blevesearch/bleve/index" + "github.com/blevesearch/bleve/numeric" + "github.com/blevesearch/bleve/search" +) + +func NewGeoPointDistanceSearcher(indexReader index.IndexReader, centerLon, + centerLat, dist float64, field string, boost float64, + options search.SearcherOptions) (search.Searcher, error) { + // compute bounding box containing the circle + topLeftLon, topLeftLat, bottomRightLon, bottomRightLat, err := + geo.RectFromPointDistance(centerLon, centerLat, dist) + if err != nil { + return nil, err + } + + // build a searcher for the box + boxSearcher, err := boxSearcher(indexReader, + topLeftLon, topLeftLat, bottomRightLon, bottomRightLat, + field, boost, options) + if err != nil { + return nil, err + } + + // wrap it in a filtering searcher which checks the actual distance + return NewFilteringSearcher(boxSearcher, + buildDistFilter(indexReader, field, centerLon, centerLat, dist)), nil +} + +// boxSearcher builds a searcher for the described bounding box +// if the desired box crosses the dateline, it is automatically split into +// two boxes joined through a disjunction searcher +func boxSearcher(indexReader index.IndexReader, + topLeftLon, topLeftLat, bottomRightLon, bottomRightLat float64, + field string, boost float64, options search.SearcherOptions) ( + search.Searcher, error) { + if bottomRightLon < topLeftLon { + // cross date line, rewrite as two parts + + leftSearcher, err := NewGeoBoundingBoxSearcher(indexReader, + -180, bottomRightLat, bottomRightLon, topLeftLat, + field, boost, options, false) + if err != nil { + return nil, err + } + rightSearcher, err := NewGeoBoundingBoxSearcher(indexReader, + topLeftLon, bottomRightLat, 180, topLeftLat, field, boost, options, false) + if err != nil { + _ = leftSearcher.Close() + return nil, err + } + + boxSearcher, err := NewDisjunctionSearcher(indexReader, + []search.Searcher{leftSearcher, rightSearcher}, 0, options) + if err != nil { + _ = leftSearcher.Close() + _ = rightSearcher.Close() + return nil, err + } + return boxSearcher, nil + } + + // build geoboundinggox searcher for that bounding box + boxSearcher, err := NewGeoBoundingBoxSearcher(indexReader, + topLeftLon, bottomRightLat, bottomRightLon, topLeftLat, field, boost, + options, false) + if err != nil { + return nil, err + } + return boxSearcher, nil +} + +func buildDistFilter(indexReader index.IndexReader, field string, + centerLon, centerLat, maxDist float64) FilterFunc { + return func(d *search.DocumentMatch) bool { + var lon, lat float64 + var found bool + err := indexReader.DocumentVisitFieldTerms(d.IndexInternalID, + []string{field}, func(field string, term []byte) { + // only consider the values which are shifted 0 + prefixCoded := numeric.PrefixCoded(term) + shift, err := prefixCoded.Shift() + if err == nil && shift == 0 { + i64, err := prefixCoded.Int64() + if err == nil { + lon = geo.MortonUnhashLon(uint64(i64)) + lat = geo.MortonUnhashLat(uint64(i64)) + found = true + } + } + }) + if err == nil && found { + dist := geo.Haversin(lon, lat, centerLon, centerLat) + if dist <= maxDist/1000 { + return true + } + } + return false + } +} diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_match_all.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_match_all.go index d8163161e7..822db2ea00 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_match_all.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_match_all.go @@ -27,7 +27,7 @@ type MatchAllSearcher struct { count uint64 } -func NewMatchAllSearcher(indexReader index.IndexReader, boost float64, explain bool) (*MatchAllSearcher, error) { +func NewMatchAllSearcher(indexReader index.IndexReader, boost float64, options search.SearcherOptions) (*MatchAllSearcher, error) { reader, err := indexReader.DocIDReaderAll() if err != nil { return nil, err @@ -37,7 +37,7 @@ func NewMatchAllSearcher(indexReader index.IndexReader, boost float64, explain b _ = reader.Close() return nil, err } - scorer := scorer.NewConstantScorer(1.0, boost, explain) + scorer := scorer.NewConstantScorer(1.0, boost, options) return &MatchAllSearcher{ indexReader: indexReader, reader: reader, diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_multi_term.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_multi_term.go new file mode 100644 index 0000000000..b469beadbb --- /dev/null +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_multi_term.go @@ -0,0 +1,85 @@ +// Copyright (c) 2017 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 ( + "github.com/blevesearch/bleve/index" + "github.com/blevesearch/bleve/search" +) + +func NewMultiTermSearcher(indexReader index.IndexReader, terms []string, + field string, boost float64, options search.SearcherOptions, limit bool) ( + search.Searcher, error) { + qsearchers := make([]search.Searcher, len(terms)) + qsearchersClose := func() { + for _, searcher := range qsearchers { + if searcher != nil { + _ = searcher.Close() + } + } + } + for i, term := range terms { + var err error + qsearchers[i], err = NewTermSearcher(indexReader, term, field, boost, options) + if err != nil { + qsearchersClose() + return nil, err + } + } + // build disjunction searcher of these ranges + return newMultiTermSearcherBytes(indexReader, qsearchers, field, boost, + options, limit) +} + +func NewMultiTermSearcherBytes(indexReader index.IndexReader, terms [][]byte, + field string, boost float64, options search.SearcherOptions, limit bool) ( + search.Searcher, error) { + qsearchers := make([]search.Searcher, len(terms)) + qsearchersClose := func() { + for _, searcher := range qsearchers { + if searcher != nil { + _ = searcher.Close() + } + } + } + for i, term := range terms { + var err error + qsearchers[i], err = NewTermSearcherBytes(indexReader, term, field, boost, options) + if err != nil { + qsearchersClose() + return nil, err + } + } + return newMultiTermSearcherBytes(indexReader, qsearchers, field, boost, + options, limit) +} + +func newMultiTermSearcherBytes(indexReader index.IndexReader, + searchers []search.Searcher, field string, boost float64, + options search.SearcherOptions, limit bool) ( + search.Searcher, error) { + + // build disjunction searcher of these ranges + searcher, err := newDisjunctionSearcher(indexReader, searchers, 0, options, + limit) + if err != nil { + for _, s := range searchers { + _ = s.Close() + } + return nil, err + } + + return searcher, nil +} diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_numeric_range.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_numeric_range.go index 03fc0dd22a..7f42d72508 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_numeric_range.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_numeric_range.go @@ -17,22 +17,16 @@ package searcher import ( "bytes" "math" + "sort" "github.com/blevesearch/bleve/index" "github.com/blevesearch/bleve/numeric" "github.com/blevesearch/bleve/search" ) -type NumericRangeSearcher struct { - indexReader index.IndexReader - min *float64 - max *float64 - field string - explain bool - searcher *DisjunctionSearcher -} - -func NewNumericRangeSearcher(indexReader index.IndexReader, min *float64, max *float64, inclusiveMin, inclusiveMax *bool, field string, boost float64, explain bool) (*NumericRangeSearcher, error) { +func NewNumericRangeSearcher(indexReader index.IndexReader, + min *float64, max *float64, inclusiveMin, inclusiveMax *bool, field string, + boost float64, options search.SearcherOptions) (search.Searcher, error) { // account for unbounded edges if min == nil { negInf := math.Inf(-1) @@ -62,64 +56,49 @@ 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)) - qsearchersClose := func() { - for _, searcher := range qsearchers { - if searcher != nil { - _ = searcher.Close() - } - } - } - for i, term := range terms { - var err error - qsearchers[i], err = NewTermSearcher(indexReader, string(term), field, boost, explain) - if err != nil { - qsearchersClose() - return nil, err - } - } - // build disjunction searcher of these ranges - searcher, err := NewDisjunctionSearcher(indexReader, qsearchers, 0, explain) + if len(terms) < 1 { + // cannot return MatchNoneSearcher because of interaction with + // commit f391b991c20f02681bacd197afc6d8aed444e132 + return NewMultiTermSearcherBytes(indexReader, terms, field, boost, options, + true) + } + var err error + terms, err = filterCandidateTerms(indexReader, terms, field) if err != nil { - qsearchersClose() return nil, err } - return &NumericRangeSearcher{ - indexReader: indexReader, - min: min, - max: max, - field: field, - explain: explain, - searcher: searcher, - }, nil -} - -func (s *NumericRangeSearcher) Count() uint64 { - return s.searcher.Count() -} + if tooManyClauses(len(terms)) { + return nil, tooManyClausesErr() + } -func (s *NumericRangeSearcher) Weight() float64 { - return s.searcher.Weight() + return NewMultiTermSearcherBytes(indexReader, terms, field, boost, options, + true) } -func (s *NumericRangeSearcher) SetQueryNorm(qnorm float64) { - s.searcher.SetQueryNorm(qnorm) -} +func filterCandidateTerms(indexReader index.IndexReader, + terms [][]byte, field string) (rv [][]byte, err error) { + fieldDict, err := indexReader.FieldDictRange(field, terms[0], terms[len(terms)-1]) + if err != nil { + return nil, err + } -func (s *NumericRangeSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) { - return s.searcher.Next(ctx) -} + // enumerate the terms and check against list of terms + tfd, err := fieldDict.Next() + for err == nil && tfd != nil { + termBytes := []byte(tfd.Term) + i := sort.Search(len(terms), func(i int) bool { return bytes.Compare(terms[i], termBytes) >= 0 }) + if i < len(terms) && bytes.Compare(terms[i], termBytes) == 0 { + rv = append(rv, terms[i]) + } + terms = terms[i:] + tfd, err = fieldDict.Next() + } -func (s *NumericRangeSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) { - return s.searcher.Advance(ctx, ID) -} + if cerr := fieldDict.Close(); cerr != nil && err == nil { + err = cerr + } -func (s *NumericRangeSearcher) Close() error { - return s.searcher.Close() + return rv, err } type termRange struct { @@ -190,7 +169,8 @@ func splitInt64Range(minBound, maxBound int64, precisionStep uint) termRanges { lowerWrapped := nextMinBound < minBound upperWrapped := nextMaxBound > maxBound - if shift+precisionStep >= 64 || nextMinBound > nextMaxBound || lowerWrapped || upperWrapped { + if shift+precisionStep >= 64 || nextMinBound > nextMaxBound || + lowerWrapped || upperWrapped { // We are in the lowest precision or the next precision is not available. rv = append(rv, newRange(minBound, maxBound, shift)) // exit the split recursion loop @@ -225,11 +205,3 @@ func newRangeBytes(minBytes, maxBytes []byte) *termRange { endTerm: maxBytes, } } - -func (s *NumericRangeSearcher) Min() int { - return 0 -} - -func (s *NumericRangeSearcher) DocumentMatchPoolSize() int { - return s.searcher.DocumentMatchPoolSize() -} diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_phrase.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_phrase.go index cab87ba75b..e3fa0895d7 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_phrase.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_phrase.go @@ -15,6 +15,7 @@ package searcher import ( + "fmt" "math" "github.com/blevesearch/bleve/index" @@ -27,11 +28,72 @@ type PhraseSearcher struct { queryNorm float64 currMust *search.DocumentMatch slop int - terms []string + terms [][]string initialized bool } -func NewPhraseSearcher(indexReader index.IndexReader, mustSearcher *ConjunctionSearcher, terms []string) (*PhraseSearcher, error) { +func NewPhraseSearcher(indexReader index.IndexReader, terms []string, field string, options search.SearcherOptions) (*PhraseSearcher, error) { + // turn flat terms []string into [][]string + mterms := make([][]string, len(terms)) + for i, term := range terms { + mterms[i] = []string{term} + } + return NewMultiPhraseSearcher(indexReader, mterms, field, options) +} + +func NewMultiPhraseSearcher(indexReader index.IndexReader, terms [][]string, field string, options search.SearcherOptions) (*PhraseSearcher, error) { + options.IncludeTermVectors = true + var termPositionSearchers []search.Searcher + for _, termPos := range terms { + if len(termPos) == 1 && termPos[0] != "" { + // single term + ts, err := NewTermSearcher(indexReader, termPos[0], field, 1.0, options) + if err != nil { + // close any searchers already opened + for _, ts := range termPositionSearchers { + _ = ts.Close() + } + return nil, fmt.Errorf("phrase searcher error building term searcher: %v", err) + } + termPositionSearchers = append(termPositionSearchers, ts) + } else if len(termPos) > 1 { + // multiple terms + var termSearchers []search.Searcher + for _, term := range termPos { + if term == "" { + continue + } + ts, err := NewTermSearcher(indexReader, term, field, 1.0, options) + if err != nil { + // close any searchers already opened + for _, ts := range termPositionSearchers { + _ = ts.Close() + } + return nil, fmt.Errorf("phrase searcher error building term searcher: %v", err) + } + termSearchers = append(termSearchers, ts) + } + disjunction, err := NewDisjunctionSearcher(indexReader, termSearchers, 1, options) + if err != nil { + // close any searchers already opened + for _, ts := range termPositionSearchers { + _ = ts.Close() + } + return nil, fmt.Errorf("phrase searcher error building term position disjunction searcher: %v", err) + } + termPositionSearchers = append(termPositionSearchers, disjunction) + } + } + + mustSearcher, err := NewConjunctionSearcher(indexReader, termPositionSearchers, options) + if err != nil { + // close any searchers already opened + for _, ts := range termPositionSearchers { + _ = ts.Close() + } + return nil, fmt.Errorf("phrase searcher error building conjunction searcher: %v", err) + } + // build our searcher rv := PhraseSearcher{ indexReader: indexReader, @@ -96,68 +158,150 @@ func (s *PhraseSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, } } - var rv *search.DocumentMatch for s.currMust != nil { - rvftlm := make(search.FieldTermLocationMap, 0) - freq := 0 - firstTerm := s.terms[0] - for field, termLocMap := range s.currMust.Locations { - rvtlm := make(search.TermLocationMap, 0) - locations, ok := termLocMap[firstTerm] - if ok { - OUTER: - for _, location := range locations { - crvtlm := make(search.TermLocationMap, 0) - INNER: - for i := 0; i < len(s.terms); i++ { - nextTerm := s.terms[i] - if nextTerm != "" { - // look through all these term locations - // to try and find the correct offsets - nextLocations, ok := termLocMap[nextTerm] - if ok { - for _, nextLocation := range nextLocations { - if nextLocation.Pos == location.Pos+float64(i) && nextLocation.SameArrayElement(location) { - // found a location match for this term - crvtlm.AddLocation(nextTerm, nextLocation) - continue INNER - } - } - // if we got here we didn't find a location match for this term - continue OUTER - } else { - continue OUTER - } - } - } - // if we got here all the terms matched - freq++ - search.MergeTermLocationMaps(rvtlm, crvtlm) - rvftlm[field] = rvtlm - } - } - } - - if freq > 0 { - // return match - rv = s.currMust - rv.Locations = rvftlm - err := s.advanceNextMust(ctx) - if err != nil { - return nil, err - } - return rv, nil - } + // check this match against phrase constraints + rv := s.checkCurrMustMatch(ctx) + // prepare for next iteration (either loop or subsequent call to Next()) err := s.advanceNextMust(ctx) if err != nil { return nil, err } + + // if match satisfied phrase constraints return it as a hit + if rv != nil { + return rv, nil + } } return nil, nil } +// checkCurrMustMatch is soley concerned with determining if the DocumentMatch +// pointed to by s.currMust (which satisifies the pre-condition searcher) +// also satisfies the phase constraints. if so, it returns a DocumentMatch +// for this document, otherwise nil +func (s *PhraseSearcher) checkCurrMustMatch(ctx *search.SearchContext) *search.DocumentMatch { + rvftlm := make(search.FieldTermLocationMap, 0) + freq := 0 + // typically we would expect there to only actually be results in + // one field, but we allow for this to not be the case + // but, we note that phrase constraints can only be satisfied within + // a single field, so we can check them each independently + for field, tlm := range s.currMust.Locations { + + f, rvtlm := s.checkCurrMustMatchField(ctx, tlm) + if f > 0 { + freq += f + rvftlm[field] = rvtlm + } + } + + if freq > 0 { + // return match + rv := s.currMust + rv.Locations = rvftlm + return rv + } + + return nil +} + +// checkCurrMustMatchField is soley concerned with determining if one particular +// field within the currMust DocumentMatch Locations satisfies the phase +// constraints (possibly more than once). if so, the number of times it was +// satisfied, and these locations are returned. otherwise 0 and either +// a nil or empty TermLocationMap +func (s *PhraseSearcher) checkCurrMustMatchField(ctx *search.SearchContext, tlm search.TermLocationMap) (int, search.TermLocationMap) { + paths := findPhrasePaths(0, nil, s.terms, tlm, nil, 0) + rv := make(search.TermLocationMap, len(s.terms)) + for _, p := range paths { + p.MergeInto(rv) + } + return len(paths), rv +} + +type phrasePart struct { + term string + loc *search.Location +} + +type phrasePath []*phrasePart + +func (p phrasePath) MergeInto(in search.TermLocationMap) { + for _, pp := range p { + in[pp.term] = append(in[pp.term], pp.loc) + } +} + +// findPhrasePaths is a function to identify phase matches from a set of known +// term locations. the implementation is recursive, so care must be taken +// with arguments and return values. +// +// prev - the previous location, nil on first invocation +// phraseTerms - slice containing the phrase terms themselves +// may contain empty string as placeholder (don't care) +// tlm - the Term Location Map containing all relevant term locations +// offset - the offset from the previous that this next term must match +// p - the current path being explored (appended to in recursive calls) +// this is the primary state being built during the traversal +// +// returns slice of paths, or nil if invocation did not find any successul paths +func findPhrasePaths(prevPos uint64, ap search.ArrayPositions, phraseTerms [][]string, tlm search.TermLocationMap, p phrasePath, remainingSlop int) []phrasePath { + + // no more terms + if len(phraseTerms) < 1 { + return []phrasePath{p} + } + + car := phraseTerms[0] + cdr := phraseTerms[1:] + + // empty term is treated as match (continue) + if len(car) == 0 || (len(car) == 1 && car[0] == "") { + nextPos := prevPos + 1 + if prevPos == 0 { + // if prevPos was 0, don't set it to 1 (as thats not a real abs pos) + nextPos = 0 // don't advance nextPos if prevPos was 0 + } + return findPhrasePaths(nextPos, ap, cdr, tlm, p, remainingSlop) + } + + var rv []phrasePath + // locations for this term + for _, carTerm := range car { + locations := tlm[carTerm] + for _, loc := range locations { + if prevPos != 0 && !loc.ArrayPositions.Equals(ap) { + // if the array positions are wrong, can't match, try next location + continue + } + + // compute distance from previous phrase term + dist := 0 + if prevPos != 0 { + dist = editDistance(prevPos+1, loc.Pos) + } + + // if enough slop reamining, continue recursively + if prevPos == 0 || (remainingSlop-dist) >= 0 { + // this location works, add it to the path (but not for empty term) + px := append(p, &phrasePart{term: carTerm, loc: loc}) + rv = append(rv, findPhrasePaths(loc.Pos, loc.ArrayPositions, cdr, tlm, px, remainingSlop-dist)...) + } + } + } + return rv +} + +func editDistance(p1, p2 uint64) int { + dist := int(p1 - p2) + if dist < 0 { + return -dist + } + return dist +} + func (s *PhraseSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) { if !s.initialized { err := s.initSearchers(ctx) diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_regexp.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_regexp.go index ceded66d01..b7cf520ac1 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_regexp.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_regexp.go @@ -21,7 +21,14 @@ import ( "github.com/blevesearch/bleve/search" ) -func NewRegexpSearcher(indexReader index.IndexReader, pattern *regexp.Regexp, field string, boost float64, explain bool) (search.Searcher, error) { +// NewRegexpSearcher creates a searcher which will match documents that +// contain terms which match the pattern regexp. The match must be EXACT +// matching the entire term. The provided regexp SHOULD NOT start with ^ +// or end with $ as this can intefere with the implementation. Separately, +// matches will be checked to ensure they match the entire term. +func NewRegexpSearcher(indexReader index.IndexReader, pattern *regexp.Regexp, + field string, boost float64, options search.SearcherOptions) ( + search.Searcher, error) { prefixTerm, complete := pattern.LiteralPrefix() var candidateTerms []string @@ -30,39 +37,19 @@ func NewRegexpSearcher(indexReader index.IndexReader, pattern *regexp.Regexp, fi candidateTerms = []string{prefixTerm} } else { var err error - candidateTerms, err = findRegexpCandidateTerms(indexReader, pattern, field, prefixTerm) + 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, len(candidateTerms)) - qsearchersClose := func() { - for _, searcher := range qsearchers { - _ = searcher.Close() - } - } - for _, cterm := range candidateTerms { - qsearcher, err := NewTermSearcher(indexReader, cterm, field, boost, explain) - if err != nil { - qsearchersClose() - return nil, err - } - qsearchers = append(qsearchers, qsearcher) - } - - // build disjunction searcher of these ranges - searcher, err := NewDisjunctionSearcher(indexReader, qsearchers, 0, explain) - if err != nil { - qsearchersClose() - return nil, err - } - - return searcher, err + return NewMultiTermSearcher(indexReader, candidateTerms, field, boost, + options, true) } -func findRegexpCandidateTerms(indexReader index.IndexReader, pattern *regexp.Regexp, field, prefixTerm string) (rv []string, err error) { +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 { @@ -79,7 +66,8 @@ func findRegexpCandidateTerms(indexReader index.IndexReader, pattern *regexp.Reg // enumerate the terms and check against regexp tfd, err := fieldDict.Next() for err == nil && tfd != nil { - if pattern.MatchString(tfd.Term) { + matchPos := pattern.FindStringIndex(tfd.Term) + if matchPos != nil && matchPos[0] == 0 && matchPos[1] == len(tfd.Term) { rv = append(rv, tfd.Term) if tooManyClauses(len(rv)) { return rv, tooManyClausesErr() diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_term.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_term.go index 73b0a1a562..6fae6ae5ae 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_term.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_term.go @@ -22,16 +22,13 @@ import ( type TermSearcher struct { indexReader index.IndexReader - term string - field string reader index.TermFieldReader scorer *scorer.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, true, true, true) +func NewTermSearcher(indexReader index.IndexReader, term string, field string, boost float64, options search.SearcherOptions) (*TermSearcher, error) { + reader, err := indexReader.TermFieldReader([]byte(term), field, true, true, options.IncludeTermVectors) if err != nil { return nil, err } @@ -40,12 +37,27 @@ func NewTermSearcher(indexReader index.IndexReader, term string, field string, b _ = reader.Close() return nil, err } - scorer := scorer.NewTermQueryScorer(term, field, boost, count, reader.Count(), explain) + scorer := scorer.NewTermQueryScorer([]byte(term), field, boost, count, reader.Count(), options) + return &TermSearcher{ + indexReader: indexReader, + reader: reader, + scorer: scorer, + }, nil +} + +func NewTermSearcherBytes(indexReader index.IndexReader, term []byte, field string, boost float64, options search.SearcherOptions) (*TermSearcher, error) { + reader, err := indexReader.TermFieldReader(term, field, true, true, options.IncludeTermVectors) + if err != nil { + return nil, err + } + count, err := indexReader.DocCount() + if err != nil { + _ = reader.Close() + return nil, err + } + scorer := scorer.NewTermQueryScorer(term, field, boost, count, reader.Count(), options) return &TermSearcher{ indexReader: indexReader, - term: term, - field: field, - explain: explain, reader: reader, scorer: scorer, }, nil diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_term_prefix.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_term_prefix.go index b666d3b72a..05d092249a 100644 --- a/vendor/github.com/blevesearch/bleve/search/searcher/search_term_prefix.go +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_term_prefix.go @@ -19,93 +19,21 @@ import ( "github.com/blevesearch/bleve/search" ) -type TermPrefixSearcher struct { - indexReader index.IndexReader - prefix string - field string - explain bool - searcher *DisjunctionSearcher -} - -func NewTermPrefixSearcher(indexReader index.IndexReader, prefix string, field string, boost float64, explain bool) (*TermPrefixSearcher, error) { +func NewTermPrefixSearcher(indexReader index.IndexReader, prefix string, + field string, boost float64, options search.SearcherOptions) ( + search.Searcher, error) { // find the terms with this prefix fieldDict, err := indexReader.FieldDictPrefix(field, []byte(prefix)) if err != nil { return nil, err } - // enumerate all the terms in the range - qsearchers := make([]search.Searcher, 0, 25) - qsearchersClose := func() { - for _, searcher := range qsearchers { - _ = searcher.Close() - } - } - + var terms []string tfd, err := fieldDict.Next() for err == nil && tfd != nil { - var qsearcher *TermSearcher - qsearcher, err = NewTermSearcher(indexReader, string(tfd.Term), field, 1.0, explain) - if err != nil { - qsearchersClose() - _ = fieldDict.Close() - return nil, err - } - qsearchers = append(qsearchers, qsearcher) + terms = append(terms, tfd.Term) tfd, err = fieldDict.Next() } - err = fieldDict.Close() - if err != nil { - qsearchersClose() - return nil, err - } - - // build disjunction searcher of these ranges - searcher, err := NewDisjunctionSearcher(indexReader, qsearchers, 0, explain) - if err != nil { - qsearchersClose() - return nil, err - } - - return &TermPrefixSearcher{ - indexReader: indexReader, - prefix: prefix, - field: field, - explain: explain, - searcher: searcher, - }, nil -} - -func (s *TermPrefixSearcher) Count() uint64 { - return s.searcher.Count() -} - -func (s *TermPrefixSearcher) Weight() float64 { - return s.searcher.Weight() -} - -func (s *TermPrefixSearcher) SetQueryNorm(qnorm float64) { - s.searcher.SetQueryNorm(qnorm) -} - -func (s *TermPrefixSearcher) Next(ctx *search.SearchContext) (*search.DocumentMatch, error) { - return s.searcher.Next(ctx) - -} - -func (s *TermPrefixSearcher) Advance(ctx *search.SearchContext, ID index.IndexInternalID) (*search.DocumentMatch, error) { - return s.searcher.Advance(ctx, ID) -} - -func (s *TermPrefixSearcher) Close() error { - return s.searcher.Close() -} - -func (s *TermPrefixSearcher) Min() int { - return 0 -} - -func (s *TermPrefixSearcher) DocumentMatchPoolSize() int { - return s.searcher.DocumentMatchPoolSize() + return NewMultiTermSearcher(indexReader, terms, field, boost, options, true) } diff --git a/vendor/github.com/blevesearch/bleve/search/searcher/search_term_range.go b/vendor/github.com/blevesearch/bleve/search/searcher/search_term_range.go new file mode 100644 index 0000000000..267c681b47 --- /dev/null +++ b/vendor/github.com/blevesearch/bleve/search/searcher/search_term_range.go @@ -0,0 +1,79 @@ +// Copyright (c) 2017 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 ( + "github.com/blevesearch/bleve/index" + "github.com/blevesearch/bleve/search" +) + +func NewTermRangeSearcher(indexReader index.IndexReader, + min, max []byte, inclusiveMin, inclusiveMax *bool, field string, + boost float64, options search.SearcherOptions) (search.Searcher, error) { + + if inclusiveMin == nil { + defaultInclusiveMin := true + inclusiveMin = &defaultInclusiveMin + } + if inclusiveMax == nil { + defaultInclusiveMax := false + inclusiveMax = &defaultInclusiveMax + } + + if min == nil { + min = []byte{} + } + + rangeMax := max + if rangeMax != nil { + // the term dictionary range end has an unfortunate implementation + rangeMax = append(rangeMax, 0) + } + + // find the terms with this prefix + fieldDict, err := indexReader.FieldDictRange(field, min, rangeMax) + if err != nil { + return nil, err + } + + var terms []string + tfd, err := fieldDict.Next() + for err == nil && tfd != nil { + terms = append(terms, tfd.Term) + tfd, err = fieldDict.Next() + } + if err != nil { + return nil, err + } + + if len(terms) < 1 { + return NewMatchNoneSearcher(indexReader) + } + + if !*inclusiveMin && min != nil && string(min) == terms[0] { + terms = terms[1:] + // check again, as we might have removed only entry + if len(terms) < 1 { + return NewMatchNoneSearcher(indexReader) + } + } + + // if our term list included the max, it would be the last item + if !*inclusiveMax && max != nil && string(max) == terms[len(terms)-1] { + terms = terms[:len(terms)-1] + } + + return NewMultiTermSearcher(indexReader, terms, field, boost, options, true) +} diff --git a/vendor/github.com/blevesearch/bleve/search/sort.go b/vendor/github.com/blevesearch/bleve/search/sort.go index 51c18e6ec9..28705d369e 100644 --- a/vendor/github.com/blevesearch/bleve/search/sort.go +++ b/vendor/github.com/blevesearch/bleve/search/sort.go @@ -17,9 +17,11 @@ package search import ( "encoding/json" "fmt" + "math" "sort" "strings" + "github.com/blevesearch/bleve/geo" "github.com/blevesearch/bleve/numeric" ) @@ -27,12 +29,15 @@ var HighTerm = strings.Repeat(string([]byte{0xff}), 10) var LowTerm = string([]byte{0x00}) type SearchSort interface { + UpdateVisitor(field string, term []byte) Value(a *DocumentMatch) string Descending() bool RequiresDocID() bool RequiresScoring() bool RequiresFields() []string + + Copy() SearchSort } func ParseSearchSortObj(input map[string]interface{}) (SearchSort, error) { @@ -50,6 +55,31 @@ func ParseSearchSortObj(input map[string]interface{}) (SearchSort, error) { return &SortScore{ Desc: descending, }, nil + case "geo_distance": + field, ok := input["field"].(string) + if !ok { + return nil, fmt.Errorf("search sort mode geo_distance must specify field") + } + lon, lat, foundLocation := geo.ExtractGeoPoint(input["location"]) + if !foundLocation { + return nil, fmt.Errorf("unable to parse geo_distance location") + } + rvd := &SortGeoDistance{ + Field: field, + Desc: descending, + Lon: lon, + Lat: lat, + unitMult: 1.0, + } + if distUnit, ok := input["unit"].(string); ok { + var err error + rvd.unitMult, err = geo.ParseDistanceUnit(distUnit) + if err != nil { + return nil, err + } + rvd.Unit = distUnit + } + return rvd, nil case "field": field, ok := input["field"].(string) if !ok { @@ -171,6 +201,20 @@ func (so SortOrder) Value(doc *DocumentMatch) { } } +func (so SortOrder) UpdateVisitor(field string, term []byte) { + for _, soi := range so { + soi.UpdateVisitor(field, term) + } +} + +func (so SortOrder) Copy() SortOrder { + rv := make(SortOrder, len(so)) + for i, soi := range so { + rv[i] = soi.Copy() + } + return rv +} + // Compare will compare two document matches using the specified sort order // if both are numbers, we avoid converting back to term func (so SortOrder) Compare(cachedScoring, cachedDesc []bool, i, j *DocumentMatch) int { @@ -300,13 +344,24 @@ type SortField struct { Type SortFieldType Mode SortFieldMode Missing SortFieldMissing + values []string +} + +// UpdateVisitor notifies this sort field that in this document +// this field has the specified term +func (s *SortField) UpdateVisitor(field string, term []byte) { + if field == s.Field { + s.values = append(s.values, string(term)) + } } // Value returns the sort value of the DocumentMatch +// it also resets the state of this SortField for +// processing the next document func (s *SortField) Value(i *DocumentMatch) string { - iTerms := i.CachedFieldTerms[s.Field] - iTerms = s.filterTermsByType(iTerms) + iTerms := s.filterTermsByType(s.values) iTerm := s.filterTermsByMode(iTerms) + s.values = nil return iTerm } @@ -368,7 +423,7 @@ func (s *SortField) filterTermsByType(terms []string) []string { for _, term := range terms { valid, shift := numeric.ValidPrefixCodedTerm(term) if valid && shift == 0 { - termsWithShiftZero = append(termsWithShiftZero) + termsWithShiftZero = append(termsWithShiftZero, term) } } terms = termsWithShiftZero @@ -430,11 +485,23 @@ func (s *SortField) MarshalJSON() ([]byte, error) { return json.Marshal(sfm) } +func (s *SortField) Copy() SearchSort { + var rv SortField + rv = *s + return &rv +} + // SortDocID will sort results by the document identifier type SortDocID struct { Desc bool } +// UpdateVisitor is a no-op for SortDocID as it's value +// is not dependent on any field terms +func (s *SortDocID) UpdateVisitor(field string, term []byte) { + +} + // Value returns the sort value of the DocumentMatch func (s *SortDocID) Value(i *DocumentMatch) string { return i.ID @@ -461,11 +528,23 @@ func (s *SortDocID) MarshalJSON() ([]byte, error) { return json.Marshal("_id") } +func (s *SortDocID) Copy() SearchSort { + var rv SortDocID + rv = *s + return &rv +} + // SortScore will sort results by the document match score type SortScore struct { Desc bool } +// UpdateVisitor is a no-op for SortScore as it's value +// is not dependent on any field terms +func (s *SortScore) UpdateVisitor(field string, term []byte) { + +} + // Value returns the sort value of the DocumentMatch func (s *SortScore) Value(i *DocumentMatch) string { return "_score" @@ -491,3 +570,142 @@ func (s *SortScore) MarshalJSON() ([]byte, error) { } return json.Marshal("_score") } + +func (s *SortScore) Copy() SearchSort { + var rv SortScore + rv = *s + return &rv +} + +var maxDistance = string(numeric.MustNewPrefixCodedInt64(math.MaxInt64, 0)) + +// NewSortGeoDistance creates SearchSort instance for sorting documents by +// their distance from the specified point. +func NewSortGeoDistance(field, unit string, lon, lat float64, desc bool) ( + *SortGeoDistance, error) { + + rv := &SortGeoDistance{ + Field: field, + Desc: desc, + Unit: unit, + Lon: lon, + Lat: lat, + } + var err error + rv.unitMult, err = geo.ParseDistanceUnit(unit) + if err != nil { + return nil, err + } + return rv, nil +} + +// SortGeoDistance will sort results by the distance of an +// indexed geo point, from the provided location. +// Field is the name of the field +// Descending reverse the sort order (default false) +type SortGeoDistance struct { + Field string + Desc bool + Unit string + values []string + Lon float64 + Lat float64 + unitMult float64 +} + +// UpdateVisitor notifies this sort field that in this document +// this field has the specified term +func (s *SortGeoDistance) UpdateVisitor(field string, term []byte) { + if field == s.Field { + s.values = append(s.values, string(term)) + } +} + +// Value returns the sort value of the DocumentMatch +// it also resets the state of this SortField for +// processing the next document +func (s *SortGeoDistance) Value(i *DocumentMatch) string { + iTerms := s.filterTermsByType(s.values) + iTerm := s.filterTermsByMode(iTerms) + s.values = nil + + if iTerm == "" { + return maxDistance + } + + i64, err := numeric.PrefixCoded(iTerm).Int64() + if err != nil { + return maxDistance + } + docLon := geo.MortonUnhashLon(uint64(i64)) + docLat := geo.MortonUnhashLat(uint64(i64)) + + dist := geo.Haversin(s.Lon, s.Lat, docLon, docLat) + // dist is returned in km, so convert to m + dist *= 1000 + if s.unitMult != 0 { + dist /= s.unitMult + } + distInt64 := numeric.Float64ToInt64(dist) + return string(numeric.MustNewPrefixCodedInt64(distInt64, 0)) +} + +// Descending determines the order of the sort +func (s *SortGeoDistance) Descending() bool { + return s.Desc +} + +func (s *SortGeoDistance) filterTermsByMode(terms []string) string { + if len(terms) >= 1 { + return terms[0] + } + + return "" +} + +// filterTermsByType attempts to make one pass on the terms +// return only valid prefix coded numbers with shift of 0 +func (s *SortGeoDistance) filterTermsByType(terms []string) []string { + var termsWithShiftZero []string + for _, term := range terms { + valid, shift := numeric.ValidPrefixCodedTerm(term) + if valid && shift == 0 { + termsWithShiftZero = append(termsWithShiftZero, term) + } + } + return termsWithShiftZero +} + +// RequiresDocID says this SearchSort does not require the DocID be loaded +func (s *SortGeoDistance) RequiresDocID() bool { return false } + +// RequiresScoring says this SearchStore does not require scoring +func (s *SortGeoDistance) RequiresScoring() bool { return false } + +// RequiresFields says this SearchStore requires the specified stored field +func (s *SortGeoDistance) RequiresFields() []string { return []string{s.Field} } + +func (s *SortGeoDistance) MarshalJSON() ([]byte, error) { + sfm := map[string]interface{}{ + "by": "geo_distance", + "field": s.Field, + "location": map[string]interface{}{ + "lon": s.Lon, + "lat": s.Lat, + }, + } + if s.Unit != "" { + sfm["unit"] = s.Unit + } + if s.Desc { + sfm["desc"] = true + } + + return json.Marshal(sfm) +} + +func (s *SortGeoDistance) Copy() SearchSort { + var rv SortGeoDistance + rv = *s + return &rv +} |