build(deps): bump github.com/blevesearch/bleve/v2 from 2.5.4 to 2.5.5
Bumps [github.com/blevesearch/bleve/v2](https://github.com/blevesearch/bleve) from 2.5.4 to 2.5.5. - [Release notes](https://github.com/blevesearch/bleve/releases) - [Commits](https://github.com/blevesearch/bleve/compare/v2.5.4...v2.5.5) --- updated-dependencies: - dependency-name: github.com/blevesearch/bleve/v2 dependency-version: 2.5.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
Ralf Haferkamp
parent
e85d8effc1
commit
4ae0951f5f
+2
-2
@@ -82,8 +82,8 @@ func scoreSortFunc() func(i, j *search.DocumentMatch) int {
|
||||
|
||||
func getFusionExplAt(hit *search.DocumentMatch, i int, value float64, message string) *search.Explanation {
|
||||
return &search.Explanation{
|
||||
Value: value,
|
||||
Message: message,
|
||||
Value: value,
|
||||
Message: message,
|
||||
Children: []*search.Explanation{hit.Expl.Children[i]},
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -388,3 +388,11 @@ type SynonymIndex interface {
|
||||
// IndexSynonym indexes a synonym definition, with the specified id and belonging to the specified collection.
|
||||
IndexSynonym(id string, collection string, definition *SynonymDefinition) error
|
||||
}
|
||||
|
||||
type InsightsIndex interface {
|
||||
Index
|
||||
// TermFrequencies returns the tokens ordered by frequencies for the field index.
|
||||
TermFrequencies(field string, limit int, descending bool) ([]index.TermFreq, error)
|
||||
// CentroidCardinalities returns the centroids (clusters) from IVF indexes ordered by data density.
|
||||
CentroidCardinalities(field string, limit int, desceding bool) ([]index.CentroidCardinality, error)
|
||||
}
|
||||
|
||||
+59
@@ -23,6 +23,7 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -1234,3 +1235,61 @@ func (is *IndexSnapshot) MergeUpdateFieldsInfo(updatedFields map[string]*index.U
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TermFrequencies returns the top N terms ordered by the frequencies
|
||||
// for a given field across all segments in the index snapshot.
|
||||
func (is *IndexSnapshot) TermFrequencies(field string, limit int, descending bool) (
|
||||
termFreqs []index.TermFreq, err error) {
|
||||
if len(is.segment) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if limit <= 0 {
|
||||
return nil, fmt.Errorf("limit must be positive")
|
||||
}
|
||||
|
||||
// Use FieldDict which aggregates term frequencies across all segments
|
||||
fieldDict, err := is.FieldDict(field)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get field dictionary for field %s: %v", field, err)
|
||||
}
|
||||
defer fieldDict.Close()
|
||||
|
||||
// Preallocate slice with capacity equal to the number of unique terms
|
||||
// in the field dictionary
|
||||
termFreqs = make([]index.TermFreq, 0, fieldDict.Cardinality())
|
||||
|
||||
// Iterate through all terms using FieldDict
|
||||
for {
|
||||
dictEntry, err := fieldDict.Next()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error iterating field dictionary: %v", err)
|
||||
}
|
||||
if dictEntry == nil {
|
||||
break // End of terms
|
||||
}
|
||||
|
||||
termFreqs = append(termFreqs, index.TermFreq{
|
||||
Term: dictEntry.Term,
|
||||
Frequency: dictEntry.Count,
|
||||
})
|
||||
}
|
||||
|
||||
// Sort by frequency (descending or ascending)
|
||||
sort.Slice(termFreqs, func(i, j int) bool {
|
||||
if termFreqs[i].Frequency == termFreqs[j].Frequency {
|
||||
// If frequencies are equal, sort by term lexicographically
|
||||
return strings.Compare(termFreqs[i].Term, termFreqs[j].Term) < 0
|
||||
}
|
||||
if descending {
|
||||
return termFreqs[i].Frequency > termFreqs[j].Frequency
|
||||
}
|
||||
return termFreqs[i].Frequency < termFreqs[j].Frequency
|
||||
})
|
||||
|
||||
if limit >= len(termFreqs) {
|
||||
return termFreqs, nil
|
||||
}
|
||||
|
||||
return termFreqs[:limit], nil
|
||||
}
|
||||
|
||||
+50
@@ -23,6 +23,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/blevesearch/bleve/v2/size"
|
||||
index "github.com/blevesearch/bleve_index_api"
|
||||
@@ -167,3 +168,52 @@ func (i *IndexSnapshotVectorReader) Close() error {
|
||||
// TODO Consider if any scope of recycling here.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IndexSnapshot) CentroidCardinalities(field string, limit int, descending bool) (
|
||||
[]index.CentroidCardinality, error) {
|
||||
if len(i.segment) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if limit <= 0 {
|
||||
return nil, fmt.Errorf("limit must be positive")
|
||||
}
|
||||
|
||||
centroids := make([]index.CentroidCardinality, 0, limit*len(i.segment))
|
||||
|
||||
for _, segment := range i.segment {
|
||||
if sv, ok := segment.segment.(segment_api.VectorSegment); ok {
|
||||
vecIndex, err := sv.InterpretVectorIndex(field,
|
||||
false /* does not require filtering */, segment.deleted)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to interpret vector index for field %s in segment: %v", field, err)
|
||||
}
|
||||
|
||||
centroidCardinalities, err := vecIndex.ObtainKCentroidCardinalitiesFromIVFIndex(limit, descending)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to obtain top k centroid cardinalities for field %s in segment: %v", field, err)
|
||||
}
|
||||
|
||||
if len(centroidCardinalities) > 0 {
|
||||
centroids = append(centroids, centroidCardinalities...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(centroids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sort.Slice(centroids, func(i, j int) bool {
|
||||
if descending {
|
||||
return centroids[i].Cardinality > centroids[j].Cardinality
|
||||
}
|
||||
return centroids[i].Cardinality < centroids[j].Cardinality
|
||||
})
|
||||
|
||||
if limit >= len(centroids) {
|
||||
return centroids, nil
|
||||
}
|
||||
|
||||
return centroids[:limit], nil
|
||||
}
|
||||
|
||||
+158
@@ -17,6 +17,8 @@ package bleve
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -1136,3 +1138,159 @@ func (f *indexAliasImplFieldDict) Close() error {
|
||||
func (f *indexAliasImplFieldDict) Cardinality() int {
|
||||
return f.fieldDict.Cardinality()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (i *indexAliasImpl) TermFrequencies(field string, limit int, descending bool) (
|
||||
[]index.TermFreq, error) {
|
||||
i.mutex.RLock()
|
||||
defer i.mutex.RUnlock()
|
||||
|
||||
if !i.open {
|
||||
return nil, ErrorIndexClosed
|
||||
}
|
||||
|
||||
if len(i.indexes) < 1 {
|
||||
return nil, ErrorAliasEmpty
|
||||
}
|
||||
|
||||
// short circuit the simple case
|
||||
if len(i.indexes) == 1 {
|
||||
if idx, ok := i.indexes[0].(InsightsIndex); ok {
|
||||
return idx.TermFrequencies(field, limit, descending)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// run search on each index in separate go routine
|
||||
var waitGroup sync.WaitGroup
|
||||
asyncResults := make(chan []index.TermFreq, len(i.indexes))
|
||||
|
||||
searchChildIndex := func(in Index, field string, limit int, descending bool) {
|
||||
var rv []index.TermFreq
|
||||
if idx, ok := in.(InsightsIndex); ok {
|
||||
// over sample for higher accuracy
|
||||
rv, _ = idx.TermFrequencies(field, limit*5, descending)
|
||||
}
|
||||
asyncResults <- rv
|
||||
waitGroup.Done()
|
||||
}
|
||||
|
||||
waitGroup.Add(len(i.indexes))
|
||||
for _, in := range i.indexes {
|
||||
go searchChildIndex(in, field, limit, descending)
|
||||
}
|
||||
|
||||
// on another go routine, close after finished
|
||||
go func() {
|
||||
waitGroup.Wait()
|
||||
close(asyncResults)
|
||||
}()
|
||||
|
||||
rvTermFreqsMap := make(map[string]uint64)
|
||||
for asr := range asyncResults {
|
||||
for _, entry := range asr {
|
||||
rvTermFreqsMap[entry.Term] += entry.Frequency
|
||||
}
|
||||
}
|
||||
|
||||
rvTermFreqs := make([]index.TermFreq, 0, len(rvTermFreqsMap))
|
||||
for term, freq := range rvTermFreqsMap {
|
||||
rvTermFreqs = append(rvTermFreqs, index.TermFreq{
|
||||
Term: term,
|
||||
Frequency: freq,
|
||||
})
|
||||
}
|
||||
|
||||
if descending {
|
||||
sort.Slice(rvTermFreqs, func(i, j int) bool {
|
||||
if rvTermFreqs[i].Frequency == rvTermFreqs[j].Frequency {
|
||||
// If frequencies are equal, sort by term lexicographically
|
||||
return strings.Compare(rvTermFreqs[i].Term, rvTermFreqs[j].Term) < 0
|
||||
}
|
||||
return rvTermFreqs[i].Frequency > rvTermFreqs[j].Frequency
|
||||
})
|
||||
} else {
|
||||
sort.Slice(rvTermFreqs, func(i, j int) bool {
|
||||
if rvTermFreqs[i].Frequency == rvTermFreqs[j].Frequency {
|
||||
// If frequencies are equal, sort by term lexicographically
|
||||
return strings.Compare(rvTermFreqs[i].Term, rvTermFreqs[j].Term) < 0
|
||||
}
|
||||
return rvTermFreqs[i].Frequency < rvTermFreqs[j].Frequency
|
||||
})
|
||||
}
|
||||
|
||||
if limit > len(rvTermFreqs) {
|
||||
limit = len(rvTermFreqs)
|
||||
}
|
||||
|
||||
return rvTermFreqs[:limit], nil
|
||||
}
|
||||
|
||||
func (i *indexAliasImpl) CentroidCardinalities(field string, limit int, descending bool) (
|
||||
[]index.CentroidCardinality, error) {
|
||||
i.mutex.RLock()
|
||||
defer i.mutex.RUnlock()
|
||||
|
||||
if !i.open {
|
||||
return nil, ErrorIndexClosed
|
||||
}
|
||||
|
||||
if len(i.indexes) < 1 {
|
||||
return nil, ErrorAliasEmpty
|
||||
}
|
||||
|
||||
// short circuit the simple case
|
||||
if len(i.indexes) == 1 {
|
||||
if idx, ok := i.indexes[0].(InsightsIndex); ok {
|
||||
return idx.CentroidCardinalities(field, limit, descending)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// run search on each index in separate go routine
|
||||
var waitGroup sync.WaitGroup
|
||||
asyncResults := make(chan []index.CentroidCardinality, len(i.indexes))
|
||||
|
||||
searchChildIndex := func(in Index, field string, limit int, descending bool) {
|
||||
var rv []index.CentroidCardinality
|
||||
if idx, ok := in.(InsightsIndex); ok {
|
||||
rv, _ = idx.CentroidCardinalities(field, limit, descending)
|
||||
}
|
||||
asyncResults <- rv
|
||||
waitGroup.Done()
|
||||
}
|
||||
|
||||
waitGroup.Add(len(i.indexes))
|
||||
for _, in := range i.indexes {
|
||||
go searchChildIndex(in, field, limit, descending)
|
||||
}
|
||||
|
||||
// on another go routine, close after finished
|
||||
go func() {
|
||||
waitGroup.Wait()
|
||||
close(asyncResults)
|
||||
}()
|
||||
|
||||
rvCentroidCardinalitiesResult := make([]index.CentroidCardinality, 0, limit)
|
||||
for asr := range asyncResults {
|
||||
asr = append(asr, rvCentroidCardinalitiesResult...)
|
||||
if descending {
|
||||
sort.Slice(asr, func(i, j int) bool {
|
||||
return asr[i].Cardinality > asr[j].Cardinality
|
||||
})
|
||||
} else {
|
||||
sort.Slice(asr, func(i, j int) bool {
|
||||
return asr[i].Cardinality < asr[j].Cardinality
|
||||
})
|
||||
}
|
||||
|
||||
if limit > len(asr) {
|
||||
limit = len(asr)
|
||||
}
|
||||
|
||||
rvCentroidCardinalitiesResult = asr[:limit]
|
||||
}
|
||||
|
||||
return rvCentroidCardinalitiesResult, nil
|
||||
}
|
||||
|
||||
+120
-45
@@ -57,8 +57,6 @@ type indexImpl struct {
|
||||
|
||||
const storePath = "store"
|
||||
|
||||
var mappingInternalKey = []byte("_mapping")
|
||||
|
||||
const (
|
||||
SearchQueryStartCallbackKey search.ContextKey = "_search_query_start_callback_key"
|
||||
SearchQueryEndCallbackKey search.ContextKey = "_search_query_end_callback_key"
|
||||
@@ -641,8 +639,57 @@ func (i *indexImpl) SearchInContext(ctx context.Context, req *SearchRequest) (sr
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
// set up additional contexts for any search operation that will proceed from
|
||||
// here, such as presearch, collectors etc.
|
||||
|
||||
// Scoring model callback to be used to get scoring model
|
||||
scoringModelCallback := func() string {
|
||||
if isBM25Enabled(i.m) {
|
||||
return index.BM25Scoring
|
||||
}
|
||||
return index.DefaultScoringModel
|
||||
}
|
||||
ctx = context.WithValue(ctx, search.GetScoringModelCallbackKey,
|
||||
search.GetScoringModelCallbackFn(scoringModelCallback))
|
||||
|
||||
// This callback and variable handles the tracking of bytes read
|
||||
// 1. as part of creation of tfr and its Next() calls which is
|
||||
// accounted by invoking this callback when the TFR is closed.
|
||||
// 2. the docvalues portion (accounted in collector) and the retrieval
|
||||
// of stored fields bytes (by LoadAndHighlightFields)
|
||||
var totalSearchCost uint64
|
||||
sendBytesRead := func(bytesRead uint64) {
|
||||
totalSearchCost += bytesRead
|
||||
}
|
||||
// Ensure IO cost accounting and result cost assignment happen on all return paths
|
||||
defer func() {
|
||||
if sr != nil {
|
||||
sr.Cost = totalSearchCost
|
||||
}
|
||||
if is, ok := indexReader.(*scorch.IndexSnapshot); ok {
|
||||
is.UpdateIOStats(totalSearchCost)
|
||||
}
|
||||
search.RecordSearchCost(ctx, search.DoneM, 0)
|
||||
}()
|
||||
|
||||
ctx = context.WithValue(ctx, search.SearchIOStatsCallbackKey, search.SearchIOStatsCallbackFunc(sendBytesRead))
|
||||
|
||||
// Geo buffer pool callback to be used for getting geo buffer pool
|
||||
var bufPool *s2.GeoBufferPool
|
||||
getBufferPool := func() *s2.GeoBufferPool {
|
||||
if bufPool == nil {
|
||||
bufPool = s2.NewGeoBufferPool(search.MaxGeoBufPoolSize, search.MinGeoBufPoolSize)
|
||||
}
|
||||
|
||||
return bufPool
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, search.GeoBufferPoolCallbackKey, search.GeoBufferPoolCallbackFunc(getBufferPool))
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
||||
if _, ok := ctx.Value(search.PreSearchKey).(bool); ok {
|
||||
preSearchResult, err := i.preSearch(ctx, req, indexReader)
|
||||
sr, err = i.preSearch(ctx, req, indexReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -656,7 +703,8 @@ func (i *indexImpl) SearchInContext(ctx context.Context, req *SearchRequest) (sr
|
||||
// time stat
|
||||
searchDuration := time.Since(searchStart)
|
||||
atomic.AddUint64(&i.stats.searchTime, uint64(searchDuration))
|
||||
return preSearchResult, nil
|
||||
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
var reverseQueryExecution bool
|
||||
@@ -726,6 +774,9 @@ func (i *indexImpl) SearchInContext(ctx context.Context, req *SearchRequest) (sr
|
||||
// if score fusion, run collect if rescorer is defined
|
||||
if rescorer != nil && requestHasKNN(req) {
|
||||
knnHits, err = i.runKnnCollector(ctx, req, indexReader, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,7 +796,6 @@ func (i *indexImpl) SearchInContext(ctx context.Context, req *SearchRequest) (sr
|
||||
if !contextScoreFusionKeyExists {
|
||||
setKnnHitsInCollector(knnHits, req, coll)
|
||||
}
|
||||
|
||||
|
||||
if fts != nil {
|
||||
if is, ok := indexReader.(*scorch.IndexSnapshot); ok {
|
||||
@@ -754,44 +804,12 @@ func (i *indexImpl) SearchInContext(ctx context.Context, req *SearchRequest) (sr
|
||||
ctx = context.WithValue(ctx, search.FieldTermSynonymMapKey, fts)
|
||||
}
|
||||
|
||||
scoringModelCallback := func() string {
|
||||
if isBM25Enabled(i.m) {
|
||||
return index.BM25Scoring
|
||||
}
|
||||
return index.DefaultScoringModel
|
||||
}
|
||||
ctx = context.WithValue(ctx, search.GetScoringModelCallbackKey,
|
||||
search.GetScoringModelCallbackFn(scoringModelCallback))
|
||||
|
||||
// set the bm25Stats (stats important for consistent scoring) in
|
||||
// the context object
|
||||
if bm25Stats != nil {
|
||||
ctx = context.WithValue(ctx, search.BM25StatsKey, bm25Stats)
|
||||
}
|
||||
|
||||
// This callback and variable handles the tracking of bytes read
|
||||
// 1. as part of creation of tfr and its Next() calls which is
|
||||
// accounted by invoking this callback when the TFR is closed.
|
||||
// 2. the docvalues portion (accounted in collector) and the retrieval
|
||||
// of stored fields bytes (by LoadAndHighlightFields)
|
||||
var totalSearchCost uint64
|
||||
sendBytesRead := func(bytesRead uint64) {
|
||||
totalSearchCost += bytesRead
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, search.SearchIOStatsCallbackKey, search.SearchIOStatsCallbackFunc(sendBytesRead))
|
||||
|
||||
var bufPool *s2.GeoBufferPool
|
||||
getBufferPool := func() *s2.GeoBufferPool {
|
||||
if bufPool == nil {
|
||||
bufPool = s2.NewGeoBufferPool(search.MaxGeoBufPoolSize, search.MinGeoBufPoolSize)
|
||||
}
|
||||
|
||||
return bufPool
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, search.GeoBufferPoolCallbackKey, search.GeoBufferPoolCallbackFunc(getBufferPool))
|
||||
|
||||
searcher, err := req.Query.Searcher(ctx, indexReader, i.m, search.SearcherOptions{
|
||||
Explain: req.Explain,
|
||||
IncludeTermVectors: req.IncludeLocations || req.Highlight != nil,
|
||||
@@ -804,14 +822,6 @@ func (i *indexImpl) SearchInContext(ctx context.Context, req *SearchRequest) (sr
|
||||
if serr := searcher.Close(); err == nil && serr != nil {
|
||||
err = serr
|
||||
}
|
||||
if sr != nil {
|
||||
sr.Cost = totalSearchCost
|
||||
}
|
||||
if sr, ok := indexReader.(*scorch.IndexSnapshot); ok {
|
||||
sr.UpdateIOStats(totalSearchCost)
|
||||
}
|
||||
|
||||
search.RecordSearchCost(ctx, search.DoneM, 0)
|
||||
}()
|
||||
|
||||
if req.Facets != nil {
|
||||
@@ -1388,3 +1398,68 @@ func (i *indexImpl) FireIndexEvent() {
|
||||
internalEventIndex.FireIndexEvent()
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (i *indexImpl) TermFrequencies(field string, limit int, descending bool) (
|
||||
[]index.TermFreq, error) {
|
||||
i.mutex.RLock()
|
||||
defer i.mutex.RUnlock()
|
||||
|
||||
if !i.open {
|
||||
return nil, ErrorIndexClosed
|
||||
}
|
||||
|
||||
reader, err := i.i.Reader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if cerr := reader.Close(); err == nil && cerr != nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
insightsReader, ok := reader.(index.IndexInsightsReader)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("index reader does not support TermFrequencies")
|
||||
}
|
||||
|
||||
return insightsReader.TermFrequencies(field, limit, descending)
|
||||
}
|
||||
|
||||
func (i *indexImpl) CentroidCardinalities(field string, limit int, descending bool) (
|
||||
[]index.CentroidCardinality, error) {
|
||||
i.mutex.RLock()
|
||||
defer i.mutex.RUnlock()
|
||||
|
||||
if !i.open {
|
||||
return nil, ErrorIndexClosed
|
||||
}
|
||||
|
||||
reader, err := i.i.Reader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if cerr := reader.Close(); err == nil && cerr != nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
insightsReader, ok := reader.(index.IndexInsightsReader)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("index reader does not support CentroidCardinalities")
|
||||
}
|
||||
|
||||
centroidCardinalities, err := insightsReader.CentroidCardinalities(field, limit, descending)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for j := 0; j < len(centroidCardinalities); j++ {
|
||||
centroidCardinalities[j].Index = i.name
|
||||
}
|
||||
|
||||
return centroidCardinalities, nil
|
||||
}
|
||||
|
||||
-1
@@ -755,4 +755,3 @@ func ParseParams(r *SearchRequest, input []byte) (*RequestParams, error) {
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
|
||||
+29
-10
@@ -185,17 +185,36 @@ func (q *BooleanQuery) Searcher(ctx context.Context, i index.IndexReader, m mapp
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var init bool
|
||||
var refDoc *search.DocumentMatch
|
||||
filterFunc = func(sctx *search.SearchContext, d *search.DocumentMatch) bool {
|
||||
// Attempt to advance the filter searcher to the document identified by
|
||||
// the base searcher's (unfiltered boolean) current result (d.IndexInternalID).
|
||||
//
|
||||
// If the filter searcher successfully finds a document with the same
|
||||
// internal ID, it means the document satisfies the filter and should be kept.
|
||||
//
|
||||
// If the filter searcher returns an error, does not find a matching document,
|
||||
// or finds a document with a different internal ID, the document should be discarded.
|
||||
dm, err := filterSearcher.Advance(sctx, d.IndexInternalID)
|
||||
return err == nil && dm != nil && bytes.Equal(dm.IndexInternalID, d.IndexInternalID)
|
||||
// Initialize the reference document to point
|
||||
// to the first document in the filterSearcher
|
||||
var err error
|
||||
if !init {
|
||||
refDoc, err = filterSearcher.Next(sctx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
init = true
|
||||
}
|
||||
if refDoc == nil {
|
||||
// filterSearcher is exhausted, d is not in filter
|
||||
return false
|
||||
}
|
||||
// Compare document IDs
|
||||
cmp := bytes.Compare(refDoc.IndexInternalID, d.IndexInternalID)
|
||||
if cmp < 0 {
|
||||
// filterSearcher is behind the current document, Advance() it
|
||||
refDoc, err = filterSearcher.Advance(sctx, d.IndexInternalID)
|
||||
if err != nil || refDoc == nil {
|
||||
return false
|
||||
}
|
||||
// After advance, check if they're now equal
|
||||
return bytes.Equal(refDoc.IndexInternalID, d.IndexInternalID)
|
||||
}
|
||||
// cmp >= 0: either equal (match) or filterSearcher is ahead (no match)
|
||||
return cmp == 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-1
@@ -431,6 +431,10 @@ func expandQuery(m mapping.IndexMapping, query Query) (Query, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q.Filter, err = expand(q.Filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q, nil
|
||||
default:
|
||||
return query, nil
|
||||
@@ -481,7 +485,7 @@ func ExtractFields(q Query, m mapping.IndexMapping, fs FieldSet) (FieldSet, erro
|
||||
fs, err = ExtractFields(expandedQuery, m, fs)
|
||||
}
|
||||
case *BooleanQuery:
|
||||
for _, subq := range []Query{q.Must, q.Should, q.MustNot} {
|
||||
for _, subq := range []Query{q.Must, q.Should, q.MustNot, q.Filter} {
|
||||
fs, err = ExtractFields(subq, m, fs)
|
||||
if err != nil {
|
||||
break
|
||||
@@ -553,6 +557,10 @@ func ExtractSynonyms(ctx context.Context, m mapping.SynonymMapping, r index.Thes
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rv, err = ExtractSynonyms(ctx, m, r, q.Filter, rv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *ConjunctionQuery:
|
||||
for _, child := range q.Conjuncts {
|
||||
rv, err = ExtractSynonyms(ctx, m, r, child, rv)
|
||||
|
||||
Reference in New Issue
Block a user