major refactor of search package
this started initially to relocate highlighting into a self contained package, which would then also use the registry however, it turned into a much larger refactor in order to avoid cyclic imports now facets, searchers, scorers and collectors are also broken out into subpackages of search
This commit is contained in:
parent
209f808722
commit
2ee7289bc8
49
config.go
49
config.go
|
@ -11,9 +11,18 @@ package bleve
|
|||
|
||||
import (
|
||||
"expvar"
|
||||
"github.com/blevesearch/bleve/registry"
|
||||
"time"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
// fragment formatters
|
||||
_ "github.com/blevesearch/bleve/search/highlight/fragment_formatters/ansi"
|
||||
_ "github.com/blevesearch/bleve/search/highlight/fragment_formatters/html"
|
||||
|
||||
// fragmenters
|
||||
_ "github.com/blevesearch/bleve/search/highlight/fragmenters/simple"
|
||||
|
||||
// highlighters
|
||||
_ "github.com/blevesearch/bleve/search/highlight/highlighters/simple"
|
||||
|
||||
// char filters
|
||||
_ "github.com/blevesearch/bleve/analysis/char_filters/html_char_filter"
|
||||
|
@ -89,21 +98,15 @@ import (
|
|||
|
||||
var bleveExpVar = expvar.NewMap("bleve")
|
||||
|
||||
type HighlightConfig struct {
|
||||
Highlighters map[string]search.Highlighter
|
||||
}
|
||||
|
||||
type configuration struct {
|
||||
Highlight *HighlightConfig
|
||||
DefaultHighlighter *string
|
||||
Cache *registry.Cache
|
||||
DefaultHighlighter string
|
||||
DefaultKVStore string
|
||||
}
|
||||
|
||||
func newConfiguration() *configuration {
|
||||
return &configuration{
|
||||
Highlight: &HighlightConfig{
|
||||
Highlighters: make(map[string]search.Highlighter),
|
||||
},
|
||||
Cache: registry.NewCache(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,18 +119,26 @@ func init() {
|
|||
// build the default configuration
|
||||
Config = newConfiguration()
|
||||
|
||||
// register ansi highlighter
|
||||
Config.Highlight.Highlighters["ansi"] = search.NewSimpleHighlighter()
|
||||
Config.Cache.DefineFragmentFormatter("highlightSpanHTML", "html",
|
||||
map[string]interface{}{
|
||||
"before": `<span class="highlight">`,
|
||||
"after": `</span>`,
|
||||
})
|
||||
|
||||
// register html highlighter
|
||||
htmlFormatter := search.NewHTMLFragmentFormatterCustom(`<span class="highlight">`, `</span>`)
|
||||
htmlHighlighter := search.NewSimpleHighlighter()
|
||||
htmlHighlighter.SetFragmentFormatter(htmlFormatter)
|
||||
Config.Highlight.Highlighters["html"] = htmlHighlighter
|
||||
Config.Cache.DefineHighlighter("html", "simple",
|
||||
map[string]interface{}{
|
||||
"fragmenter": "simple",
|
||||
"formatter": "highlightSpanHTML",
|
||||
})
|
||||
|
||||
Config.Cache.DefineHighlighter("ansi", "simple",
|
||||
map[string]interface{}{
|
||||
"fragmenter": "simple",
|
||||
"formatter": "ansi",
|
||||
})
|
||||
|
||||
// set the default highlighter
|
||||
htmlHighlighterName := "html"
|
||||
Config.DefaultHighlighter = &htmlHighlighterName
|
||||
Config.DefaultHighlighter = "html"
|
||||
|
||||
// default kv store
|
||||
Config.DefaultKVStore = "boltdb"
|
||||
|
|
|
@ -22,6 +22,8 @@ import (
|
|||
"github.com/blevesearch/bleve/index/upside_down"
|
||||
"github.com/blevesearch/bleve/registry"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/collectors"
|
||||
"github.com/blevesearch/bleve/search/facets"
|
||||
)
|
||||
|
||||
type indexImpl struct {
|
||||
|
@ -320,7 +322,7 @@ func (i *indexImpl) Search(req *SearchRequest) (*SearchResult, error) {
|
|||
return nil, ERROR_INDEX_CLOSED
|
||||
}
|
||||
|
||||
collector := search.NewTopScorerSkipCollector(req.Size, req.From)
|
||||
collector := collectors.NewTopScorerSkipCollector(req.Size, req.From)
|
||||
searcher, err := req.Query.Searcher(i, req.Explain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -332,14 +334,14 @@ func (i *indexImpl) Search(req *SearchRequest) (*SearchResult, error) {
|
|||
for facetName, facetRequest := range req.Facets {
|
||||
if facetRequest.NumericRanges != nil {
|
||||
// build numeric range facet
|
||||
facetBuilder := search.NewNumericFacetBuilder(facetRequest.Field, facetRequest.Size)
|
||||
facetBuilder := facets.NewNumericFacetBuilder(facetRequest.Field, facetRequest.Size)
|
||||
for _, nr := range facetRequest.NumericRanges {
|
||||
facetBuilder.AddRange(nr.Name, nr.Min, nr.Max)
|
||||
}
|
||||
facetsBuilder.Add(facetName, facetBuilder)
|
||||
} else if facetRequest.DateTimeRanges != nil {
|
||||
// build date range facet
|
||||
facetBuilder := search.NewDateTimeFacetBuilder(facetRequest.Field, facetRequest.Size)
|
||||
facetBuilder := facets.NewDateTimeFacetBuilder(facetRequest.Field, facetRequest.Size)
|
||||
dateTimeParser := i.m.dateTimeParserNamed(i.m.DefaultDateTimeParser)
|
||||
for _, dr := range facetRequest.DateTimeRanges {
|
||||
dr.ParseDates(dateTimeParser)
|
||||
|
@ -348,7 +350,7 @@ func (i *indexImpl) Search(req *SearchRequest) (*SearchResult, error) {
|
|||
facetsBuilder.Add(facetName, facetBuilder)
|
||||
} else {
|
||||
// build terms facet
|
||||
facetBuilder := search.NewTermsFacetBuilder(facetRequest.Field, facetRequest.Size)
|
||||
facetBuilder := facets.NewTermsFacetBuilder(facetRequest.Field, facetRequest.Size)
|
||||
facetsBuilder.Add(facetName, facetBuilder)
|
||||
}
|
||||
}
|
||||
|
@ -364,13 +366,19 @@ func (i *indexImpl) Search(req *SearchRequest) (*SearchResult, error) {
|
|||
|
||||
if req.Highlight != nil {
|
||||
// get the right highlighter
|
||||
highlighter := Config.Highlight.Highlighters[*Config.DefaultHighlighter]
|
||||
highlighter, err := Config.Cache.HighlighterNamed(Config.DefaultHighlighter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.Highlight.Style != nil {
|
||||
highlighter = Config.Highlight.Highlighters[*req.Highlight.Style]
|
||||
if highlighter == nil {
|
||||
return nil, fmt.Errorf("no highlighter named `%s` registered", *req.Highlight.Style)
|
||||
highlighter, err = Config.Cache.HighlighterNamed(*req.Highlight.Style)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if highlighter == nil {
|
||||
return nil, fmt.Errorf("no highlighter named `%s` registered", *req.Highlight.Style)
|
||||
}
|
||||
|
||||
for _, hit := range hits {
|
||||
doc, err := i.i.Document(hit.ID)
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/searchers"
|
||||
)
|
||||
|
||||
type booleanQuery struct {
|
||||
|
@ -97,7 +98,7 @@ func (q *booleanQuery) Searcher(i *indexImpl, explain bool) (search.Searcher, er
|
|||
}
|
||||
}
|
||||
|
||||
return search.NewBooleanSearcher(i.i, mustSearcher, shouldSearcher, mustNotSearcher, explain)
|
||||
return searchers.NewBooleanSearcher(i.i, mustSearcher, shouldSearcher, mustNotSearcher, explain)
|
||||
}
|
||||
|
||||
func (q *booleanQuery) Validate() error {
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/searchers"
|
||||
)
|
||||
|
||||
type conjunctionQuery struct {
|
||||
|
@ -44,15 +45,15 @@ func (q *conjunctionQuery) AddQuery(aq Query) *conjunctionQuery {
|
|||
}
|
||||
|
||||
func (q *conjunctionQuery) Searcher(i *indexImpl, explain bool) (search.Searcher, error) {
|
||||
searchers := make([]search.Searcher, len(q.Conjuncts))
|
||||
ss := make([]search.Searcher, len(q.Conjuncts))
|
||||
for in, conjunct := range q.Conjuncts {
|
||||
var err error
|
||||
searchers[in], err = conjunct.Searcher(i, explain)
|
||||
ss[in], err = conjunct.Searcher(i, explain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return search.NewConjunctionSearcher(i.i, searchers, explain)
|
||||
return searchers.NewConjunctionSearcher(i.i, ss, explain)
|
||||
}
|
||||
|
||||
func (q *conjunctionQuery) Validate() error {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
"github.com/blevesearch/bleve/numeric_util"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/searchers"
|
||||
)
|
||||
|
||||
type dateRangeQuery struct {
|
||||
|
@ -99,7 +100,7 @@ func (q *dateRangeQuery) Searcher(i *indexImpl, explain bool) (search.Searcher,
|
|||
max = numeric_util.Int64ToFloat64(endTime.UnixNano())
|
||||
}
|
||||
|
||||
return search.NewNumericRangeSearcher(i.i, &min, &max, q.InclusiveStart, q.InclusiveEnd, field, q.BoostVal, explain)
|
||||
return searchers.NewNumericRangeSearcher(i.i, &min, &max, q.InclusiveStart, q.InclusiveEnd, field, q.BoostVal, explain)
|
||||
}
|
||||
|
||||
func (q *dateRangeQuery) Validate() error {
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/searchers"
|
||||
)
|
||||
|
||||
type disjunctionQuery struct {
|
||||
|
@ -64,15 +65,15 @@ func (q *disjunctionQuery) SetMin(m float64) Query {
|
|||
}
|
||||
|
||||
func (q *disjunctionQuery) Searcher(i *indexImpl, explain bool) (search.Searcher, error) {
|
||||
searchers := make([]search.Searcher, len(q.Disjuncts))
|
||||
ss := make([]search.Searcher, len(q.Disjuncts))
|
||||
for in, disjunct := range q.Disjuncts {
|
||||
var err error
|
||||
searchers[in], err = disjunct.Searcher(i, explain)
|
||||
ss[in], err = disjunct.Searcher(i, explain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return search.NewDisjunctionSearcher(i.i, searchers, q.MinVal, explain)
|
||||
return searchers.NewDisjunctionSearcher(i.i, ss, q.MinVal, explain)
|
||||
}
|
||||
|
||||
func (q *disjunctionQuery) Validate() error {
|
||||
|
|
|
@ -11,6 +11,7 @@ package bleve
|
|||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/searchers"
|
||||
)
|
||||
|
||||
type matchAllQuery struct {
|
||||
|
@ -35,7 +36,7 @@ func (q *matchAllQuery) SetBoost(b float64) Query {
|
|||
}
|
||||
|
||||
func (q *matchAllQuery) Searcher(i *indexImpl, explain bool) (search.Searcher, error) {
|
||||
return search.NewMatchAllSearcher(i.i, q.BoostVal, explain)
|
||||
return searchers.NewMatchAllSearcher(i.i, q.BoostVal, explain)
|
||||
}
|
||||
|
||||
func (q *matchAllQuery) Validate() error {
|
||||
|
|
|
@ -11,6 +11,7 @@ package bleve
|
|||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/searchers"
|
||||
)
|
||||
|
||||
type matchNoneQuery struct {
|
||||
|
@ -35,7 +36,7 @@ func (q *matchNoneQuery) SetBoost(b float64) Query {
|
|||
}
|
||||
|
||||
func (q *matchNoneQuery) Searcher(i *indexImpl, explain bool) (search.Searcher, error) {
|
||||
return search.NewMatchNoneSearcher(i.i)
|
||||
return searchers.NewMatchNoneSearcher(i.i)
|
||||
}
|
||||
|
||||
func (q *matchNoneQuery) Validate() error {
|
||||
|
|
|
@ -11,6 +11,7 @@ package bleve
|
|||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/searchers"
|
||||
)
|
||||
|
||||
type numericRangeQuery struct {
|
||||
|
@ -68,7 +69,7 @@ func (q *numericRangeQuery) Searcher(i *indexImpl, explain bool) (search.Searche
|
|||
if q.FieldVal == "" {
|
||||
field = i.m.DefaultField
|
||||
}
|
||||
return search.NewNumericRangeSearcher(i.i, q.Min, q.Max, q.InclusiveMin, q.InclusiveMax, field, q.BoostVal, explain)
|
||||
return searchers.NewNumericRangeSearcher(i.i, q.Min, q.Max, q.InclusiveMin, q.InclusiveMax, field, q.BoostVal, explain)
|
||||
}
|
||||
|
||||
func (q *numericRangeQuery) Validate() error {
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/searchers"
|
||||
)
|
||||
|
||||
type phraseQuery struct {
|
||||
|
@ -58,7 +59,7 @@ func (q *phraseQuery) Searcher(i *indexImpl, explain bool) (search.Searcher, err
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return search.NewPhraseSearcher(i.i, conjunctionSearcher.(*search.ConjunctionSearcher), terms)
|
||||
return searchers.NewPhraseSearcher(i.i, conjunctionSearcher.(*searchers.ConjunctionSearcher), terms)
|
||||
}
|
||||
|
||||
func (q *phraseQuery) Validate() error {
|
||||
|
|
|
@ -11,6 +11,7 @@ package bleve
|
|||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/searchers"
|
||||
)
|
||||
|
||||
type prefixQuery struct {
|
||||
|
@ -52,7 +53,7 @@ func (q *prefixQuery) Searcher(i *indexImpl, explain bool) (search.Searcher, err
|
|||
if q.FieldVal == "" {
|
||||
field = i.m.DefaultField
|
||||
}
|
||||
return search.NewTermPrefixSearcher(i.i, q.Prefix, field, q.BoostVal, explain)
|
||||
return searchers.NewTermPrefixSearcher(i.i, q.Prefix, field, q.BoostVal, explain)
|
||||
}
|
||||
|
||||
func (q *prefixQuery) Validate() error {
|
||||
|
|
|
@ -11,6 +11,7 @@ package bleve
|
|||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/searchers"
|
||||
)
|
||||
|
||||
type termQuery struct {
|
||||
|
@ -51,7 +52,7 @@ func (q *termQuery) Searcher(i *indexImpl, explain bool) (search.Searcher, error
|
|||
if q.FieldVal == "" {
|
||||
field = i.m.DefaultField
|
||||
}
|
||||
return search.NewTermSearcher(i.i, q.Term, field, q.BoostVal, explain)
|
||||
return searchers.NewTermSearcher(i.i, q.Term, field, q.BoostVal, explain)
|
||||
}
|
||||
|
||||
func (q *termQuery) Validate() error {
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// 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 registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
func RegisterFragmentFormatter(name string, constructor FragmentFormatterConstructor) {
|
||||
_, exists := fragmentFormatters[name]
|
||||
if exists {
|
||||
panic(fmt.Errorf("attempted to register duplicate fragment formatter named '%s'", name))
|
||||
}
|
||||
fragmentFormatters[name] = constructor
|
||||
}
|
||||
|
||||
type FragmentFormatterConstructor func(config map[string]interface{}, cache *Cache) (highlight.FragmentFormatter, error)
|
||||
type FragmentFormatterRegistry map[string]FragmentFormatterConstructor
|
||||
type FragmentFormatterCache map[string]highlight.FragmentFormatter
|
||||
|
||||
func (c FragmentFormatterCache) FragmentFormatterNamed(name string, cache *Cache) (highlight.FragmentFormatter, error) {
|
||||
fragmentFormatter, cached := c[name]
|
||||
if cached {
|
||||
return fragmentFormatter, nil
|
||||
}
|
||||
fragmentFormatterConstructor, registered := fragmentFormatters[name]
|
||||
if !registered {
|
||||
return nil, fmt.Errorf("no fragment formatter with name or type '%s' registered", name)
|
||||
}
|
||||
fragmentFormatter, err := fragmentFormatterConstructor(nil, cache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building fragment formatter: %v", err)
|
||||
}
|
||||
c[name] = fragmentFormatter
|
||||
return fragmentFormatter, nil
|
||||
}
|
||||
|
||||
func (c FragmentFormatterCache) DefineFragmentFormatter(name string, typ string, config map[string]interface{}, cache *Cache) (highlight.FragmentFormatter, error) {
|
||||
_, cached := c[name]
|
||||
if cached {
|
||||
return nil, fmt.Errorf("fragment formatter named '%s' already defined", name)
|
||||
}
|
||||
fragmentFormatterConstructor, registered := fragmentFormatters[typ]
|
||||
if !registered {
|
||||
return nil, fmt.Errorf("no fragment formatter type '%s' registered", typ)
|
||||
}
|
||||
fragmentFormatter, err := fragmentFormatterConstructor(config, cache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building fragment formatter: %v", err)
|
||||
}
|
||||
c[name] = fragmentFormatter
|
||||
return fragmentFormatter, nil
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// 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 registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
func RegisterFragmenter(name string, constructor FragmenterConstructor) {
|
||||
_, exists := fragmenters[name]
|
||||
if exists {
|
||||
panic(fmt.Errorf("attempted to register duplicate fragmenter named '%s'", name))
|
||||
}
|
||||
fragmenters[name] = constructor
|
||||
}
|
||||
|
||||
type FragmenterConstructor func(config map[string]interface{}, cache *Cache) (highlight.Fragmenter, error)
|
||||
type FragmenterRegistry map[string]FragmenterConstructor
|
||||
type FragmenterCache map[string]highlight.Fragmenter
|
||||
|
||||
func (c FragmenterCache) FragmenterNamed(name string, cache *Cache) (highlight.Fragmenter, error) {
|
||||
fragmenter, cached := c[name]
|
||||
if cached {
|
||||
return fragmenter, nil
|
||||
}
|
||||
fragmenterConstructor, registered := fragmenters[name]
|
||||
if !registered {
|
||||
return nil, fmt.Errorf("no fragmenter with name or type '%s' registered", name)
|
||||
}
|
||||
fragmenter, err := fragmenterConstructor(nil, cache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building fragmenter: %v", err)
|
||||
}
|
||||
c[name] = fragmenter
|
||||
return fragmenter, nil
|
||||
}
|
||||
|
||||
func (c FragmenterCache) DefineFragmenter(name string, typ string, config map[string]interface{}, cache *Cache) (highlight.Fragmenter, error) {
|
||||
_, cached := c[name]
|
||||
if cached {
|
||||
return nil, fmt.Errorf("fragmenter named '%s' already defined", name)
|
||||
}
|
||||
fragmenterConstructor, registered := fragmenters[typ]
|
||||
if !registered {
|
||||
return nil, fmt.Errorf("no fragmenter type '%s' registered", typ)
|
||||
}
|
||||
fragmenter, err := fragmenterConstructor(config, cache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building fragmenter: %v", err)
|
||||
}
|
||||
c[name] = fragmenter
|
||||
return fragmenter, nil
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// 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 registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
func RegisterHighlighter(name string, constructor HighlighterConstructor) {
|
||||
_, exists := highlighters[name]
|
||||
if exists {
|
||||
panic(fmt.Errorf("attempted to register duplicate highlighter named '%s'", name))
|
||||
}
|
||||
highlighters[name] = constructor
|
||||
}
|
||||
|
||||
type HighlighterConstructor func(config map[string]interface{}, cache *Cache) (highlight.Highlighter, error)
|
||||
type HighlighterRegistry map[string]HighlighterConstructor
|
||||
type HighlighterCache map[string]highlight.Highlighter
|
||||
|
||||
func (c HighlighterCache) HighlighterNamed(name string, cache *Cache) (highlight.Highlighter, error) {
|
||||
highlighter, cached := c[name]
|
||||
if cached {
|
||||
return highlighter, nil
|
||||
}
|
||||
highlighterConstructor, registered := highlighters[name]
|
||||
if !registered {
|
||||
return nil, fmt.Errorf("no highlighter with name or type '%s' registered", name)
|
||||
}
|
||||
highlighter, err := highlighterConstructor(nil, cache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building highlighter: %v", err)
|
||||
}
|
||||
c[name] = highlighter
|
||||
return highlighter, nil
|
||||
}
|
||||
|
||||
func (c HighlighterCache) DefineHighlighter(name string, typ string, config map[string]interface{}, cache *Cache) (highlight.Highlighter, error) {
|
||||
_, cached := c[name]
|
||||
if cached {
|
||||
return nil, fmt.Errorf("highlighter named '%s' already defined", name)
|
||||
}
|
||||
highlighterConstructor, registered := highlighters[typ]
|
||||
if !registered {
|
||||
return nil, fmt.Errorf("no highlighter type '%s' registered", typ)
|
||||
}
|
||||
highlighter, err := highlighterConstructor(config, cache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building highlighter: %v", err)
|
||||
}
|
||||
c[name] = highlighter
|
||||
return highlighter, nil
|
||||
}
|
|
@ -13,12 +13,18 @@ import (
|
|||
"sort"
|
||||
|
||||
"github.com/blevesearch/bleve/analysis"
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
var stores = make(KVStoreRegistry, 0)
|
||||
|
||||
var byteArrayConverters = make(ByteArrayConverterRegistry, 0)
|
||||
|
||||
// highlight
|
||||
var fragmentFormatters = make(FragmentFormatterRegistry, 0)
|
||||
var fragmenters = make(FragmenterRegistry, 0)
|
||||
var highlighters = make(HighlighterRegistry, 0)
|
||||
|
||||
// analysis
|
||||
var charFilters = make(CharFilterRegistry, 0)
|
||||
var tokenizers = make(TokenizerRegistry, 0)
|
||||
|
@ -28,22 +34,28 @@ var analyzers = make(AnalyzerRegistry, 0)
|
|||
var dateTimeParsers = make(DateTimeParserRegistry, 0)
|
||||
|
||||
type Cache struct {
|
||||
CharFilters CharFilterCache
|
||||
Tokenizers TokenizerCache
|
||||
TokenMaps TokenMapCache
|
||||
TokenFilters TokenFilterCache
|
||||
Analyzers AnalyzerCache
|
||||
DateTimeParsers DateTimeParserCache
|
||||
CharFilters CharFilterCache
|
||||
Tokenizers TokenizerCache
|
||||
TokenMaps TokenMapCache
|
||||
TokenFilters TokenFilterCache
|
||||
Analyzers AnalyzerCache
|
||||
DateTimeParsers DateTimeParserCache
|
||||
FragmentFormatters FragmentFormatterCache
|
||||
Fragmenters FragmenterCache
|
||||
Highlighters HighlighterCache
|
||||
}
|
||||
|
||||
func NewCache() *Cache {
|
||||
return &Cache{
|
||||
CharFilters: make(CharFilterCache, 0),
|
||||
Tokenizers: make(TokenizerCache, 0),
|
||||
TokenMaps: make(TokenMapCache, 0),
|
||||
TokenFilters: make(TokenFilterCache, 0),
|
||||
Analyzers: make(AnalyzerCache, 0),
|
||||
DateTimeParsers: make(DateTimeParserCache, 0),
|
||||
CharFilters: make(CharFilterCache, 0),
|
||||
Tokenizers: make(TokenizerCache, 0),
|
||||
TokenMaps: make(TokenMapCache, 0),
|
||||
TokenFilters: make(TokenFilterCache, 0),
|
||||
Analyzers: make(AnalyzerCache, 0),
|
||||
DateTimeParsers: make(DateTimeParserCache, 0),
|
||||
FragmentFormatters: make(FragmentFormatterCache, 0),
|
||||
Fragmenters: make(FragmenterCache, 0),
|
||||
Highlighters: make(HighlighterCache, 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,6 +107,30 @@ func (c *Cache) DefineDateTimeParser(name string, typ string, config map[string]
|
|||
return c.DateTimeParsers.DefineDateTimeParser(name, typ, config, c)
|
||||
}
|
||||
|
||||
func (c *Cache) FragmentFormatterNamed(name string) (highlight.FragmentFormatter, error) {
|
||||
return c.FragmentFormatters.FragmentFormatterNamed(name, c)
|
||||
}
|
||||
|
||||
func (c *Cache) DefineFragmentFormatter(name string, typ string, config map[string]interface{}) (highlight.FragmentFormatter, error) {
|
||||
return c.FragmentFormatters.DefineFragmentFormatter(name, typ, config, c)
|
||||
}
|
||||
|
||||
func (c *Cache) FragmenterNamed(name string) (highlight.Fragmenter, error) {
|
||||
return c.Fragmenters.FragmenterNamed(name, c)
|
||||
}
|
||||
|
||||
func (c *Cache) DefineFragmenter(name string, typ string, config map[string]interface{}) (highlight.Fragmenter, error) {
|
||||
return c.Fragmenters.DefineFragmenter(name, typ, config, c)
|
||||
}
|
||||
|
||||
func (c *Cache) HighlighterNamed(name string) (highlight.Highlighter, error) {
|
||||
return c.Highlighters.HighlighterNamed(name, c)
|
||||
}
|
||||
|
||||
func (c *Cache) DefineHighlighter(name string, typ string, config map[string]interface{}) (highlight.Highlighter, error) {
|
||||
return c.Highlighters.DefineHighlighter(name, typ, config, c)
|
||||
}
|
||||
|
||||
func PrintRegistry() {
|
||||
sorted := make(sort.StringSlice, 0, len(charFilters))
|
||||
for name, _ := range charFilters {
|
||||
|
@ -183,4 +219,37 @@ func PrintRegistry() {
|
|||
fmt.Printf("\t%s\n", name)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
sorted = make(sort.StringSlice, 0, len(fragmentFormatters))
|
||||
for name, _ := range fragmentFormatters {
|
||||
sorted = append(sorted, name)
|
||||
}
|
||||
sorted.Sort()
|
||||
fmt.Printf("Fragment Formatters:\n")
|
||||
for _, name := range sorted {
|
||||
fmt.Printf("\t%s\n", name)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
sorted = make(sort.StringSlice, 0, len(fragmenters))
|
||||
for name, _ := range fragmenters {
|
||||
sorted = append(sorted, name)
|
||||
}
|
||||
sorted.Sort()
|
||||
fmt.Printf("Fragmenters:\n")
|
||||
for _, name := range sorted {
|
||||
fmt.Printf("\t%s\n", name)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
sorted = make(sort.StringSlice, 0, len(highlighters))
|
||||
for name, _ := range highlighters {
|
||||
sorted = append(sorted, name)
|
||||
}
|
||||
sorted.Sort()
|
||||
fmt.Printf("Highlighters:\n")
|
||||
for _, name := range sorted {
|
||||
fmt.Printf("\t%s\n", name)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
// 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 search
|
||||
package collectors
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"time"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type TopScoreCollector struct {
|
||||
|
@ -20,7 +22,7 @@ type TopScoreCollector struct {
|
|||
took time.Duration
|
||||
maxScore float64
|
||||
total uint64
|
||||
facetsBuilder *FacetsBuilder
|
||||
facetsBuilder *search.FacetsBuilder
|
||||
}
|
||||
|
||||
func NewTopScorerCollector(k int) *TopScoreCollector {
|
||||
|
@ -51,7 +53,7 @@ func (tksc *TopScoreCollector) Took() time.Duration {
|
|||
return tksc.took
|
||||
}
|
||||
|
||||
func (tksc *TopScoreCollector) Collect(searcher Searcher) error {
|
||||
func (tksc *TopScoreCollector) Collect(searcher search.Searcher) error {
|
||||
startTime := time.Now()
|
||||
next, err := searcher.Next()
|
||||
for err == nil && next != nil {
|
||||
|
@ -69,7 +71,7 @@ func (tksc *TopScoreCollector) Collect(searcher Searcher) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (tksc *TopScoreCollector) collectSingle(dm *DocumentMatch) {
|
||||
func (tksc *TopScoreCollector) collectSingle(dm *search.DocumentMatch) {
|
||||
// increment total hits
|
||||
tksc.total += 1
|
||||
|
||||
|
@ -79,7 +81,7 @@ func (tksc *TopScoreCollector) collectSingle(dm *DocumentMatch) {
|
|||
}
|
||||
|
||||
for e := tksc.results.Front(); e != nil; e = e.Next() {
|
||||
curr := e.Value.(*DocumentMatch)
|
||||
curr := e.Value.(*search.DocumentMatch)
|
||||
if dm.Score < curr.Score {
|
||||
|
||||
tksc.results.InsertBefore(dm, e)
|
||||
|
@ -99,9 +101,9 @@ func (tksc *TopScoreCollector) collectSingle(dm *DocumentMatch) {
|
|||
}
|
||||
}
|
||||
|
||||
func (tksc *TopScoreCollector) Results() DocumentMatchCollection {
|
||||
func (tksc *TopScoreCollector) Results() search.DocumentMatchCollection {
|
||||
if tksc.results.Len()-tksc.skip > 0 {
|
||||
rv := make(DocumentMatchCollection, tksc.results.Len()-tksc.skip)
|
||||
rv := make(search.DocumentMatchCollection, tksc.results.Len()-tksc.skip)
|
||||
i := 0
|
||||
skipped := 0
|
||||
for e := tksc.results.Back(); e != nil; e = e.Prev() {
|
||||
|
@ -109,22 +111,22 @@ func (tksc *TopScoreCollector) Results() DocumentMatchCollection {
|
|||
skipped++
|
||||
continue
|
||||
}
|
||||
rv[i] = e.Value.(*DocumentMatch)
|
||||
rv[i] = e.Value.(*search.DocumentMatch)
|
||||
i++
|
||||
}
|
||||
return rv
|
||||
}
|
||||
return DocumentMatchCollection{}
|
||||
return search.DocumentMatchCollection{}
|
||||
}
|
||||
|
||||
func (tksc *TopScoreCollector) SetFacetsBuilder(facetsBuilder *FacetsBuilder) {
|
||||
func (tksc *TopScoreCollector) SetFacetsBuilder(facetsBuilder *search.FacetsBuilder) {
|
||||
tksc.facetsBuilder = facetsBuilder
|
||||
}
|
||||
|
||||
func (tksc *TopScoreCollector) FacetResults() FacetResults {
|
||||
func (tksc *TopScoreCollector) FacetResults() search.FacetResults {
|
||||
if tksc.facetsBuilder != nil {
|
||||
return tksc.facetsBuilder.Results()
|
||||
} else {
|
||||
return FacetResults{}
|
||||
return search.FacetResults{}
|
||||
}
|
||||
}
|
|
@ -6,10 +6,12 @@
|
|||
// 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 search
|
||||
package collectors
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
func TestTop10Scores(t *testing.T) {
|
||||
|
@ -18,60 +20,60 @@ func TestTop10Scores(t *testing.T) {
|
|||
// the top-10 scores are > 10
|
||||
// everything else is less than 10
|
||||
searcher := &stubSearcher{
|
||||
matches: DocumentMatchCollection{
|
||||
&DocumentMatch{
|
||||
matches: search.DocumentMatchCollection{
|
||||
&search.DocumentMatch{
|
||||
ID: "a",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "b",
|
||||
Score: 9,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "c",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "d",
|
||||
Score: 9,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "e",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "f",
|
||||
Score: 9,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "g",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "h",
|
||||
Score: 9,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "i",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "j",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "k",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "l",
|
||||
Score: 99,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "m",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "n",
|
||||
Score: 11,
|
||||
},
|
||||
|
@ -123,60 +125,60 @@ func TestTop10ScoresSkip10(t *testing.T) {
|
|||
// the top-10 scores are > 10
|
||||
// everything else is less than 10
|
||||
searcher := &stubSearcher{
|
||||
matches: DocumentMatchCollection{
|
||||
&DocumentMatch{
|
||||
matches: search.DocumentMatchCollection{
|
||||
&search.DocumentMatch{
|
||||
ID: "a",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "b",
|
||||
Score: 9.5,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "c",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "d",
|
||||
Score: 9,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "e",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "f",
|
||||
Score: 9,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "g",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "h",
|
||||
Score: 9,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "i",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "j",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "k",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "l",
|
||||
Score: 99,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "m",
|
||||
Score: 11,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "n",
|
||||
Score: 11,
|
||||
},
|
|
@ -6,14 +6,18 @@
|
|||
// 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 search
|
||||
package collectors
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type stubSearcher struct {
|
||||
index int
|
||||
matches DocumentMatchCollection
|
||||
matches search.DocumentMatchCollection
|
||||
}
|
||||
|
||||
func (ss *stubSearcher) Next() (*DocumentMatch, error) {
|
||||
func (ss *stubSearcher) Next() (*search.DocumentMatch, error) {
|
||||
if ss.index < len(ss.matches) {
|
||||
rv := ss.matches[ss.index]
|
||||
ss.index++
|
||||
|
@ -22,7 +26,7 @@ func (ss *stubSearcher) Next() (*DocumentMatch, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (ss *stubSearcher) Advance(ID string) (*DocumentMatch, error) {
|
||||
func (ss *stubSearcher) Advance(ID string) (*search.DocumentMatch, error) {
|
||||
|
||||
for ss.index < len(ss.matches) && ss.matches[ss.index].ID < ID {
|
||||
ss.index++
|
|
@ -6,7 +6,7 @@
|
|||
// 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 search
|
||||
package facets
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/numeric_util"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type dateTimeRange struct {
|
||||
|
@ -81,8 +82,8 @@ func (fb *DateTimeFacetBuilder) Update(ft index.FieldTerms) {
|
|||
}
|
||||
}
|
||||
|
||||
func (fb *DateTimeFacetBuilder) Result() FacetResult {
|
||||
rv := FacetResult{
|
||||
func (fb *DateTimeFacetBuilder) Result() search.FacetResult {
|
||||
rv := search.FacetResult{
|
||||
Field: fb.field,
|
||||
Total: fb.total,
|
||||
Missing: fb.missing,
|
||||
|
@ -95,7 +96,7 @@ func (fb *DateTimeFacetBuilder) Result() FacetResult {
|
|||
OUTER:
|
||||
for term, count := range fb.termsCount {
|
||||
dateRange := fb.ranges[term]
|
||||
tf := &DateRangeFacet{
|
||||
tf := &search.DateRangeFacet{
|
||||
Name: term,
|
||||
Count: count,
|
||||
}
|
||||
|
@ -109,7 +110,7 @@ OUTER:
|
|||
}
|
||||
|
||||
for e := topN.Front(); e != nil; e = e.Next() {
|
||||
curr := e.Value.(*DateRangeFacet)
|
||||
curr := e.Value.(*search.DateRangeFacet)
|
||||
if tf.Count < curr.Count {
|
||||
|
||||
topN.InsertBefore(tf, e)
|
||||
|
@ -131,13 +132,13 @@ OUTER:
|
|||
}
|
||||
|
||||
// we now have the list of the top N facets
|
||||
rv.DateRanges = make([]*DateRangeFacet, topN.Len())
|
||||
rv.DateRanges = make([]*search.DateRangeFacet, topN.Len())
|
||||
i := 0
|
||||
notOther := 0
|
||||
for e := topN.Back(); e != nil; e = e.Prev() {
|
||||
rv.DateRanges[i] = e.Value.(*DateRangeFacet)
|
||||
rv.DateRanges[i] = e.Value.(*search.DateRangeFacet)
|
||||
i++
|
||||
notOther += e.Value.(*DateRangeFacet).Count
|
||||
notOther += e.Value.(*search.DateRangeFacet).Count
|
||||
}
|
||||
rv.Other = fb.total - notOther
|
||||
|
|
@ -6,13 +6,14 @@
|
|||
// 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 search
|
||||
package facets
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/numeric_util"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type numericRange struct {
|
||||
|
@ -80,8 +81,8 @@ func (fb *NumericFacetBuilder) Update(ft index.FieldTerms) {
|
|||
}
|
||||
}
|
||||
|
||||
func (fb *NumericFacetBuilder) Result() FacetResult {
|
||||
rv := FacetResult{
|
||||
func (fb *NumericFacetBuilder) Result() search.FacetResult {
|
||||
rv := search.FacetResult{
|
||||
Field: fb.field,
|
||||
Total: fb.total,
|
||||
Missing: fb.missing,
|
||||
|
@ -94,7 +95,7 @@ func (fb *NumericFacetBuilder) Result() FacetResult {
|
|||
OUTER:
|
||||
for term, count := range fb.termsCount {
|
||||
numericRange := fb.ranges[term]
|
||||
tf := &NumericRangeFacet{
|
||||
tf := &search.NumericRangeFacet{
|
||||
Name: term,
|
||||
Count: count,
|
||||
Min: numericRange.min,
|
||||
|
@ -102,7 +103,7 @@ OUTER:
|
|||
}
|
||||
|
||||
for e := topN.Front(); e != nil; e = e.Next() {
|
||||
curr := e.Value.(*NumericRangeFacet)
|
||||
curr := e.Value.(*search.NumericRangeFacet)
|
||||
if tf.Count < curr.Count {
|
||||
|
||||
topN.InsertBefore(tf, e)
|
||||
|
@ -124,13 +125,13 @@ OUTER:
|
|||
}
|
||||
|
||||
// we now have the list of the top N facets
|
||||
rv.NumericRanges = make([]*NumericRangeFacet, topN.Len())
|
||||
rv.NumericRanges = make([]*search.NumericRangeFacet, topN.Len())
|
||||
i := 0
|
||||
notOther := 0
|
||||
for e := topN.Back(); e != nil; e = e.Prev() {
|
||||
rv.NumericRanges[i] = e.Value.(*NumericRangeFacet)
|
||||
rv.NumericRanges[i] = e.Value.(*search.NumericRangeFacet)
|
||||
i++
|
||||
notOther += e.Value.(*NumericRangeFacet).Count
|
||||
notOther += e.Value.(*search.NumericRangeFacet).Count
|
||||
}
|
||||
rv.Other = fb.total - notOther
|
||||
|
|
@ -6,12 +6,13 @@
|
|||
// 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 search
|
||||
package facets
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type TermsFacetBuilder struct {
|
||||
|
@ -47,8 +48,8 @@ func (fb *TermsFacetBuilder) Update(ft index.FieldTerms) {
|
|||
}
|
||||
}
|
||||
|
||||
func (fb *TermsFacetBuilder) Result() FacetResult {
|
||||
rv := FacetResult{
|
||||
func (fb *TermsFacetBuilder) Result() search.FacetResult {
|
||||
rv := search.FacetResult{
|
||||
Field: fb.field,
|
||||
Total: fb.total,
|
||||
Missing: fb.missing,
|
||||
|
@ -60,13 +61,13 @@ func (fb *TermsFacetBuilder) Result() FacetResult {
|
|||
// walk entries and find top N
|
||||
OUTER:
|
||||
for term, count := range fb.termsCount {
|
||||
tf := &TermFacet{
|
||||
tf := &search.TermFacet{
|
||||
Term: term,
|
||||
Count: count,
|
||||
}
|
||||
|
||||
for e := topN.Front(); e != nil; e = e.Next() {
|
||||
curr := e.Value.(*TermFacet)
|
||||
curr := e.Value.(*search.TermFacet)
|
||||
if tf.Count < curr.Count {
|
||||
|
||||
topN.InsertBefore(tf, e)
|
||||
|
@ -88,13 +89,13 @@ OUTER:
|
|||
}
|
||||
|
||||
// we now have the list of the top N facets
|
||||
rv.Terms = make([]*TermFacet, topN.Len())
|
||||
rv.Terms = make([]*search.TermFacet, topN.Len())
|
||||
i := 0
|
||||
notOther := 0
|
||||
for e := topN.Back(); e != nil; e = e.Prev() {
|
||||
rv.Terms[i] = e.Value.(*TermFacet)
|
||||
rv.Terms[i] = e.Value.(*search.TermFacet)
|
||||
i++
|
||||
notOther += e.Value.(*TermFacet).Count
|
||||
notOther += e.Value.(*search.TermFacet).Count
|
||||
}
|
||||
rv.Other = fb.total - notOther
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
// 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 search
|
||||
|
||||
import ()
|
||||
|
||||
const DEFAULT_HTML_HIGHLIGHT_BEFORE = "<b>"
|
||||
const DEFAULT_HTML_HIGHLIGHT_AFTER = "</b>"
|
||||
|
||||
type HTMLFragmentFormatter struct {
|
||||
before string
|
||||
after string
|
||||
}
|
||||
|
||||
func NewHTMLFragmentFormatter() *HTMLFragmentFormatter {
|
||||
return &HTMLFragmentFormatter{
|
||||
before: DEFAULT_HTML_HIGHLIGHT_BEFORE,
|
||||
after: DEFAULT_HTML_HIGHLIGHT_AFTER,
|
||||
}
|
||||
}
|
||||
|
||||
func NewHTMLFragmentFormatterCustom(before, after string) *HTMLFragmentFormatter {
|
||||
return &HTMLFragmentFormatter{
|
||||
before: before,
|
||||
after: after,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *HTMLFragmentFormatter) Format(f *Fragment, tlm TermLocationMap) string {
|
||||
orderedTermLocations := OrderTermLocations(tlm)
|
||||
rv := ""
|
||||
curr := f.start
|
||||
for _, termLocation := range orderedTermLocations {
|
||||
if termLocation.Start < curr {
|
||||
continue
|
||||
}
|
||||
if termLocation.End > f.end {
|
||||
break
|
||||
}
|
||||
// add the stuff before this location
|
||||
rv += string(f.orig[curr:termLocation.Start])
|
||||
// add the color
|
||||
rv += a.before
|
||||
// add the term itself
|
||||
rv += string(f.orig[termLocation.Start:termLocation.End])
|
||||
// reset the color
|
||||
rv += a.after
|
||||
// update current
|
||||
curr = termLocation.End
|
||||
}
|
||||
// add any remaining text after the last token
|
||||
rv += string(f.orig[curr:f.end])
|
||||
|
||||
return rv
|
||||
}
|
|
@ -6,9 +6,16 @@
|
|||
// 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 search
|
||||
|
||||
import ()
|
||||
package ansi
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/registry"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
const Name = "ansi"
|
||||
|
||||
const DEFAULT_ANSI_HIGHLIGHT = bgYellow
|
||||
|
||||
|
@ -22,30 +29,30 @@ func NewANSIFragmentFormatter() *ANSIFragmentFormatter {
|
|||
}
|
||||
}
|
||||
|
||||
func (a *ANSIFragmentFormatter) Format(f *Fragment, tlm TermLocationMap) string {
|
||||
orderedTermLocations := OrderTermLocations(tlm)
|
||||
func (a *ANSIFragmentFormatter) Format(f *highlight.Fragment, tlm search.TermLocationMap) string {
|
||||
orderedTermLocations := highlight.OrderTermLocations(tlm)
|
||||
rv := ""
|
||||
curr := f.start
|
||||
curr := f.Start
|
||||
for _, termLocation := range orderedTermLocations {
|
||||
if termLocation.Start < curr {
|
||||
continue
|
||||
}
|
||||
if termLocation.End > f.end {
|
||||
if termLocation.End > f.End {
|
||||
break
|
||||
}
|
||||
// add the stuff before this location
|
||||
rv += string(f.orig[curr:termLocation.Start])
|
||||
rv += string(f.Orig[curr:termLocation.Start])
|
||||
// add the color
|
||||
rv += a.color
|
||||
// add the term itself
|
||||
rv += string(f.orig[termLocation.Start:termLocation.End])
|
||||
rv += string(f.Orig[termLocation.Start:termLocation.End])
|
||||
// reset the color
|
||||
rv += reset
|
||||
// update current
|
||||
curr = termLocation.End
|
||||
}
|
||||
// add any remaining text after the last token
|
||||
rv += string(f.orig[curr:f.end])
|
||||
rv += string(f.Orig[curr:f.End])
|
||||
|
||||
return rv
|
||||
}
|
||||
|
@ -77,3 +84,11 @@ const (
|
|||
bgCyan = "\x1b[46m"
|
||||
bgWhite = "\x1b[47m"
|
||||
)
|
||||
|
||||
func Constructor(config map[string]interface{}, cache *registry.Cache) (highlight.FragmentFormatter, error) {
|
||||
return NewANSIFragmentFormatter(), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.RegisterFragmentFormatter(Name, Constructor)
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// 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 html
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/registry"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
const Name = "html"
|
||||
|
||||
const defaultHtmlHighlightBefore = "<b>"
|
||||
const defaultHtmlHighlightAfter = "</b>"
|
||||
|
||||
type HTMLFragmentFormatter struct {
|
||||
before string
|
||||
after string
|
||||
}
|
||||
|
||||
func NewHTMLFragmentFormatter(before, after string) *HTMLFragmentFormatter {
|
||||
return &HTMLFragmentFormatter{
|
||||
before: before,
|
||||
after: after,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *HTMLFragmentFormatter) Format(f *highlight.Fragment, tlm search.TermLocationMap) string {
|
||||
orderedTermLocations := highlight.OrderTermLocations(tlm)
|
||||
rv := ""
|
||||
curr := f.Start
|
||||
for _, termLocation := range orderedTermLocations {
|
||||
if termLocation.Start < curr {
|
||||
continue
|
||||
}
|
||||
if termLocation.End > f.End {
|
||||
break
|
||||
}
|
||||
// add the stuff before this location
|
||||
rv += string(f.Orig[curr:termLocation.Start])
|
||||
// add the color
|
||||
rv += a.before
|
||||
// add the term itself
|
||||
rv += string(f.Orig[termLocation.Start:termLocation.End])
|
||||
// reset the color
|
||||
rv += a.after
|
||||
// update current
|
||||
curr = termLocation.End
|
||||
}
|
||||
// add any remaining text after the last token
|
||||
rv += string(f.Orig[curr:f.End])
|
||||
|
||||
return rv
|
||||
}
|
||||
|
||||
func Constructor(config map[string]interface{}, cache *registry.Cache) (highlight.FragmentFormatter, error) {
|
||||
before := defaultHtmlHighlightBefore
|
||||
beforeVal, ok := config["before"].(string)
|
||||
if ok {
|
||||
before = beforeVal
|
||||
}
|
||||
after := defaultHtmlHighlightAfter
|
||||
afterVal, ok := config["after"].(string)
|
||||
if ok {
|
||||
after = afterVal
|
||||
}
|
||||
return NewHTMLFragmentFormatter(before, after), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.RegisterFragmentFormatter(Name, Constructor)
|
||||
}
|
|
@ -6,27 +6,31 @@
|
|||
// 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 search
|
||||
|
||||
package html
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
func TestHTMLFragmentFormatterDefault(t *testing.T) {
|
||||
func TestHTMLFragmentFormatter1(t *testing.T) {
|
||||
tests := []struct {
|
||||
fragment *Fragment
|
||||
tlm TermLocationMap
|
||||
fragment *highlight.Fragment
|
||||
tlm search.TermLocationMap
|
||||
output string
|
||||
}{
|
||||
{
|
||||
fragment: &Fragment{
|
||||
orig: []byte("the quick brown fox"),
|
||||
start: 0,
|
||||
end: 19,
|
||||
fragment: &highlight.Fragment{
|
||||
Orig: []byte("the quick brown fox"),
|
||||
Start: 0,
|
||||
End: 19,
|
||||
},
|
||||
tlm: TermLocationMap{
|
||||
"quick": Locations{
|
||||
&Location{
|
||||
tlm: search.TermLocationMap{
|
||||
"quick": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 2,
|
||||
Start: 4,
|
||||
End: 9,
|
||||
|
@ -37,7 +41,7 @@ func TestHTMLFragmentFormatterDefault(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
emHtmlFormatter := NewHTMLFragmentFormatter()
|
||||
emHtmlFormatter := NewHTMLFragmentFormatter("<b>", "</b>")
|
||||
for _, test := range tests {
|
||||
result := emHtmlFormatter.Format(test.fragment, test.tlm)
|
||||
if result != test.output {
|
||||
|
@ -46,21 +50,21 @@ func TestHTMLFragmentFormatterDefault(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHTMLFragmentFormatterCustom(t *testing.T) {
|
||||
func TestHTMLFragmentFormatter2(t *testing.T) {
|
||||
tests := []struct {
|
||||
fragment *Fragment
|
||||
tlm TermLocationMap
|
||||
fragment *highlight.Fragment
|
||||
tlm search.TermLocationMap
|
||||
output string
|
||||
}{
|
||||
{
|
||||
fragment: &Fragment{
|
||||
orig: []byte("the quick brown fox"),
|
||||
start: 0,
|
||||
end: 19,
|
||||
fragment: &highlight.Fragment{
|
||||
Orig: []byte("the quick brown fox"),
|
||||
Start: 0,
|
||||
End: 19,
|
||||
},
|
||||
tlm: TermLocationMap{
|
||||
"quick": Locations{
|
||||
&Location{
|
||||
tlm: search.TermLocationMap{
|
||||
"quick": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 2,
|
||||
Start: 4,
|
||||
End: 9,
|
||||
|
@ -71,7 +75,7 @@ func TestHTMLFragmentFormatterCustom(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
emHtmlFormatter := NewHTMLFragmentFormatterCustom("<em>", "</em>")
|
||||
emHtmlFormatter := NewHTMLFragmentFormatter("<em>", "</em>")
|
||||
for _, test := range tests {
|
||||
result := emHtmlFormatter.Format(test.fragment, test.tlm)
|
||||
if result != test.output {
|
|
@ -6,30 +6,30 @@
|
|||
// 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 search
|
||||
|
||||
import ()
|
||||
package simple
|
||||
|
||||
const DEFAULT_FRAGMENT_SIZE = 200
|
||||
import (
|
||||
"github.com/blevesearch/bleve/registry"
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
const Name = "simple"
|
||||
|
||||
const defaultFragmentSize = 200
|
||||
|
||||
type SimpleFragmenter struct {
|
||||
fragmentSize int
|
||||
}
|
||||
|
||||
func NewSimpleFragmenter() *SimpleFragmenter {
|
||||
return &SimpleFragmenter{
|
||||
fragmentSize: DEFAULT_FRAGMENT_SIZE,
|
||||
}
|
||||
}
|
||||
|
||||
func NewSimpleFragmenterWithSize(fragmentSize int) *SimpleFragmenter {
|
||||
func NewSimpleFragmenter(fragmentSize int) *SimpleFragmenter {
|
||||
return &SimpleFragmenter{
|
||||
fragmentSize: fragmentSize,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleFragmenter) Fragment(orig []byte, ot termLocations) []*Fragment {
|
||||
rv := make([]*Fragment, 0)
|
||||
func (s *SimpleFragmenter) Fragment(orig []byte, ot highlight.TermLocations) []*highlight.Fragment {
|
||||
rv := make([]*highlight.Fragment, 0)
|
||||
|
||||
maxbegin := 0
|
||||
for currTermIndex, termLocation := range ot {
|
||||
|
@ -65,7 +65,7 @@ func (s *SimpleFragmenter) Fragment(orig []byte, ot termLocations) []*Fragment {
|
|||
}
|
||||
|
||||
offset := roomToMove / 2
|
||||
rv = append(rv, &Fragment{orig: orig, start: start - offset, end: end - offset})
|
||||
rv = append(rv, &highlight.Fragment{Orig: orig, Start: start - offset, End: end - offset})
|
||||
// set maxbegin to the end of the current term location
|
||||
// so that next one won't back up to include it
|
||||
maxbegin = termLocation.End
|
||||
|
@ -79,8 +79,21 @@ func (s *SimpleFragmenter) Fragment(orig []byte, ot termLocations) []*Fragment {
|
|||
if end > len(orig) {
|
||||
end = len(orig)
|
||||
}
|
||||
rv = append(rv, &Fragment{orig: orig, start: start, end: end})
|
||||
rv = append(rv, &highlight.Fragment{Orig: orig, Start: start, End: end})
|
||||
}
|
||||
|
||||
return rv
|
||||
}
|
||||
|
||||
func Constructor(config map[string]interface{}, cache *registry.Cache) (highlight.Fragmenter, error) {
|
||||
size := defaultFragmentSize
|
||||
sizeVal, ok := config["size"].(float64)
|
||||
if ok {
|
||||
size = int(sizeVal)
|
||||
}
|
||||
return NewSimpleFragmenter(size), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.RegisterFragmenter(Name, Constructor)
|
||||
}
|
|
@ -6,31 +6,34 @@
|
|||
// 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 search
|
||||
|
||||
package simple
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
func TestSimpleFragmenter(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
orig []byte
|
||||
fragments []*Fragment
|
||||
ot termLocations
|
||||
fragments []*highlight.Fragment
|
||||
ot highlight.TermLocations
|
||||
}{
|
||||
{
|
||||
orig: []byte("this is a test"),
|
||||
fragments: []*Fragment{
|
||||
&Fragment{
|
||||
orig: []byte("this is a test"),
|
||||
start: 0,
|
||||
end: 14,
|
||||
fragments: []*highlight.Fragment{
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("this is a test"),
|
||||
Start: 0,
|
||||
End: 14,
|
||||
},
|
||||
},
|
||||
ot: termLocations{
|
||||
&termLocation{
|
||||
ot: highlight.TermLocations{
|
||||
&highlight.TermLocation{
|
||||
Term: "test",
|
||||
Pos: 4,
|
||||
Start: 10,
|
||||
|
@ -40,15 +43,15 @@ func TestSimpleFragmenter(t *testing.T) {
|
|||
},
|
||||
{
|
||||
orig: []byte("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"),
|
||||
fragments: []*Fragment{
|
||||
&Fragment{
|
||||
orig: []byte("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"),
|
||||
start: 0,
|
||||
end: 100,
|
||||
fragments: []*highlight.Fragment{
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"),
|
||||
Start: 0,
|
||||
End: 100,
|
||||
},
|
||||
},
|
||||
ot: termLocations{
|
||||
&termLocation{
|
||||
ot: highlight.TermLocations{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789",
|
||||
Pos: 1,
|
||||
Start: 0,
|
||||
|
@ -58,114 +61,114 @@ func TestSimpleFragmenter(t *testing.T) {
|
|||
},
|
||||
{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
fragments: []*Fragment{
|
||||
&Fragment{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
start: 0,
|
||||
end: 100,
|
||||
fragments: []*highlight.Fragment{
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
Start: 0,
|
||||
End: 100,
|
||||
},
|
||||
&Fragment{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
start: 10,
|
||||
end: 101,
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
Start: 10,
|
||||
End: 101,
|
||||
},
|
||||
&Fragment{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
start: 20,
|
||||
end: 101,
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
Start: 20,
|
||||
End: 101,
|
||||
},
|
||||
&Fragment{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
start: 30,
|
||||
end: 101,
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
Start: 30,
|
||||
End: 101,
|
||||
},
|
||||
&Fragment{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
start: 40,
|
||||
end: 101,
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
Start: 40,
|
||||
End: 101,
|
||||
},
|
||||
&Fragment{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
start: 50,
|
||||
end: 101,
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
Start: 50,
|
||||
End: 101,
|
||||
},
|
||||
&Fragment{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
start: 60,
|
||||
end: 101,
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
Start: 60,
|
||||
End: 101,
|
||||
},
|
||||
&Fragment{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
start: 70,
|
||||
end: 101,
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
Start: 70,
|
||||
End: 101,
|
||||
},
|
||||
&Fragment{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
start: 80,
|
||||
end: 101,
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
Start: 80,
|
||||
End: 101,
|
||||
},
|
||||
&Fragment{
|
||||
orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
start: 90,
|
||||
end: 101,
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"),
|
||||
Start: 90,
|
||||
End: 101,
|
||||
},
|
||||
},
|
||||
ot: termLocations{
|
||||
&termLocation{
|
||||
ot: highlight.TermLocations{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789",
|
||||
Pos: 1,
|
||||
Start: 0,
|
||||
End: 10,
|
||||
},
|
||||
&termLocation{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789",
|
||||
Pos: 2,
|
||||
Start: 10,
|
||||
End: 20,
|
||||
},
|
||||
&termLocation{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789",
|
||||
Pos: 3,
|
||||
Start: 20,
|
||||
End: 30,
|
||||
},
|
||||
&termLocation{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789",
|
||||
Pos: 4,
|
||||
Start: 30,
|
||||
End: 40,
|
||||
},
|
||||
&termLocation{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789",
|
||||
Pos: 5,
|
||||
Start: 40,
|
||||
End: 50,
|
||||
},
|
||||
&termLocation{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789",
|
||||
Pos: 6,
|
||||
Start: 50,
|
||||
End: 60,
|
||||
},
|
||||
&termLocation{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789",
|
||||
Pos: 7,
|
||||
Start: 60,
|
||||
End: 70,
|
||||
},
|
||||
&termLocation{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789",
|
||||
Pos: 8,
|
||||
Start: 70,
|
||||
End: 80,
|
||||
},
|
||||
&termLocation{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789",
|
||||
Pos: 9,
|
||||
Start: 80,
|
||||
End: 90,
|
||||
},
|
||||
&termLocation{
|
||||
&highlight.TermLocation{
|
||||
Term: "0123456789",
|
||||
Pos: 10,
|
||||
Start: 90,
|
||||
|
@ -175,7 +178,7 @@ func TestSimpleFragmenter(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
fragmenter := NewSimpleFragmenterWithSize(100)
|
||||
fragmenter := NewSimpleFragmenter(100)
|
||||
for _, test := range tests {
|
||||
fragments := fragmenter.Fragment(test.orig, test.ot)
|
||||
if !reflect.DeepEqual(fragments, test.fragments) {
|
||||
|
@ -191,31 +194,31 @@ func TestSimpleFragmenterWithSize(t *testing.T) {
|
|||
|
||||
tests := []struct {
|
||||
orig []byte
|
||||
fragments []*Fragment
|
||||
ot termLocations
|
||||
fragments []*highlight.Fragment
|
||||
ot highlight.TermLocations
|
||||
}{
|
||||
{
|
||||
orig: []byte("this is a test"),
|
||||
fragments: []*Fragment{
|
||||
&Fragment{
|
||||
orig: []byte("this is a test"),
|
||||
start: 0,
|
||||
end: 5,
|
||||
fragments: []*highlight.Fragment{
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("this is a test"),
|
||||
Start: 0,
|
||||
End: 5,
|
||||
},
|
||||
&Fragment{
|
||||
orig: []byte("this is a test"),
|
||||
start: 9,
|
||||
end: 14,
|
||||
&highlight.Fragment{
|
||||
Orig: []byte("this is a test"),
|
||||
Start: 9,
|
||||
End: 14,
|
||||
},
|
||||
},
|
||||
ot: termLocations{
|
||||
&termLocation{
|
||||
ot: highlight.TermLocations{
|
||||
&highlight.TermLocation{
|
||||
Term: "this",
|
||||
Pos: 1,
|
||||
Start: 0,
|
||||
End: 5,
|
||||
},
|
||||
&termLocation{
|
||||
&highlight.TermLocation{
|
||||
Term: "test",
|
||||
Pos: 4,
|
||||
Start: 10,
|
||||
|
@ -225,7 +228,7 @@ func TestSimpleFragmenterWithSize(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
fragmenter := NewSimpleFragmenterWithSize(5)
|
||||
fragmenter := NewSimpleFragmenter(5)
|
||||
for _, test := range tests {
|
||||
fragments := fragmenter.Fragment(test.orig, test.ot)
|
||||
if !reflect.DeepEqual(fragments, test.fragments) {
|
|
@ -6,35 +6,37 @@
|
|||
// 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 search
|
||||
|
||||
package highlight
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/document"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type Fragment struct {
|
||||
orig []byte
|
||||
start int
|
||||
end int
|
||||
score float64
|
||||
index int // used by heap
|
||||
Orig []byte
|
||||
Start int
|
||||
End int
|
||||
Score float64
|
||||
Index int // used by heap
|
||||
}
|
||||
|
||||
func (f *Fragment) Overlaps(other *Fragment) bool {
|
||||
if other.start >= f.start && other.start < f.end {
|
||||
if other.Start >= f.Start && other.Start < f.End {
|
||||
return true
|
||||
} else if f.start >= other.start && f.start < other.end {
|
||||
} else if f.Start >= other.Start && f.Start < other.End {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Fragmenter interface {
|
||||
Fragment([]byte, termLocations) []*Fragment
|
||||
Fragment([]byte, TermLocations) []*Fragment
|
||||
}
|
||||
|
||||
type FragmentFormatter interface {
|
||||
Format(f *Fragment, tlm TermLocationMap) string
|
||||
Format(f *Fragment, tlm search.TermLocationMap) string
|
||||
}
|
||||
|
||||
type FragmentScorer interface {
|
||||
|
@ -51,6 +53,6 @@ type Highlighter interface {
|
|||
Separator() string
|
||||
SetSeparator(string)
|
||||
|
||||
BestFragmentInField(*DocumentMatch, *document.Document, string) string
|
||||
BestFragmentsInField(*DocumentMatch, *document.Document, string, int) []string
|
||||
BestFragmentInField(*search.DocumentMatch, *document.Document, string) string
|
||||
BestFragmentsInField(*search.DocumentMatch, *document.Document, string, int) []string
|
||||
}
|
|
@ -6,29 +6,33 @@
|
|||
// 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 search
|
||||
|
||||
import ()
|
||||
package simple
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
// SimpleFragmentScorer will score fragments by how many
|
||||
// unique terms occur in the fragment with no regard for
|
||||
// any boost values used in the original query
|
||||
type SimpleFragmentScorer struct {
|
||||
tlm TermLocationMap
|
||||
tlm search.TermLocationMap
|
||||
}
|
||||
|
||||
func NewSimpleFragmentScorer(tlm TermLocationMap) *SimpleFragmentScorer {
|
||||
func NewSimpleFragmentScorer(tlm search.TermLocationMap) *SimpleFragmentScorer {
|
||||
return &SimpleFragmentScorer{
|
||||
tlm: tlm,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleFragmentScorer) Score(f *Fragment) {
|
||||
func (s *SimpleFragmentScorer) Score(f *highlight.Fragment) {
|
||||
score := 0.0
|
||||
OUTER:
|
||||
for _, locations := range s.tlm {
|
||||
for _, location := range locations {
|
||||
if int(location.Start) >= f.start && int(location.End) <= f.end {
|
||||
if 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
|
||||
|
@ -36,5 +40,5 @@ OUTER:
|
|||
}
|
||||
}
|
||||
}
|
||||
f.score = score
|
||||
f.Score = score
|
||||
}
|
|
@ -6,28 +6,32 @@
|
|||
// 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 search
|
||||
|
||||
package simple
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
func TestSimpleFragmentScorer(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
fragment *Fragment
|
||||
tlm TermLocationMap
|
||||
fragment *highlight.Fragment
|
||||
tlm search.TermLocationMap
|
||||
score float64
|
||||
}{
|
||||
{
|
||||
fragment: &Fragment{
|
||||
orig: []byte("cat in the hat"),
|
||||
start: 0,
|
||||
end: 14,
|
||||
fragment: &highlight.Fragment{
|
||||
Orig: []byte("cat in the hat"),
|
||||
Start: 0,
|
||||
End: 14,
|
||||
},
|
||||
tlm: TermLocationMap{
|
||||
"cat": Locations{
|
||||
&Location{
|
||||
tlm: search.TermLocationMap{
|
||||
"cat": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 0,
|
||||
End: 3,
|
||||
|
@ -37,21 +41,21 @@ func TestSimpleFragmentScorer(t *testing.T) {
|
|||
score: 1,
|
||||
},
|
||||
{
|
||||
fragment: &Fragment{
|
||||
orig: []byte("cat in the hat"),
|
||||
start: 0,
|
||||
end: 14,
|
||||
fragment: &highlight.Fragment{
|
||||
Orig: []byte("cat in the hat"),
|
||||
Start: 0,
|
||||
End: 14,
|
||||
},
|
||||
tlm: TermLocationMap{
|
||||
"cat": Locations{
|
||||
&Location{
|
||||
tlm: search.TermLocationMap{
|
||||
"cat": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 1,
|
||||
Start: 0,
|
||||
End: 3,
|
||||
},
|
||||
},
|
||||
"hat": Locations{
|
||||
&Location{
|
||||
"hat": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 4,
|
||||
Start: 11,
|
||||
End: 14,
|
||||
|
@ -65,8 +69,8 @@ func TestSimpleFragmentScorer(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
scorer := NewSimpleFragmentScorer(test.tlm)
|
||||
scorer.Score(test.fragment)
|
||||
if test.fragment.score != test.score {
|
||||
t.Errorf("expected score %f, got %f", test.score, test.fragment.score)
|
||||
if test.fragment.Score != test.score {
|
||||
t.Errorf("expected score %f, got %f", test.score, test.fragment.Score)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,43 +6,49 @@
|
|||
// 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 search
|
||||
|
||||
package simple
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"fmt"
|
||||
|
||||
"github.com/blevesearch/bleve/document"
|
||||
"github.com/blevesearch/bleve/registry"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/highlight"
|
||||
)
|
||||
|
||||
const DEFAULT_SEPARATOR = "…"
|
||||
const Name = "simple"
|
||||
const defaultSeparator = "…"
|
||||
|
||||
type SimpleHighlighter struct {
|
||||
fragmenter Fragmenter
|
||||
formatter FragmentFormatter
|
||||
fragmenter highlight.Fragmenter
|
||||
formatter highlight.FragmentFormatter
|
||||
sep string
|
||||
}
|
||||
|
||||
func NewSimpleHighlighter() *SimpleHighlighter {
|
||||
func NewSimpleHighlighter(fragmenter highlight.Fragmenter, formatter highlight.FragmentFormatter, separator string) *SimpleHighlighter {
|
||||
return &SimpleHighlighter{
|
||||
fragmenter: NewSimpleFragmenter(),
|
||||
formatter: NewANSIFragmentFormatter(),
|
||||
sep: DEFAULT_SEPARATOR,
|
||||
fragmenter: fragmenter,
|
||||
formatter: formatter,
|
||||
sep: separator,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleHighlighter) Fragmenter() Fragmenter {
|
||||
func (s *SimpleHighlighter) Fragmenter() highlight.Fragmenter {
|
||||
return s.fragmenter
|
||||
}
|
||||
|
||||
func (s *SimpleHighlighter) SetFragmenter(f Fragmenter) {
|
||||
func (s *SimpleHighlighter) SetFragmenter(f highlight.Fragmenter) {
|
||||
s.fragmenter = f
|
||||
}
|
||||
|
||||
func (s *SimpleHighlighter) FragmentFormatter() FragmentFormatter {
|
||||
func (s *SimpleHighlighter) FragmentFormatter() highlight.FragmentFormatter {
|
||||
return s.formatter
|
||||
}
|
||||
|
||||
func (s *SimpleHighlighter) SetFragmentFormatter(f FragmentFormatter) {
|
||||
func (s *SimpleHighlighter) SetFragmentFormatter(f highlight.FragmentFormatter) {
|
||||
s.formatter = f
|
||||
}
|
||||
|
||||
|
@ -54,7 +60,7 @@ func (s *SimpleHighlighter) SetSeparator(sep string) {
|
|||
s.sep = sep
|
||||
}
|
||||
|
||||
func (s *SimpleHighlighter) BestFragmentInField(dm *DocumentMatch, doc *document.Document, field string) string {
|
||||
func (s *SimpleHighlighter) BestFragmentInField(dm *search.DocumentMatch, doc *document.Document, field string) string {
|
||||
fragments := s.BestFragmentsInField(dm, doc, field, 1)
|
||||
if len(fragments) > 0 {
|
||||
return fragments[0]
|
||||
|
@ -62,9 +68,9 @@ func (s *SimpleHighlighter) BestFragmentInField(dm *DocumentMatch, doc *document
|
|||
return ""
|
||||
}
|
||||
|
||||
func (s *SimpleHighlighter) BestFragmentsInField(dm *DocumentMatch, doc *document.Document, field string, num int) []string {
|
||||
func (s *SimpleHighlighter) BestFragmentsInField(dm *search.DocumentMatch, doc *document.Document, field string, num int) []string {
|
||||
tlm := dm.Locations[field]
|
||||
orderedTermLocations := OrderTermLocations(tlm)
|
||||
orderedTermLocations := highlight.OrderTermLocations(tlm)
|
||||
scorer := NewSimpleFragmentScorer(dm.Locations[field])
|
||||
|
||||
// score the fragments and put them into a priority queue ordered by score
|
||||
|
@ -85,7 +91,7 @@ func (s *SimpleHighlighter) BestFragmentsInField(dm *DocumentMatch, doc *documen
|
|||
}
|
||||
|
||||
// now find the N best non-overlapping fragments
|
||||
bestFragments := make([]*Fragment, 0)
|
||||
bestFragments := make([]*highlight.Fragment, 0)
|
||||
if len(fq) > 0 {
|
||||
candidate := heap.Pop(&fq)
|
||||
OUTER:
|
||||
|
@ -93,7 +99,7 @@ func (s *SimpleHighlighter) BestFragmentsInField(dm *DocumentMatch, doc *documen
|
|||
// see if this overlaps with any of the best already identified
|
||||
if len(bestFragments) > 0 {
|
||||
for _, frag := range bestFragments {
|
||||
if candidate.(*Fragment).Overlaps(frag) {
|
||||
if candidate.(*highlight.Fragment).Overlaps(frag) {
|
||||
if len(fq) < 1 {
|
||||
break OUTER
|
||||
}
|
||||
|
@ -101,9 +107,9 @@ func (s *SimpleHighlighter) BestFragmentsInField(dm *DocumentMatch, doc *documen
|
|||
continue OUTER
|
||||
}
|
||||
}
|
||||
bestFragments = append(bestFragments, candidate.(*Fragment))
|
||||
bestFragments = append(bestFragments, candidate.(*highlight.Fragment))
|
||||
} else {
|
||||
bestFragments = append(bestFragments, candidate.(*Fragment))
|
||||
bestFragments = append(bestFragments, candidate.(*highlight.Fragment))
|
||||
}
|
||||
|
||||
if len(fq) < 1 {
|
||||
|
@ -117,17 +123,17 @@ func (s *SimpleHighlighter) BestFragmentsInField(dm *DocumentMatch, doc *documen
|
|||
formattedFragments := make([]string, len(bestFragments))
|
||||
for i, fragment := range bestFragments {
|
||||
formattedFragments[i] = ""
|
||||
if fragment.start != 0 {
|
||||
if fragment.Start != 0 {
|
||||
formattedFragments[i] += s.sep
|
||||
}
|
||||
formattedFragments[i] += s.formatter.Format(fragment, dm.Locations[field])
|
||||
if fragment.end != len(fragment.orig) {
|
||||
if fragment.End != len(fragment.Orig) {
|
||||
formattedFragments[i] += s.sep
|
||||
}
|
||||
}
|
||||
|
||||
if dm.Fragments == nil {
|
||||
dm.Fragments = make(FieldFragmentMap, 0)
|
||||
dm.Fragments = make(search.FieldFragmentMap, 0)
|
||||
}
|
||||
dm.Fragments[field] = formattedFragments
|
||||
|
||||
|
@ -135,25 +141,25 @@ func (s *SimpleHighlighter) BestFragmentsInField(dm *DocumentMatch, doc *documen
|
|||
}
|
||||
|
||||
// A PriorityQueue implements heap.Interface and holds Items.
|
||||
type FragmentQueue []*Fragment
|
||||
type FragmentQueue []*highlight.Fragment
|
||||
|
||||
func (fq FragmentQueue) Len() int { return len(fq) }
|
||||
|
||||
func (fq FragmentQueue) Less(i, j int) bool {
|
||||
// We want Pop to give us the highest, not lowest, priority so we use greater than here.
|
||||
return fq[i].score > fq[j].score
|
||||
return fq[i].Score > fq[j].Score
|
||||
}
|
||||
|
||||
func (fq FragmentQueue) Swap(i, j int) {
|
||||
fq[i], fq[j] = fq[j], fq[i]
|
||||
fq[i].index = i
|
||||
fq[j].index = j
|
||||
fq[i].Index = i
|
||||
fq[j].Index = j
|
||||
}
|
||||
|
||||
func (fq *FragmentQueue) Push(x interface{}) {
|
||||
n := len(*fq)
|
||||
item := x.(*Fragment)
|
||||
item.index = n
|
||||
item := x.(*highlight.Fragment)
|
||||
item.Index = n
|
||||
*fq = append(*fq, item)
|
||||
}
|
||||
|
||||
|
@ -161,7 +167,39 @@ func (fq *FragmentQueue) Pop() interface{} {
|
|||
old := *fq
|
||||
n := len(old)
|
||||
item := old[n-1]
|
||||
item.index = -1 // for safety
|
||||
item.Index = -1 // for safety
|
||||
*fq = old[0 : n-1]
|
||||
return item
|
||||
}
|
||||
|
||||
func Constructor(config map[string]interface{}, cache *registry.Cache) (highlight.Highlighter, error) {
|
||||
separator := defaultSeparator
|
||||
separatorVal, ok := config["separator"].(string)
|
||||
if ok {
|
||||
separator = separatorVal
|
||||
}
|
||||
|
||||
fragmenterName, ok := config["fragmenter"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("must specify fragmenter")
|
||||
}
|
||||
fragmenter, err := cache.FragmenterNamed(fragmenterName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building fragmenter: %v", err)
|
||||
}
|
||||
|
||||
formatterName, ok := config["formatter"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("must specify formatter")
|
||||
}
|
||||
formatter, err := cache.FragmentFormatterNamed(formatterName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building fragment formatter: %v", err)
|
||||
}
|
||||
|
||||
return NewSimpleHighlighter(fragmenter, formatter, separator), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.RegisterHighlighter(Name, Constructor)
|
||||
}
|
|
@ -6,33 +6,43 @@
|
|||
// 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 search
|
||||
|
||||
package simple
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/document"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/highlight/fragment_formatters/ansi"
|
||||
sfrag "github.com/blevesearch/bleve/search/highlight/fragmenters/simple"
|
||||
)
|
||||
|
||||
const (
|
||||
reset = "\x1b[0m"
|
||||
DEFAULT_ANSI_HIGHLIGHT = "\x1b[43m"
|
||||
)
|
||||
|
||||
func TestSimpleHighlighter(t *testing.T) {
|
||||
fragmenter := sfrag.NewSimpleFragmenter(100)
|
||||
formatter := ansi.NewANSIFragmentFormatter()
|
||||
highlighter := NewSimpleHighlighter(fragmenter, formatter, defaultSeparator)
|
||||
|
||||
highlighter := NewSimpleHighlighter()
|
||||
|
||||
docMatch := DocumentMatch{
|
||||
docMatch := search.DocumentMatch{
|
||||
ID: "a",
|
||||
Score: 1.0,
|
||||
Locations: FieldTermLocationMap{
|
||||
"desc": TermLocationMap{
|
||||
"quick": Locations{
|
||||
&Location{
|
||||
Locations: search.FieldTermLocationMap{
|
||||
"desc": search.TermLocationMap{
|
||||
"quick": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 2,
|
||||
Start: 4,
|
||||
End: 9,
|
||||
},
|
||||
},
|
||||
"fox": Locations{
|
||||
&Location{
|
||||
"fox": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 4,
|
||||
Start: 16,
|
||||
End: 19,
|
||||
|
@ -64,67 +74,67 @@ Fusce viverra eleifend iaculis. Maecenas tempor dictum cursus. Mauris faucibus,
|
|||
Etiam vel augue vel nisl commodo suscipit et ac nisl. Quisque eros diam, porttitor et aliquet sed, vulputate in odio. Aenean feugiat est quis neque vehicula, eget vulputate nunc tempor. Donec quis nulla ut quam feugiat consectetur ut et justo. Nulla congue, metus auctor facilisis scelerisque, nunc risus vulputate urna, in blandit urna nibh et neque. Etiam quis tortor ut nulla dignissim dictum non sed ligula. Vivamus accumsan ligula eget ipsum ultrices, a tincidunt urna blandit. In hac habitasse platea dictumst.`)
|
||||
|
||||
doc := document.NewDocument("a").AddField(document.NewTextField("full", []uint64{}, fieldBytes))
|
||||
docMatch := DocumentMatch{
|
||||
docMatch := search.DocumentMatch{
|
||||
ID: "a",
|
||||
Score: 1.0,
|
||||
Locations: FieldTermLocationMap{
|
||||
"full": TermLocationMap{
|
||||
"metus": Locations{
|
||||
&Location{
|
||||
Locations: search.FieldTermLocationMap{
|
||||
"full": search.TermLocationMap{
|
||||
"metus": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 883,
|
||||
End: 888,
|
||||
},
|
||||
&Location{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 915,
|
||||
End: 920,
|
||||
},
|
||||
&Location{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 2492,
|
||||
End: 2497,
|
||||
},
|
||||
&Location{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 2822,
|
||||
End: 2827,
|
||||
},
|
||||
&Location{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 3417,
|
||||
End: 3422,
|
||||
},
|
||||
},
|
||||
"interdum": Locations{
|
||||
&Location{
|
||||
"interdum": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 1891,
|
||||
End: 1899,
|
||||
},
|
||||
&Location{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 2813,
|
||||
End: 2821,
|
||||
},
|
||||
},
|
||||
"venenatis": Locations{
|
||||
&Location{
|
||||
"venenatis": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 954,
|
||||
End: 963,
|
||||
},
|
||||
&Location{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 1252,
|
||||
End: 1261,
|
||||
},
|
||||
&Location{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 1795,
|
||||
End: 1804,
|
||||
},
|
||||
&Location{
|
||||
&search.Location{
|
||||
Pos: 0,
|
||||
Start: 2803,
|
||||
End: 2812,
|
||||
|
@ -142,8 +152,9 @@ Etiam vel augue vel nisl commodo suscipit et ac nisl. Quisque eros diam, porttit
|
|||
"… accumsan. Vivamus eros felis, rhoncus vel " + DEFAULT_ANSI_HIGHLIGHT + "interdum" + reset + " bibendum, imperdiet nec diam. Etiam sed eros sed…",
|
||||
}
|
||||
|
||||
highlighter := NewSimpleHighlighter()
|
||||
highlighter.SetFragmenter(NewSimpleFragmenterWithSize(100))
|
||||
fragmenter := sfrag.NewSimpleFragmenter(100)
|
||||
formatter := ansi.NewANSIFragmentFormatter()
|
||||
highlighter := NewSimpleHighlighter(fragmenter, formatter, defaultSeparator)
|
||||
fragments := highlighter.BestFragmentsInField(&docMatch, doc, "full", 5)
|
||||
|
||||
if !reflect.DeepEqual(fragments, expectedFragments) {
|
|
@ -6,30 +6,33 @@
|
|||
// 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 search
|
||||
|
||||
package highlight
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type termLocation struct {
|
||||
type TermLocation struct {
|
||||
Term string
|
||||
Pos int
|
||||
Start int
|
||||
End int
|
||||
}
|
||||
|
||||
type termLocations []*termLocation
|
||||
type TermLocations []*TermLocation
|
||||
|
||||
func (t termLocations) Len() int { return len(t) }
|
||||
func (t termLocations) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
|
||||
func (t termLocations) Less(i, j int) bool { return t[i].Start < t[j].Start }
|
||||
func (t TermLocations) Len() int { return len(t) }
|
||||
func (t TermLocations) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
|
||||
func (t TermLocations) Less(i, j int) bool { return t[i].Start < t[j].Start }
|
||||
|
||||
func OrderTermLocations(tlm TermLocationMap) termLocations {
|
||||
rv := make(termLocations, 0)
|
||||
func OrderTermLocations(tlm search.TermLocationMap) TermLocations {
|
||||
rv := make(TermLocations, 0)
|
||||
for term, locations := range tlm {
|
||||
for _, location := range locations {
|
||||
tl := termLocation{
|
||||
tl := TermLocation{
|
||||
Term: term,
|
||||
Pos: int(location.Pos),
|
||||
Start: int(location.Start),
|
|
@ -6,9 +6,11 @@
|
|||
// 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 search
|
||||
package scorers
|
||||
|
||||
import ()
|
||||
import (
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type ConjunctionQueryScorer struct {
|
||||
explain bool
|
||||
|
@ -20,18 +22,18 @@ func NewConjunctionQueryScorer(explain bool) *ConjunctionQueryScorer {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *ConjunctionQueryScorer) Score(constituents []*DocumentMatch) *DocumentMatch {
|
||||
rv := DocumentMatch{
|
||||
func (s *ConjunctionQueryScorer) Score(constituents []*search.DocumentMatch) *search.DocumentMatch {
|
||||
rv := search.DocumentMatch{
|
||||
ID: constituents[0].ID,
|
||||
}
|
||||
|
||||
var sum float64
|
||||
var childrenExplanations []*Explanation
|
||||
var childrenExplanations []*search.Explanation
|
||||
if s.explain {
|
||||
childrenExplanations = make([]*Explanation, len(constituents))
|
||||
childrenExplanations = make([]*search.Explanation, len(constituents))
|
||||
}
|
||||
|
||||
locations := []FieldTermLocationMap{}
|
||||
locations := []search.FieldTermLocationMap{}
|
||||
for i, docMatch := range constituents {
|
||||
sum += docMatch.Score
|
||||
if s.explain {
|
||||
|
@ -43,13 +45,13 @@ func (s *ConjunctionQueryScorer) Score(constituents []*DocumentMatch) *DocumentM
|
|||
}
|
||||
rv.Score = sum
|
||||
if s.explain {
|
||||
rv.Expl = &Explanation{Value: sum, Message: "sum of:", Children: childrenExplanations}
|
||||
rv.Expl = &search.Explanation{Value: sum, Message: "sum of:", Children: childrenExplanations}
|
||||
}
|
||||
|
||||
if len(locations) == 1 {
|
||||
rv.Locations = locations[0]
|
||||
} else if len(locations) > 1 {
|
||||
rv.Locations = mergeLocations(locations)
|
||||
rv.Locations = search.MergeLocations(locations)
|
||||
}
|
||||
|
||||
return &rv
|
|
@ -6,10 +6,12 @@
|
|||
// 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 search
|
||||
package scorers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type ConstantScorer struct {
|
||||
|
@ -18,7 +20,7 @@ type ConstantScorer struct {
|
|||
explain bool
|
||||
queryNorm float64
|
||||
queryWeight float64
|
||||
queryWeightExplanation *Explanation
|
||||
queryWeightExplanation *search.Explanation
|
||||
}
|
||||
|
||||
func NewConstantScorer(constant float64, boost float64, explain bool) *ConstantScorer {
|
||||
|
@ -44,16 +46,16 @@ func (s *ConstantScorer) SetQueryNorm(qnorm float64) {
|
|||
s.queryWeight = s.boost * s.queryNorm
|
||||
|
||||
if s.explain {
|
||||
childrenExplanations := make([]*Explanation, 2)
|
||||
childrenExplanations[0] = &Explanation{
|
||||
childrenExplanations := make([]*search.Explanation, 2)
|
||||
childrenExplanations[0] = &search.Explanation{
|
||||
Value: s.boost,
|
||||
Message: "boost",
|
||||
}
|
||||
childrenExplanations[1] = &Explanation{
|
||||
childrenExplanations[1] = &search.Explanation{
|
||||
Value: s.queryNorm,
|
||||
Message: "queryNorm",
|
||||
}
|
||||
s.queryWeightExplanation = &Explanation{
|
||||
s.queryWeightExplanation = &search.Explanation{
|
||||
Value: s.queryWeight,
|
||||
Message: fmt.Sprintf("ConstantScore()^%f, product of:", s.boost),
|
||||
Children: childrenExplanations,
|
||||
|
@ -61,13 +63,13 @@ func (s *ConstantScorer) SetQueryNorm(qnorm float64) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *ConstantScorer) Score(id string) *DocumentMatch {
|
||||
var scoreExplanation *Explanation
|
||||
func (s *ConstantScorer) Score(id string) *search.DocumentMatch {
|
||||
var scoreExplanation *search.Explanation
|
||||
|
||||
score := s.constant
|
||||
|
||||
if s.explain {
|
||||
scoreExplanation = &Explanation{
|
||||
scoreExplanation = &search.Explanation{
|
||||
Value: score,
|
||||
Message: fmt.Sprintf("ConstantScore()"),
|
||||
}
|
||||
|
@ -77,10 +79,10 @@ func (s *ConstantScorer) Score(id string) *DocumentMatch {
|
|||
if s.queryWeight != 1.0 {
|
||||
score = score * s.queryWeight
|
||||
if s.explain {
|
||||
childExplanations := make([]*Explanation, 2)
|
||||
childExplanations := make([]*search.Explanation, 2)
|
||||
childExplanations[0] = s.queryWeightExplanation
|
||||
childExplanations[1] = scoreExplanation
|
||||
scoreExplanation = &Explanation{
|
||||
scoreExplanation = &search.Explanation{
|
||||
Value: score,
|
||||
Message: fmt.Sprintf("weight(^%f), product of:", s.boost),
|
||||
Children: childExplanations,
|
||||
|
@ -88,7 +90,7 @@ func (s *ConstantScorer) Score(id string) *DocumentMatch {
|
|||
}
|
||||
}
|
||||
|
||||
rv := DocumentMatch{
|
||||
rv := search.DocumentMatch{
|
||||
ID: id,
|
||||
Score: score,
|
||||
}
|
|
@ -6,10 +6,12 @@
|
|||
// 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 search
|
||||
package scorers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type DisjunctionQueryScorer struct {
|
||||
|
@ -22,18 +24,18 @@ func NewDisjunctionQueryScorer(explain bool) *DisjunctionQueryScorer {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *DisjunctionQueryScorer) Score(constituents []*DocumentMatch, countMatch, countTotal int) *DocumentMatch {
|
||||
rv := DocumentMatch{
|
||||
func (s *DisjunctionQueryScorer) Score(constituents []*search.DocumentMatch, countMatch, countTotal int) *search.DocumentMatch {
|
||||
rv := search.DocumentMatch{
|
||||
ID: constituents[0].ID,
|
||||
}
|
||||
|
||||
var sum float64
|
||||
var childrenExplanations []*Explanation
|
||||
var childrenExplanations []*search.Explanation
|
||||
if s.explain {
|
||||
childrenExplanations = make([]*Explanation, len(constituents))
|
||||
childrenExplanations = make([]*search.Explanation, len(constituents))
|
||||
}
|
||||
|
||||
locations := []FieldTermLocationMap{}
|
||||
locations := []search.FieldTermLocationMap{}
|
||||
for i, docMatch := range constituents {
|
||||
sum += docMatch.Score
|
||||
if s.explain {
|
||||
|
@ -44,24 +46,24 @@ func (s *DisjunctionQueryScorer) Score(constituents []*DocumentMatch, countMatch
|
|||
}
|
||||
}
|
||||
|
||||
var rawExpl *Explanation
|
||||
var rawExpl *search.Explanation
|
||||
if s.explain {
|
||||
rawExpl = &Explanation{Value: sum, Message: "sum of:", Children: childrenExplanations}
|
||||
rawExpl = &search.Explanation{Value: sum, Message: "sum of:", Children: childrenExplanations}
|
||||
}
|
||||
|
||||
coord := float64(countMatch) / float64(countTotal)
|
||||
rv.Score = sum * coord
|
||||
if s.explain {
|
||||
ce := make([]*Explanation, 2)
|
||||
ce := make([]*search.Explanation, 2)
|
||||
ce[0] = rawExpl
|
||||
ce[1] = &Explanation{Value: coord, Message: fmt.Sprintf("coord(%d/%d)", countMatch, countTotal)}
|
||||
rv.Expl = &Explanation{Value: rv.Score, Message: "product of:", Children: ce}
|
||||
ce[1] = &search.Explanation{Value: coord, Message: fmt.Sprintf("coord(%d/%d)", countMatch, countTotal)}
|
||||
rv.Expl = &search.Explanation{Value: rv.Score, Message: "product of:", Children: ce}
|
||||
}
|
||||
|
||||
if len(locations) == 1 {
|
||||
rv.Locations = locations[0]
|
||||
} else if len(locations) > 1 {
|
||||
rv.Locations = mergeLocations(locations)
|
||||
rv.Locations = search.MergeLocations(locations)
|
||||
}
|
||||
|
||||
return &rv
|
|
@ -6,13 +6,14 @@
|
|||
// 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 search
|
||||
package scorers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
const MAX_SCORE_CACHE = 64
|
||||
|
@ -25,10 +26,10 @@ type TermQueryScorer struct {
|
|||
docTotal uint64
|
||||
idf float64
|
||||
explain bool
|
||||
idfExplanation *Explanation
|
||||
idfExplanation *search.Explanation
|
||||
queryNorm float64
|
||||
queryWeight float64
|
||||
queryWeightExplanation *Explanation
|
||||
queryWeightExplanation *search.Explanation
|
||||
}
|
||||
|
||||
func NewTermQueryScorer(queryTerm string, queryField string, queryBoost float64, docTotal, docTerm uint64, explain bool) *TermQueryScorer {
|
||||
|
@ -44,7 +45,7 @@ func NewTermQueryScorer(queryTerm string, queryField string, queryBoost float64,
|
|||
}
|
||||
|
||||
if explain {
|
||||
rv.idfExplanation = &Explanation{
|
||||
rv.idfExplanation = &search.Explanation{
|
||||
Value: rv.idf,
|
||||
Message: fmt.Sprintf("idf(docFreq=%d, maxDocs=%d)", docTerm, docTotal),
|
||||
}
|
||||
|
@ -65,17 +66,17 @@ func (s *TermQueryScorer) SetQueryNorm(qnorm float64) {
|
|||
s.queryWeight = s.queryBoost * s.idf * s.queryNorm
|
||||
|
||||
if s.explain {
|
||||
childrenExplanations := make([]*Explanation, 3)
|
||||
childrenExplanations[0] = &Explanation{
|
||||
childrenExplanations := make([]*search.Explanation, 3)
|
||||
childrenExplanations[0] = &search.Explanation{
|
||||
Value: s.queryBoost,
|
||||
Message: "boost",
|
||||
}
|
||||
childrenExplanations[1] = s.idfExplanation
|
||||
childrenExplanations[2] = &Explanation{
|
||||
childrenExplanations[2] = &search.Explanation{
|
||||
Value: s.queryNorm,
|
||||
Message: "queryNorm",
|
||||
}
|
||||
s.queryWeightExplanation = &Explanation{
|
||||
s.queryWeightExplanation = &search.Explanation{
|
||||
Value: s.queryWeight,
|
||||
Message: fmt.Sprintf("queryWeight(%s:%s^%f), product of:", s.queryField, string(s.queryTerm), s.queryBoost),
|
||||
Children: childrenExplanations,
|
||||
|
@ -83,8 +84,8 @@ func (s *TermQueryScorer) SetQueryNorm(qnorm float64) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *TermQueryScorer) Score(termMatch *index.TermFieldDoc) *DocumentMatch {
|
||||
var scoreExplanation *Explanation
|
||||
func (s *TermQueryScorer) Score(termMatch *index.TermFieldDoc) *search.DocumentMatch {
|
||||
var scoreExplanation *search.Explanation
|
||||
|
||||
// need to compute score
|
||||
var tf float64
|
||||
|
@ -96,17 +97,17 @@ func (s *TermQueryScorer) Score(termMatch *index.TermFieldDoc) *DocumentMatch {
|
|||
score := tf * termMatch.Norm * s.idf
|
||||
|
||||
if s.explain {
|
||||
childrenExplanations := make([]*Explanation, 3)
|
||||
childrenExplanations[0] = &Explanation{
|
||||
childrenExplanations := make([]*search.Explanation, 3)
|
||||
childrenExplanations[0] = &search.Explanation{
|
||||
Value: tf,
|
||||
Message: fmt.Sprintf("tf(termFreq(%s:%s)=%d", s.queryField, string(s.queryTerm), termMatch.Freq),
|
||||
}
|
||||
childrenExplanations[1] = &Explanation{
|
||||
childrenExplanations[1] = &search.Explanation{
|
||||
Value: termMatch.Norm,
|
||||
Message: fmt.Sprintf("fieldNorm(field=%s, doc=%s)", s.queryField, termMatch.ID),
|
||||
}
|
||||
childrenExplanations[2] = s.idfExplanation
|
||||
scoreExplanation = &Explanation{
|
||||
scoreExplanation = &search.Explanation{
|
||||
Value: score,
|
||||
Message: fmt.Sprintf("fieldWeight(%s:%s in %s), product of:", s.queryField, string(s.queryTerm), termMatch.ID),
|
||||
Children: childrenExplanations,
|
||||
|
@ -117,10 +118,10 @@ func (s *TermQueryScorer) Score(termMatch *index.TermFieldDoc) *DocumentMatch {
|
|||
if s.queryWeight != 1.0 {
|
||||
score = score * s.queryWeight
|
||||
if s.explain {
|
||||
childExplanations := make([]*Explanation, 2)
|
||||
childExplanations := make([]*search.Explanation, 2)
|
||||
childExplanations[0] = s.queryWeightExplanation
|
||||
childExplanations[1] = scoreExplanation
|
||||
scoreExplanation = &Explanation{
|
||||
scoreExplanation = &search.Explanation{
|
||||
Value: score,
|
||||
Message: fmt.Sprintf("weight(%s:%s^%f in %s), product of:", s.queryField, string(s.queryTerm), s.queryBoost, termMatch.ID),
|
||||
Children: childExplanations,
|
||||
|
@ -128,7 +129,7 @@ func (s *TermQueryScorer) Score(termMatch *index.TermFieldDoc) *DocumentMatch {
|
|||
}
|
||||
}
|
||||
|
||||
rv := DocumentMatch{
|
||||
rv := search.DocumentMatch{
|
||||
ID: termMatch.ID,
|
||||
Score: score,
|
||||
}
|
||||
|
@ -138,14 +139,14 @@ func (s *TermQueryScorer) Score(termMatch *index.TermFieldDoc) *DocumentMatch {
|
|||
|
||||
if termMatch.Vectors != nil && len(termMatch.Vectors) > 0 {
|
||||
|
||||
rv.Locations = make(FieldTermLocationMap)
|
||||
rv.Locations = make(search.FieldTermLocationMap)
|
||||
for _, v := range termMatch.Vectors {
|
||||
tlm := rv.Locations[v.Field]
|
||||
if tlm == nil {
|
||||
tlm = make(TermLocationMap)
|
||||
tlm = make(search.TermLocationMap)
|
||||
}
|
||||
|
||||
loc := Location{
|
||||
loc := search.Location{
|
||||
Pos: float64(v.Pos),
|
||||
Start: float64(v.Start),
|
||||
End: float64(v.End),
|
||||
|
@ -153,7 +154,7 @@ func (s *TermQueryScorer) Score(termMatch *index.TermFieldDoc) *DocumentMatch {
|
|||
|
||||
locations := tlm[s.queryTerm]
|
||||
if locations == nil {
|
||||
locations = make(Locations, 1)
|
||||
locations = make(search.Locations, 1)
|
||||
locations[0] = &loc
|
||||
} else {
|
||||
locations = append(locations, &loc)
|
|
@ -6,7 +6,7 @@
|
|||
// 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 search
|
||||
package scorers
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
@ -14,6 +14,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
func TestTermScorer(t *testing.T) {
|
||||
|
@ -28,7 +29,7 @@ func TestTermScorer(t *testing.T) {
|
|||
|
||||
tests := []struct {
|
||||
termMatch *index.TermFieldDoc
|
||||
result *DocumentMatch
|
||||
result *search.DocumentMatch
|
||||
}{
|
||||
// test some simple math
|
||||
{
|
||||
|
@ -45,31 +46,31 @@ func TestTermScorer(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
result: &DocumentMatch{
|
||||
result: &search.DocumentMatch{
|
||||
ID: "one",
|
||||
Score: math.Sqrt(1.0) * idf,
|
||||
Expl: &Explanation{
|
||||
Expl: &search.Explanation{
|
||||
Value: math.Sqrt(1.0) * idf,
|
||||
Message: "fieldWeight(desc:beer in one), product of:",
|
||||
Children: []*Explanation{
|
||||
&Explanation{
|
||||
Children: []*search.Explanation{
|
||||
&search.Explanation{
|
||||
Value: 1,
|
||||
Message: "tf(termFreq(desc:beer)=1",
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: 1,
|
||||
Message: "fieldNorm(field=desc, doc=one)",
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: idf,
|
||||
Message: "idf(docFreq=9, maxDocs=100)",
|
||||
},
|
||||
},
|
||||
},
|
||||
Locations: FieldTermLocationMap{
|
||||
"desc": TermLocationMap{
|
||||
"beer": Locations{
|
||||
&Location{
|
||||
Locations: search.FieldTermLocationMap{
|
||||
"desc": search.TermLocationMap{
|
||||
"beer": search.Locations{
|
||||
&search.Location{
|
||||
Pos: 1,
|
||||
Start: 0,
|
||||
End: 4,
|
||||
|
@ -86,22 +87,22 @@ func TestTermScorer(t *testing.T) {
|
|||
Freq: 1,
|
||||
Norm: 1.0,
|
||||
},
|
||||
result: &DocumentMatch{
|
||||
result: &search.DocumentMatch{
|
||||
ID: "one",
|
||||
Score: math.Sqrt(1.0) * idf,
|
||||
Expl: &Explanation{
|
||||
Expl: &search.Explanation{
|
||||
Value: math.Sqrt(1.0) * idf,
|
||||
Message: "fieldWeight(desc:beer in one), product of:",
|
||||
Children: []*Explanation{
|
||||
&Explanation{
|
||||
Children: []*search.Explanation{
|
||||
&search.Explanation{
|
||||
Value: 1,
|
||||
Message: "tf(termFreq(desc:beer)=1",
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: 1,
|
||||
Message: "fieldNorm(field=desc, doc=one)",
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: idf,
|
||||
Message: "idf(docFreq=9, maxDocs=100)",
|
||||
},
|
||||
|
@ -116,22 +117,22 @@ func TestTermScorer(t *testing.T) {
|
|||
Freq: 65,
|
||||
Norm: 1.0,
|
||||
},
|
||||
result: &DocumentMatch{
|
||||
result: &search.DocumentMatch{
|
||||
ID: "one",
|
||||
Score: math.Sqrt(65) * idf,
|
||||
Expl: &Explanation{
|
||||
Expl: &search.Explanation{
|
||||
Value: math.Sqrt(65) * idf,
|
||||
Message: "fieldWeight(desc:beer in one), product of:",
|
||||
Children: []*Explanation{
|
||||
&Explanation{
|
||||
Children: []*search.Explanation{
|
||||
&search.Explanation{
|
||||
Value: math.Sqrt(65),
|
||||
Message: "tf(termFreq(desc:beer)=65",
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: 1,
|
||||
Message: "fieldNorm(field=desc, doc=one)",
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: idf,
|
||||
Message: "idf(docFreq=9, maxDocs=100)",
|
||||
},
|
||||
|
@ -171,7 +172,7 @@ func TestTermScorerWithQueryNorm(t *testing.T) {
|
|||
|
||||
tests := []struct {
|
||||
termMatch *index.TermFieldDoc
|
||||
result *DocumentMatch
|
||||
result *search.DocumentMatch
|
||||
}{
|
||||
{
|
||||
termMatch: &index.TermFieldDoc{
|
||||
|
@ -179,44 +180,44 @@ func TestTermScorerWithQueryNorm(t *testing.T) {
|
|||
Freq: 1,
|
||||
Norm: 1.0,
|
||||
},
|
||||
result: &DocumentMatch{
|
||||
result: &search.DocumentMatch{
|
||||
ID: "one",
|
||||
Score: math.Sqrt(1.0) * idf * 3.0 * idf * 2.0,
|
||||
Expl: &Explanation{
|
||||
Expl: &search.Explanation{
|
||||
Value: math.Sqrt(1.0) * idf * 3.0 * idf * 2.0,
|
||||
Message: "weight(desc:beer^3.000000 in one), product of:",
|
||||
Children: []*Explanation{
|
||||
&Explanation{
|
||||
Children: []*search.Explanation{
|
||||
&search.Explanation{
|
||||
Value: 2.0 * idf * 3.0,
|
||||
Message: "queryWeight(desc:beer^3.000000), product of:",
|
||||
Children: []*Explanation{
|
||||
&Explanation{
|
||||
Children: []*search.Explanation{
|
||||
&search.Explanation{
|
||||
Value: 3,
|
||||
Message: "boost",
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: idf,
|
||||
Message: "idf(docFreq=9, maxDocs=100)",
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: 2,
|
||||
Message: "queryNorm",
|
||||
},
|
||||
},
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: math.Sqrt(1.0) * idf,
|
||||
Message: "fieldWeight(desc:beer in one), product of:",
|
||||
Children: []*Explanation{
|
||||
&Explanation{
|
||||
Children: []*search.Explanation{
|
||||
&search.Explanation{
|
||||
Value: 1,
|
||||
Message: "tf(termFreq(desc:beer)=1",
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: 1,
|
||||
Message: "fieldNorm(field=desc, doc=one)",
|
||||
},
|
||||
&Explanation{
|
||||
&search.Explanation{
|
||||
Value: idf,
|
||||
Message: "idf(docFreq=9, maxDocs=100)",
|
||||
},
|
|
@ -6,7 +6,7 @@
|
|||
// 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 search
|
||||
package scorers
|
||||
|
||||
import (
|
||||
"math"
|
|
@ -6,7 +6,7 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"math"
|
|
@ -6,9 +6,13 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
type OrderedSearcherList []Searcher
|
||||
import (
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type OrderedSearcherList []search.Searcher
|
||||
|
||||
// sort.Interface
|
||||
|
|
@ -6,37 +6,39 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/scorers"
|
||||
)
|
||||
|
||||
type BooleanSearcher struct {
|
||||
initialized bool
|
||||
index index.Index
|
||||
mustSearcher Searcher
|
||||
shouldSearcher Searcher
|
||||
mustNotSearcher Searcher
|
||||
mustSearcher search.Searcher
|
||||
shouldSearcher search.Searcher
|
||||
mustNotSearcher search.Searcher
|
||||
queryNorm float64
|
||||
currMust *DocumentMatch
|
||||
currShould *DocumentMatch
|
||||
currMustNot *DocumentMatch
|
||||
currMust *search.DocumentMatch
|
||||
currShould *search.DocumentMatch
|
||||
currMustNot *search.DocumentMatch
|
||||
currentId string
|
||||
min uint64
|
||||
scorer *ConjunctionQueryScorer
|
||||
scorer *scorers.ConjunctionQueryScorer
|
||||
}
|
||||
|
||||
func NewBooleanSearcher(index index.Index, mustSearcher Searcher, shouldSearcher Searcher, mustNotSearcher Searcher, explain bool) (*BooleanSearcher, error) {
|
||||
func NewBooleanSearcher(index index.Index, mustSearcher search.Searcher, shouldSearcher search.Searcher, mustNotSearcher search.Searcher, explain bool) (*BooleanSearcher, error) {
|
||||
// build our searcher
|
||||
rv := BooleanSearcher{
|
||||
index: index,
|
||||
mustSearcher: mustSearcher,
|
||||
shouldSearcher: shouldSearcher,
|
||||
mustNotSearcher: mustNotSearcher,
|
||||
scorer: NewConjunctionQueryScorer(explain),
|
||||
scorer: scorers.NewConjunctionQueryScorer(explain),
|
||||
}
|
||||
rv.computeQueryNorm()
|
||||
return &rv, nil
|
||||
|
@ -145,7 +147,7 @@ func (s *BooleanSearcher) SetQueryNorm(qnorm float64) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *BooleanSearcher) Next() (*DocumentMatch, error) {
|
||||
func (s *BooleanSearcher) Next() (*search.DocumentMatch, error) {
|
||||
|
||||
if !s.initialized {
|
||||
err := s.initSearchers()
|
||||
|
@ -155,7 +157,7 @@ func (s *BooleanSearcher) Next() (*DocumentMatch, error) {
|
|||
}
|
||||
|
||||
var err error
|
||||
var rv *DocumentMatch
|
||||
var rv *search.DocumentMatch
|
||||
|
||||
for s.currentId != "" {
|
||||
if s.currMustNot != nil && s.currMustNot.ID < s.currentId {
|
||||
|
@ -183,7 +185,7 @@ func (s *BooleanSearcher) Next() (*DocumentMatch, error) {
|
|||
}
|
||||
if s.currShould != nil && s.currShould.ID == s.currentId {
|
||||
// score bonus matches should
|
||||
cons := []*DocumentMatch{}
|
||||
cons := []*search.DocumentMatch{}
|
||||
if s.currMust != nil {
|
||||
cons = append(cons, s.currMust)
|
||||
}
|
||||
|
@ -193,13 +195,13 @@ func (s *BooleanSearcher) Next() (*DocumentMatch, error) {
|
|||
break
|
||||
} else if s.shouldSearcher.Min() == 0 {
|
||||
// match is OK anyway
|
||||
rv = s.scorer.Score([]*DocumentMatch{s.currMust})
|
||||
rv = s.scorer.Score([]*search.DocumentMatch{s.currMust})
|
||||
s.advanceNextMust()
|
||||
break
|
||||
}
|
||||
} else if s.currShould != nil && s.currShould.ID == s.currentId {
|
||||
// score bonus matches should
|
||||
cons := []*DocumentMatch{}
|
||||
cons := []*search.DocumentMatch{}
|
||||
if s.currMust != nil {
|
||||
cons = append(cons, s.currMust)
|
||||
}
|
||||
|
@ -209,7 +211,7 @@ func (s *BooleanSearcher) Next() (*DocumentMatch, error) {
|
|||
break
|
||||
} else if s.shouldSearcher == nil || s.shouldSearcher.Min() == 0 {
|
||||
// match is OK anyway
|
||||
rv = s.scorer.Score([]*DocumentMatch{s.currMust})
|
||||
rv = s.scorer.Score([]*search.DocumentMatch{s.currMust})
|
||||
s.advanceNextMust()
|
||||
break
|
||||
}
|
||||
|
@ -219,7 +221,7 @@ func (s *BooleanSearcher) Next() (*DocumentMatch, error) {
|
|||
return rv, nil
|
||||
}
|
||||
|
||||
func (s *BooleanSearcher) Advance(ID string) (*DocumentMatch, error) {
|
||||
func (s *BooleanSearcher) Advance(ID string) (*search.DocumentMatch, error) {
|
||||
|
||||
if !s.initialized {
|
||||
err := s.initSearchers()
|
|
@ -6,10 +6,12 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
func TestBooleanSearch(t *testing.T) {
|
||||
|
@ -19,7 +21,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustSearcher, err := NewConjunctionSearcher(twoDocIndex, []Searcher{beerTermSearcher}, true)
|
||||
mustSearcher, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{beerTermSearcher}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -31,7 +33,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shouldSearcher, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{martyTermSearcher, dustinTermSearcher}, 0, true)
|
||||
shouldSearcher, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{martyTermSearcher, dustinTermSearcher}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -39,7 +41,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustNotSearcher, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{steveTermSearcher}, 0, true)
|
||||
mustNotSearcher, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{steveTermSearcher}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -57,7 +59,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shouldSearcher2, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{martyTermSearcher2, dustinTermSearcher2}, 0, true)
|
||||
shouldSearcher2, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{martyTermSearcher2, dustinTermSearcher2}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -65,7 +67,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustNotSearcher2, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{steveTermSearcher2}, 0, true)
|
||||
mustNotSearcher2, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{steveTermSearcher2}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -79,7 +81,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustNotSearcher3, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{steveTermSearcher3}, 0, true)
|
||||
mustNotSearcher3, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{steveTermSearcher3}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -93,7 +95,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustSearcher4, err := NewConjunctionSearcher(twoDocIndex, []Searcher{beerTermSearcher4}, true)
|
||||
mustSearcher4, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{beerTermSearcher4}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -101,7 +103,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustNotSearcher4, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{steveTermSearcher4}, 0, true)
|
||||
mustNotSearcher4, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{steveTermSearcher4}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -115,7 +117,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustSearcher5, err := NewConjunctionSearcher(twoDocIndex, []Searcher{beerTermSearcher5}, true)
|
||||
mustSearcher5, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{beerTermSearcher5}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -127,7 +129,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustNotSearcher5, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{steveTermSearcher5, martyTermSearcher5}, 0, true)
|
||||
mustNotSearcher5, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{steveTermSearcher5, martyTermSearcher5}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -141,7 +143,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustSearcher6, err := NewConjunctionSearcher(twoDocIndex, []Searcher{beerTermSearcher6}, true)
|
||||
mustSearcher6, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{beerTermSearcher6}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -153,7 +155,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shouldSearcher6, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{martyTermSearcher6, dustinTermSearcher6}, 2, true)
|
||||
shouldSearcher6, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{martyTermSearcher6, dustinTermSearcher6}, 2, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -167,7 +169,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustSearcher7, err := NewConjunctionSearcher(twoDocIndex, []Searcher{beerTermSearcher7}, true)
|
||||
mustSearcher7, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{beerTermSearcher7}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -179,14 +181,14 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
conjunctionSearcher7, err := NewConjunctionSearcher(twoDocIndex, []Searcher{martyTermSearcher7, booleanSearcher7}, true)
|
||||
conjunctionSearcher7, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{martyTermSearcher7, booleanSearcher7}, true)
|
||||
|
||||
// test 7
|
||||
beerTermSearcher8, err := NewTermSearcher(twoDocIndex, "beer", "desc", 1.0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustSearcher8, err := NewConjunctionSearcher(twoDocIndex, []Searcher{beerTermSearcher8}, true)
|
||||
mustSearcher8, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{beerTermSearcher8}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -198,7 +200,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shouldSearcher8, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{martyTermSearcher8, dustinTermSearcher8}, 0, true)
|
||||
shouldSearcher8, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{martyTermSearcher8, dustinTermSearcher8}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -206,7 +208,7 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustNotSearcher8, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{steveTermSearcher8}, 0, true)
|
||||
mustNotSearcher8, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{steveTermSearcher8}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -218,27 +220,27 @@ func TestBooleanSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
conjunctionSearcher8, err := NewConjunctionSearcher(twoDocIndex, []Searcher{booleanSearcher8, dustinTermSearcher8a}, true)
|
||||
conjunctionSearcher8, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{booleanSearcher8, dustinTermSearcher8a}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
searcher Searcher
|
||||
results []*DocumentMatch
|
||||
searcher search.Searcher
|
||||
results []*search.DocumentMatch
|
||||
}{
|
||||
{
|
||||
searcher: booleanSearcher,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "1",
|
||||
Score: 0.9818005051949021,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "3",
|
||||
Score: 0.808709699395535,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "4",
|
||||
Score: 0.34618161159873423,
|
||||
},
|
||||
|
@ -246,12 +248,12 @@ func TestBooleanSearch(t *testing.T) {
|
|||
},
|
||||
{
|
||||
searcher: booleanSearcher2,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "1",
|
||||
Score: 0.6775110856165737,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "3",
|
||||
Score: 0.6775110856165737,
|
||||
},
|
||||
|
@ -260,20 +262,20 @@ func TestBooleanSearch(t *testing.T) {
|
|||
// no MUST or SHOULD clauses yields no results
|
||||
{
|
||||
searcher: booleanSearcher3,
|
||||
results: []*DocumentMatch{},
|
||||
results: []*search.DocumentMatch{},
|
||||
},
|
||||
{
|
||||
searcher: booleanSearcher4,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "1",
|
||||
Score: 1.0,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "3",
|
||||
Score: 0.5,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "4",
|
||||
Score: 1.0,
|
||||
},
|
||||
|
@ -281,12 +283,12 @@ func TestBooleanSearch(t *testing.T) {
|
|||
},
|
||||
{
|
||||
searcher: booleanSearcher5,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "3",
|
||||
Score: 0.5,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "4",
|
||||
Score: 1.0,
|
||||
},
|
||||
|
@ -294,13 +296,13 @@ func TestBooleanSearch(t *testing.T) {
|
|||
},
|
||||
{
|
||||
searcher: booleanSearcher6,
|
||||
results: []*DocumentMatch{},
|
||||
results: []*search.DocumentMatch{},
|
||||
},
|
||||
// test a conjunction query with a nested boolean
|
||||
{
|
||||
searcher: conjunctionSearcher7,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "1",
|
||||
Score: 2.0097428702814377,
|
||||
},
|
||||
|
@ -308,8 +310,8 @@ func TestBooleanSearch(t *testing.T) {
|
|||
},
|
||||
{
|
||||
searcher: conjunctionSearcher8,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "3",
|
||||
Score: 2.0681575785068107,
|
||||
},
|
|
@ -6,13 +6,15 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/scorers"
|
||||
)
|
||||
|
||||
type ConjunctionSearcher struct {
|
||||
|
@ -21,12 +23,12 @@ type ConjunctionSearcher struct {
|
|||
searchers OrderedSearcherList
|
||||
explain bool
|
||||
queryNorm float64
|
||||
currs []*DocumentMatch
|
||||
currs []*search.DocumentMatch
|
||||
currentId string
|
||||
scorer *ConjunctionQueryScorer
|
||||
scorer *scorers.ConjunctionQueryScorer
|
||||
}
|
||||
|
||||
func NewConjunctionSearcher(index index.Index, qsearchers []Searcher, explain bool) (*ConjunctionSearcher, error) {
|
||||
func NewConjunctionSearcher(index index.Index, qsearchers []search.Searcher, explain bool) (*ConjunctionSearcher, error) {
|
||||
// build the downstream searchres
|
||||
searchers := make(OrderedSearcherList, len(qsearchers))
|
||||
for i, searcher := range qsearchers {
|
||||
|
@ -39,8 +41,8 @@ func NewConjunctionSearcher(index index.Index, qsearchers []Searcher, explain bo
|
|||
index: index,
|
||||
explain: explain,
|
||||
searchers: searchers,
|
||||
currs: make([]*DocumentMatch, len(searchers)),
|
||||
scorer: NewConjunctionQueryScorer(explain),
|
||||
currs: make([]*search.DocumentMatch, len(searchers)),
|
||||
scorer: scorers.NewConjunctionQueryScorer(explain),
|
||||
}
|
||||
rv.computeQueryNorm()
|
||||
return &rv, nil
|
||||
|
@ -96,14 +98,14 @@ func (s *ConjunctionSearcher) SetQueryNorm(qnorm float64) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *ConjunctionSearcher) Next() (*DocumentMatch, error) {
|
||||
func (s *ConjunctionSearcher) Next() (*search.DocumentMatch, error) {
|
||||
if !s.initialized {
|
||||
err := s.initSearchers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var rv *DocumentMatch
|
||||
var rv *search.DocumentMatch
|
||||
var err error
|
||||
OUTER:
|
||||
for s.currentId != "" {
|
||||
|
@ -148,7 +150,7 @@ OUTER:
|
|||
return rv, nil
|
||||
}
|
||||
|
||||
func (s *ConjunctionSearcher) Advance(ID string) (*DocumentMatch, error) {
|
||||
func (s *ConjunctionSearcher) Advance(ID string) (*search.DocumentMatch, error) {
|
||||
if !s.initialized {
|
||||
err := s.initSearchers()
|
||||
if err != nil {
|
|
@ -6,10 +6,12 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
func TestConjunctionSearch(t *testing.T) {
|
||||
|
@ -23,7 +25,7 @@ func TestConjunctionSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beerAndMartySearcher, err := NewConjunctionSearcher(twoDocIndex, []Searcher{beerTermSearcher, martyTermSearcher}, true)
|
||||
beerAndMartySearcher, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{beerTermSearcher, martyTermSearcher}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -37,7 +39,7 @@ func TestConjunctionSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
angstAndBeerSearcher, err := NewConjunctionSearcher(twoDocIndex, []Searcher{angstTermSearcher, beerTermSearcher2}, true)
|
||||
angstAndBeerSearcher, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{angstTermSearcher, beerTermSearcher2}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -51,7 +53,7 @@ func TestConjunctionSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beerAndJackSearcher, err := NewConjunctionSearcher(twoDocIndex, []Searcher{beerTermSearcher3, jackTermSearcher}, true)
|
||||
beerAndJackSearcher, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{beerTermSearcher3, jackTermSearcher}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -65,7 +67,7 @@ func TestConjunctionSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beerAndMisterSearcher, err := NewConjunctionSearcher(twoDocIndex, []Searcher{beerTermSearcher4, misterTermSearcher}, true)
|
||||
beerAndMisterSearcher, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{beerTermSearcher4, misterTermSearcher}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -79,7 +81,7 @@ func TestConjunctionSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
couchbaseAndMisterSearcher, err := NewConjunctionSearcher(twoDocIndex, []Searcher{couchbaseTermSearcher, misterTermSearcher2}, true)
|
||||
couchbaseAndMisterSearcher, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{couchbaseTermSearcher, misterTermSearcher2}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -97,23 +99,23 @@ func TestConjunctionSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
couchbaseAndMisterSearcher2, err := NewConjunctionSearcher(twoDocIndex, []Searcher{couchbaseTermSearcher2, misterTermSearcher3}, true)
|
||||
couchbaseAndMisterSearcher2, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{couchbaseTermSearcher2, misterTermSearcher3}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beerAndCouchbaseAndMisterSearcher, err := NewConjunctionSearcher(twoDocIndex, []Searcher{beerTermSearcher5, couchbaseAndMisterSearcher2}, true)
|
||||
beerAndCouchbaseAndMisterSearcher, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{beerTermSearcher5, couchbaseAndMisterSearcher2}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
searcher Searcher
|
||||
results []*DocumentMatch
|
||||
searcher search.Searcher
|
||||
results []*search.DocumentMatch
|
||||
}{
|
||||
{
|
||||
searcher: beerAndMartySearcher,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "1",
|
||||
Score: 2.0097428702814377,
|
||||
},
|
||||
|
@ -121,8 +123,8 @@ func TestConjunctionSearch(t *testing.T) {
|
|||
},
|
||||
{
|
||||
searcher: angstAndBeerSearcher,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "2",
|
||||
Score: 1.0807601687084403,
|
||||
},
|
||||
|
@ -130,16 +132,16 @@ func TestConjunctionSearch(t *testing.T) {
|
|||
},
|
||||
{
|
||||
searcher: beerAndJackSearcher,
|
||||
results: []*DocumentMatch{},
|
||||
results: []*search.DocumentMatch{},
|
||||
},
|
||||
{
|
||||
searcher: beerAndMisterSearcher,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "2",
|
||||
Score: 1.2877980334016337,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "3",
|
||||
Score: 1.2877980334016337,
|
||||
},
|
||||
|
@ -147,8 +149,8 @@ func TestConjunctionSearch(t *testing.T) {
|
|||
},
|
||||
{
|
||||
searcher: couchbaseAndMisterSearcher,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "2",
|
||||
Score: 1.4436599157093672,
|
||||
},
|
||||
|
@ -156,8 +158,8 @@ func TestConjunctionSearch(t *testing.T) {
|
|||
},
|
||||
{
|
||||
searcher: beerAndCouchbaseAndMisterSearcher,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "2",
|
||||
Score: 1.441614953806971,
|
||||
},
|
|
@ -6,13 +6,15 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/scorers"
|
||||
)
|
||||
|
||||
type DisjunctionSearcher struct {
|
||||
|
@ -20,13 +22,13 @@ type DisjunctionSearcher struct {
|
|||
index index.Index
|
||||
searchers OrderedSearcherList
|
||||
queryNorm float64
|
||||
currs []*DocumentMatch
|
||||
currs []*search.DocumentMatch
|
||||
currentId string
|
||||
scorer *DisjunctionQueryScorer
|
||||
scorer *scorers.DisjunctionQueryScorer
|
||||
min float64
|
||||
}
|
||||
|
||||
func NewDisjunctionSearcher(index index.Index, qsearchers []Searcher, min float64, explain bool) (*DisjunctionSearcher, error) {
|
||||
func NewDisjunctionSearcher(index index.Index, qsearchers []search.Searcher, min float64, explain bool) (*DisjunctionSearcher, error) {
|
||||
// build the downstream searchres
|
||||
searchers := make(OrderedSearcherList, len(qsearchers))
|
||||
for i, searcher := range qsearchers {
|
||||
|
@ -38,8 +40,8 @@ func NewDisjunctionSearcher(index index.Index, qsearchers []Searcher, min float6
|
|||
rv := DisjunctionSearcher{
|
||||
index: index,
|
||||
searchers: searchers,
|
||||
currs: make([]*DocumentMatch, len(searchers)),
|
||||
scorer: NewDisjunctionQueryScorer(explain),
|
||||
currs: make([]*search.DocumentMatch, len(searchers)),
|
||||
scorer: scorers.NewDisjunctionQueryScorer(explain),
|
||||
min: min,
|
||||
}
|
||||
rv.computeQueryNorm()
|
||||
|
@ -99,7 +101,7 @@ func (s *DisjunctionSearcher) SetQueryNorm(qnorm float64) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *DisjunctionSearcher) Next() (*DocumentMatch, error) {
|
||||
func (s *DisjunctionSearcher) Next() (*search.DocumentMatch, error) {
|
||||
if !s.initialized {
|
||||
err := s.initSearchers()
|
||||
if err != nil {
|
||||
|
@ -107,8 +109,8 @@ func (s *DisjunctionSearcher) Next() (*DocumentMatch, error) {
|
|||
}
|
||||
}
|
||||
var err error
|
||||
var rv *DocumentMatch
|
||||
matching := make([]*DocumentMatch, 0)
|
||||
var rv *search.DocumentMatch
|
||||
matching := make([]*search.DocumentMatch, 0)
|
||||
|
||||
found := false
|
||||
for !found && s.currentId != "" {
|
||||
|
@ -125,7 +127,7 @@ func (s *DisjunctionSearcher) Next() (*DocumentMatch, error) {
|
|||
}
|
||||
|
||||
// reset matching
|
||||
matching = make([]*DocumentMatch, 0)
|
||||
matching = make([]*search.DocumentMatch, 0)
|
||||
// invoke next on all the matching searchers
|
||||
for i, curr := range s.currs {
|
||||
if curr != nil && curr.ID == s.currentId {
|
||||
|
@ -141,7 +143,7 @@ func (s *DisjunctionSearcher) Next() (*DocumentMatch, error) {
|
|||
return rv, nil
|
||||
}
|
||||
|
||||
func (s *DisjunctionSearcher) Advance(ID string) (*DocumentMatch, error) {
|
||||
func (s *DisjunctionSearcher) Advance(ID string) (*search.DocumentMatch, error) {
|
||||
if !s.initialized {
|
||||
err := s.initSearchers()
|
||||
if err != nil {
|
|
@ -6,10 +6,12 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
func TestDisjunctionSearch(t *testing.T) {
|
||||
|
@ -22,7 +24,7 @@ func TestDisjunctionSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
martyOrDustinSearcher, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{martyTermSearcher, dustinTermSearcher}, 0, true)
|
||||
martyOrDustinSearcher, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{martyTermSearcher, dustinTermSearcher}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -35,7 +37,7 @@ func TestDisjunctionSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
martyOrDustinSearcher2, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{martyTermSearcher2, dustinTermSearcher2}, 0, true)
|
||||
martyOrDustinSearcher2, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{martyTermSearcher2, dustinTermSearcher2}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -44,23 +46,23 @@ func TestDisjunctionSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
nestedRaviOrMartyOrDustinSearcher, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{raviTermSearcher, martyOrDustinSearcher2}, 0, true)
|
||||
nestedRaviOrMartyOrDustinSearcher, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{raviTermSearcher, martyOrDustinSearcher2}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
searcher Searcher
|
||||
results []*DocumentMatch
|
||||
searcher search.Searcher
|
||||
results []*search.DocumentMatch
|
||||
}{
|
||||
{
|
||||
searcher: martyOrDustinSearcher,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "1",
|
||||
Score: 0.6775110856165737,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "3",
|
||||
Score: 0.6775110856165737,
|
||||
},
|
||||
|
@ -69,16 +71,16 @@ func TestDisjunctionSearch(t *testing.T) {
|
|||
// test a nested disjunction
|
||||
{
|
||||
searcher: nestedRaviOrMartyOrDustinSearcher,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "1",
|
||||
Score: 0.2765927424732821,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "3",
|
||||
Score: 0.2765927424732821,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "4",
|
||||
Score: 0.5531854849465642,
|
||||
},
|
||||
|
@ -124,7 +126,7 @@ func TestDisjunctionAdvance(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
martyOrDustinSearcher, err := NewDisjunctionSearcher(twoDocIndex, []Searcher{martyTermSearcher, dustinTermSearcher}, 0, true)
|
||||
martyOrDustinSearcher, err := NewDisjunctionSearcher(twoDocIndex, []search.Searcher{martyTermSearcher, dustinTermSearcher}, 0, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
|
@ -6,16 +6,18 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/scorers"
|
||||
)
|
||||
|
||||
type MatchAllSearcher struct {
|
||||
index index.Index
|
||||
reader index.DocIdReader
|
||||
scorer *ConstantScorer
|
||||
scorer *scorers.ConstantScorer
|
||||
}
|
||||
|
||||
func NewMatchAllSearcher(index index.Index, boost float64, explain bool) (*MatchAllSearcher, error) {
|
||||
|
@ -23,7 +25,7 @@ func NewMatchAllSearcher(index index.Index, boost float64, explain bool) (*Match
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scorer := NewConstantScorer(1.0, boost, explain)
|
||||
scorer := scorers.NewConstantScorer(1.0, boost, explain)
|
||||
return &MatchAllSearcher{
|
||||
index: index,
|
||||
reader: reader,
|
||||
|
@ -43,7 +45,7 @@ func (s *MatchAllSearcher) SetQueryNorm(qnorm float64) {
|
|||
s.scorer.SetQueryNorm(qnorm)
|
||||
}
|
||||
|
||||
func (s *MatchAllSearcher) Next() (*DocumentMatch, error) {
|
||||
func (s *MatchAllSearcher) Next() (*search.DocumentMatch, error) {
|
||||
id, err := s.reader.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -60,7 +62,7 @@ func (s *MatchAllSearcher) Next() (*DocumentMatch, error) {
|
|||
|
||||
}
|
||||
|
||||
func (s *MatchAllSearcher) Advance(ID string) (*DocumentMatch, error) {
|
||||
func (s *MatchAllSearcher) Advance(ID string) (*search.DocumentMatch, error) {
|
||||
id, err := s.reader.Advance(ID)
|
||||
if err != nil {
|
||||
return nil, err
|
|
@ -6,10 +6,12 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
func TestMatchAllSearch(t *testing.T) {
|
||||
|
@ -25,31 +27,31 @@ func TestMatchAllSearch(t *testing.T) {
|
|||
}
|
||||
|
||||
tests := []struct {
|
||||
searcher Searcher
|
||||
searcher search.Searcher
|
||||
queryNorm float64
|
||||
results []*DocumentMatch
|
||||
results []*search.DocumentMatch
|
||||
}{
|
||||
{
|
||||
searcher: allSearcher,
|
||||
queryNorm: 1.0,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "1",
|
||||
Score: 1.0,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "2",
|
||||
Score: 1.0,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "3",
|
||||
Score: 1.0,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "4",
|
||||
Score: 1.0,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "5",
|
||||
Score: 1.0,
|
||||
},
|
||||
|
@ -58,24 +60,24 @@ func TestMatchAllSearch(t *testing.T) {
|
|||
{
|
||||
searcher: allSearcher2,
|
||||
queryNorm: 0.8333333,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "1",
|
||||
Score: 1.0,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "2",
|
||||
Score: 1.0,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "3",
|
||||
Score: 1.0,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "4",
|
||||
Score: 1.0,
|
||||
},
|
||||
&DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "5",
|
||||
Score: 1.0,
|
||||
},
|
|
@ -6,10 +6,11 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type MatchNoneSearcher struct {
|
||||
|
@ -34,11 +35,11 @@ func (s *MatchNoneSearcher) SetQueryNorm(qnorm float64) {
|
|||
|
||||
}
|
||||
|
||||
func (s *MatchNoneSearcher) Next() (*DocumentMatch, error) {
|
||||
func (s *MatchNoneSearcher) Next() (*search.DocumentMatch, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *MatchNoneSearcher) Advance(ID string) (*DocumentMatch, error) {
|
||||
func (s *MatchNoneSearcher) Advance(ID string) (*search.DocumentMatch, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -6,10 +6,12 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
func TestMatchNoneSearch(t *testing.T) {
|
||||
|
@ -20,12 +22,12 @@ func TestMatchNoneSearch(t *testing.T) {
|
|||
}
|
||||
|
||||
tests := []struct {
|
||||
searcher Searcher
|
||||
results []*DocumentMatch
|
||||
searcher search.Searcher
|
||||
results []*search.DocumentMatch
|
||||
}{
|
||||
{
|
||||
searcher: noneSearcher,
|
||||
results: []*DocumentMatch{},
|
||||
results: []*search.DocumentMatch{},
|
||||
},
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/numeric_util"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type NumericRangeSearcher struct {
|
||||
|
@ -56,7 +57,7 @@ func NewNumericRangeSearcher(index index.Index, min *float64, max *float64, incl
|
|||
termRanges := splitInt64Range(minInt64, maxInt64, 4)
|
||||
terms := termRanges.Enumerate()
|
||||
// enumerate all the terms in the range
|
||||
qsearchers := make([]Searcher, len(terms))
|
||||
qsearchers := make([]search.Searcher, len(terms))
|
||||
for i, term := range terms {
|
||||
var err error
|
||||
qsearchers[i], err = NewTermSearcher(index, string(term), field, 1.0, explain)
|
||||
|
@ -91,11 +92,11 @@ func (s *NumericRangeSearcher) SetQueryNorm(qnorm float64) {
|
|||
s.searcher.SetQueryNorm(qnorm)
|
||||
}
|
||||
|
||||
func (s *NumericRangeSearcher) Next() (*DocumentMatch, error) {
|
||||
func (s *NumericRangeSearcher) Next() (*search.DocumentMatch, error) {
|
||||
return s.searcher.Next()
|
||||
}
|
||||
|
||||
func (s *NumericRangeSearcher) Advance(ID string) (*DocumentMatch, error) {
|
||||
func (s *NumericRangeSearcher) Advance(ID string) (*search.DocumentMatch, error) {
|
||||
return s.searcher.Advance(ID)
|
||||
}
|
||||
|
|
@ -1,4 +1,13 @@
|
|||
package search
|
||||
// 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 searchers
|
||||
|
||||
import (
|
||||
"reflect"
|
|
@ -6,12 +6,13 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type PhraseSearcher struct {
|
||||
|
@ -19,7 +20,7 @@ type PhraseSearcher struct {
|
|||
index index.Index
|
||||
mustSearcher *ConjunctionSearcher
|
||||
queryNorm float64
|
||||
currMust *DocumentMatch
|
||||
currMust *search.DocumentMatch
|
||||
slop int
|
||||
terms []string
|
||||
}
|
||||
|
@ -89,7 +90,7 @@ func (s *PhraseSearcher) SetQueryNorm(qnorm float64) {
|
|||
s.mustSearcher.SetQueryNorm(qnorm)
|
||||
}
|
||||
|
||||
func (s *PhraseSearcher) Next() (*DocumentMatch, error) {
|
||||
func (s *PhraseSearcher) Next() (*search.DocumentMatch, error) {
|
||||
if !s.initialized {
|
||||
err := s.initSearchers()
|
||||
if err != nil {
|
||||
|
@ -97,18 +98,18 @@ func (s *PhraseSearcher) Next() (*DocumentMatch, error) {
|
|||
}
|
||||
}
|
||||
|
||||
var rv *DocumentMatch
|
||||
var rv *search.DocumentMatch
|
||||
for s.currMust != nil {
|
||||
rvftlm := make(FieldTermLocationMap, 0)
|
||||
rvftlm := make(search.FieldTermLocationMap, 0)
|
||||
freq := 0
|
||||
firstTerm := s.terms[0]
|
||||
for field, termLocMap := range s.currMust.Locations {
|
||||
rvtlm := make(TermLocationMap, 0)
|
||||
rvtlm := make(search.TermLocationMap, 0)
|
||||
locations, ok := termLocMap[firstTerm]
|
||||
if ok {
|
||||
OUTER:
|
||||
for _, location := range locations {
|
||||
crvtlm := make(TermLocationMap, 0)
|
||||
crvtlm := make(search.TermLocationMap, 0)
|
||||
INNER:
|
||||
for i := 0; i < len(s.mustSearcher.searchers); i++ {
|
||||
nextTerm := s.terms[i]
|
||||
|
@ -133,7 +134,7 @@ func (s *PhraseSearcher) Next() (*DocumentMatch, error) {
|
|||
}
|
||||
// if we got here all the terms matched
|
||||
freq += 1
|
||||
mergeTermLocationMaps(rvtlm, crvtlm)
|
||||
search.MergeTermLocationMaps(rvtlm, crvtlm)
|
||||
rvftlm[field] = rvtlm
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +154,7 @@ func (s *PhraseSearcher) Next() (*DocumentMatch, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *PhraseSearcher) Advance(ID string) (*DocumentMatch, error) {
|
||||
func (s *PhraseSearcher) Advance(ID string) (*search.DocumentMatch, error) {
|
||||
if !s.initialized {
|
||||
err := s.initSearchers()
|
||||
if err != nil {
|
|
@ -6,10 +6,12 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
func TestPhraseSearch(t *testing.T) {
|
||||
|
@ -22,7 +24,7 @@ func TestPhraseSearch(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustSearcher, err := NewConjunctionSearcher(twoDocIndex, []Searcher{angstTermSearcher, beerTermSearcher}, true)
|
||||
mustSearcher, err := NewConjunctionSearcher(twoDocIndex, []search.Searcher{angstTermSearcher, beerTermSearcher}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -32,13 +34,13 @@ func TestPhraseSearch(t *testing.T) {
|
|||
}
|
||||
|
||||
tests := []struct {
|
||||
searcher Searcher
|
||||
results []*DocumentMatch
|
||||
searcher search.Searcher
|
||||
results []*search.DocumentMatch
|
||||
}{
|
||||
{
|
||||
searcher: phraseSearcher,
|
||||
results: []*DocumentMatch{
|
||||
&DocumentMatch{
|
||||
results: []*search.DocumentMatch{
|
||||
&search.DocumentMatch{
|
||||
ID: "2",
|
||||
Score: 1.0807601687084403,
|
||||
},
|
|
@ -6,10 +6,12 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
"github.com/blevesearch/bleve/search/scorers"
|
||||
)
|
||||
|
||||
type TermSearcher struct {
|
||||
|
@ -18,7 +20,7 @@ type TermSearcher struct {
|
|||
field string
|
||||
explain bool
|
||||
reader index.TermFieldReader
|
||||
scorer *TermQueryScorer
|
||||
scorer *scorers.TermQueryScorer
|
||||
}
|
||||
|
||||
func NewTermSearcher(index index.Index, term string, field string, boost float64, explain bool) (*TermSearcher, error) {
|
||||
|
@ -26,7 +28,7 @@ func NewTermSearcher(index index.Index, term string, field string, boost float64
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scorer := NewTermQueryScorer(term, field, boost, index.DocCount(), reader.Count(), explain)
|
||||
scorer := scorers.NewTermQueryScorer(term, field, boost, index.DocCount(), reader.Count(), explain)
|
||||
return &TermSearcher{
|
||||
index: index,
|
||||
term: term,
|
||||
|
@ -49,7 +51,7 @@ func (s *TermSearcher) SetQueryNorm(qnorm float64) {
|
|||
s.scorer.SetQueryNorm(qnorm)
|
||||
}
|
||||
|
||||
func (s *TermSearcher) Next() (*DocumentMatch, error) {
|
||||
func (s *TermSearcher) Next() (*search.DocumentMatch, error) {
|
||||
termMatch, err := s.reader.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -66,7 +68,7 @@ func (s *TermSearcher) Next() (*DocumentMatch, error) {
|
|||
|
||||
}
|
||||
|
||||
func (s *TermSearcher) Advance(ID string) (*DocumentMatch, error) {
|
||||
func (s *TermSearcher) Advance(ID string) (*search.DocumentMatch, error) {
|
||||
termMatch, err := s.reader.Advance(ID)
|
||||
if err != nil {
|
||||
return nil, err
|
|
@ -6,10 +6,11 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type TermPrefixSearcher struct {
|
||||
|
@ -25,7 +26,7 @@ func NewTermPrefixSearcher(index index.Index, prefix string, field string, boost
|
|||
fieldReader, err := index.FieldReader(field, []byte(prefix), []byte(prefix))
|
||||
|
||||
// enumerate all the terms in the range
|
||||
qsearchers := make([]Searcher, 0, 25)
|
||||
qsearchers := make([]search.Searcher, 0, 25)
|
||||
tfd, err := fieldReader.Next()
|
||||
for err == nil && tfd != nil {
|
||||
qsearcher, err := NewTermSearcher(index, string(tfd.Term), field, 1.0, explain)
|
||||
|
@ -61,12 +62,12 @@ func (s *TermPrefixSearcher) SetQueryNorm(qnorm float64) {
|
|||
s.searcher.SetQueryNorm(qnorm)
|
||||
}
|
||||
|
||||
func (s *TermPrefixSearcher) Next() (*DocumentMatch, error) {
|
||||
func (s *TermPrefixSearcher) Next() (*search.DocumentMatch, error) {
|
||||
return s.searcher.Next()
|
||||
|
||||
}
|
||||
|
||||
func (s *TermPrefixSearcher) Advance(ID string) (*DocumentMatch, error) {
|
||||
func (s *TermPrefixSearcher) Advance(ID string) (*search.DocumentMatch, error) {
|
||||
return s.searcher.Next()
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
// 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 search
|
||||
package searchers
|
||||
|
||||
import (
|
||||
"math"
|
|
@ -8,7 +8,7 @@
|
|||
// and limitations under the License.
|
||||
package search
|
||||
|
||||
func mergeLocations(locations []FieldTermLocationMap) FieldTermLocationMap {
|
||||
func MergeLocations(locations []FieldTermLocationMap) FieldTermLocationMap {
|
||||
rv := locations[0]
|
||||
|
||||
for i := 1; i < len(locations); i++ {
|
||||
|
@ -16,7 +16,7 @@ func mergeLocations(locations []FieldTermLocationMap) FieldTermLocationMap {
|
|||
for field, termLocationMap := range nextLocations {
|
||||
rvTermLocationMap, rvHasField := rv[field]
|
||||
if rvHasField {
|
||||
rv[field] = mergeTermLocationMaps(rvTermLocationMap, termLocationMap)
|
||||
rv[field] = MergeTermLocationMaps(rvTermLocationMap, termLocationMap)
|
||||
} else {
|
||||
rv[field] = termLocationMap
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ func mergeLocations(locations []FieldTermLocationMap) FieldTermLocationMap {
|
|||
return rv
|
||||
}
|
||||
|
||||
func mergeTermLocationMaps(rv, other TermLocationMap) TermLocationMap {
|
||||
func MergeTermLocationMaps(rv, other TermLocationMap) TermLocationMap {
|
||||
for term, locationMap := range other {
|
||||
// for a given term/document there cannot be different locations
|
||||
// if they can back from different clauses, overwrite is ok
|
||||
|
|
|
@ -78,7 +78,7 @@ func TestMergeLocations(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
mergedLocations := mergeLocations([]FieldTermLocationMap{flm1, flm2, flm3})
|
||||
mergedLocations := MergeLocations([]FieldTermLocationMap{flm1, flm2, flm3})
|
||||
if !reflect.DeepEqual(expectedMerge, mergedLocations) {
|
||||
t.Errorf("expected %v, got %v", expectedMerge, mergedLocations)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue