snapshot of https://github.com/blevesearch/bleve.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
Coverage / coverage (push) Failing after 6s
Tests / test (1.24.x, ubuntu-latest) (push) Failing after 42s
Tests / test (1.26.x, ubuntu-latest) (push) Failing after 38s
Tests / test (1.25.x, ubuntu-latest) (push) Failing after 35s
Tests / test (1.25.x, windows-latest) (push) Has been cancelled
Tests / test (1.26.x, macos-latest) (push) Has been cancelled
Tests / test (1.26.x, windows-latest) (push) Has been cancelled
Tests / test (1.24.x, macos-latest) (push) Has been cancelled
Tests / test (1.24.x, windows-latest) (push) Has been cancelled
Tests / test (1.25.x, macos-latest) (push) Has been cancelled
Some checks failed
Coverage / coverage (push) Failing after 6s
Tests / test (1.24.x, ubuntu-latest) (push) Failing after 42s
Tests / test (1.26.x, ubuntu-latest) (push) Failing after 38s
Tests / test (1.25.x, ubuntu-latest) (push) Failing after 35s
Tests / test (1.25.x, windows-latest) (push) Has been cancelled
Tests / test (1.26.x, macos-latest) (push) Has been cancelled
Tests / test (1.26.x, windows-latest) (push) Has been cancelled
Tests / test (1.24.x, macos-latest) (push) Has been cancelled
Tests / test (1.24.x, windows-latest) (push) Has been cancelled
Tests / test (1.25.x, macos-latest) (push) Has been cancelled
This commit is contained in:
137
search/query/custom_score.go
Normal file
137
search/query/custom_score.go
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) 2026 Couchbase, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/blevesearch/bleve/v2/mapping"
|
||||
"github.com/blevesearch/bleve/v2/search"
|
||||
"github.com/blevesearch/bleve/v2/search/searcher"
|
||||
index "github.com/blevesearch/bleve_index_api"
|
||||
)
|
||||
|
||||
// CustomScoreQuery wraps a child query and re-scores its candidate matches via
|
||||
// an embedder-provided per-hit callback.
|
||||
type CustomScoreQuery struct {
|
||||
Query Query `json:"query"`
|
||||
Fields []string `json:"fields,omitempty"`
|
||||
|
||||
scoreFunc searcher.CustomScoreFunc
|
||||
payload map[string]interface{}
|
||||
}
|
||||
|
||||
// CustomScoreQueryParser lets an embedder override parsing of
|
||||
// {"custom_score": ...} nodes. It is intended to be assigned once during
|
||||
// process startup or init, before any queries are parsed; callers must not
|
||||
// mutate it concurrently with ParseQuery(). For example:
|
||||
//
|
||||
// func init() {
|
||||
// query.CustomScoreQueryParser = parseCustomScoreQuery
|
||||
// }
|
||||
var CustomScoreQueryParser func([]byte) (Query, error)
|
||||
|
||||
func NewCustomScoreQueryWithScorer(query Query, score searcher.CustomScoreFunc, fields []string, payload map[string]interface{}) *CustomScoreQuery {
|
||||
return &CustomScoreQuery{
|
||||
Query: query,
|
||||
Fields: fields,
|
||||
scoreFunc: score,
|
||||
payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *CustomScoreQuery) Searcher(ctx context.Context, i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) {
|
||||
if q == nil {
|
||||
return nil, fmt.Errorf("custom score query is nil")
|
||||
}
|
||||
if q.Query == nil {
|
||||
return nil, fmt.Errorf("custom score query must have a query")
|
||||
}
|
||||
if q.scoreFunc == nil {
|
||||
return nil, fmt.Errorf("custom score query must have a score callback")
|
||||
}
|
||||
|
||||
// Build the inner searcher first; custom scoring wraps its output.
|
||||
childSearcher, err := q.Query.Searcher(ctx, i, m, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a doc value reader for the requested fields (if any) so the
|
||||
// searcher can populate d.Fields before invoking the callback.
|
||||
var dvReader index.DocValueReader
|
||||
var fieldTypes map[string]string
|
||||
if len(q.Fields) > 0 {
|
||||
// Expand a "*" wildcard to the concrete field set; the doc-value
|
||||
// reader matches field names literally and would otherwise load
|
||||
// nothing, leaving d.Fields empty in the callback.
|
||||
fields, err2 := expandFieldWildcard(q.Fields, i)
|
||||
if err2 != nil {
|
||||
_ = childSearcher.Close()
|
||||
return nil, err2
|
||||
}
|
||||
dvReader, err2 = i.DocValueReader(fields)
|
||||
if err2 != nil {
|
||||
_ = childSearcher.Close()
|
||||
return nil, err2
|
||||
}
|
||||
fieldTypes = resolveFieldTypes(fields, m)
|
||||
}
|
||||
|
||||
return searcher.NewCustomScoreSearcher(ctx, childSearcher, q.scoreFunc, dvReader, i, fieldTypes, options.Explain), nil
|
||||
}
|
||||
|
||||
func (q *CustomScoreQuery) Validate() error {
|
||||
if q == nil {
|
||||
return fmt.Errorf("custom score query is nil")
|
||||
}
|
||||
if q.Query == nil {
|
||||
return fmt.Errorf("custom score query must have a query")
|
||||
}
|
||||
if q.scoreFunc == nil {
|
||||
return fmt.Errorf("custom score query must have a score callback")
|
||||
}
|
||||
if vq, ok := q.Query.(ValidatableQuery); ok {
|
||||
return vq.Validate()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *CustomScoreQuery) MarshalJSON() ([]byte, error) {
|
||||
inner := make(map[string]interface{}, len(q.payload)+2)
|
||||
for k, v := range q.payload {
|
||||
inner[k] = v
|
||||
}
|
||||
inner["query"] = q.Query
|
||||
if len(q.Fields) > 0 {
|
||||
inner["fields"] = q.Fields
|
||||
}
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"custom_score": inner,
|
||||
})
|
||||
}
|
||||
|
||||
func (q *CustomScoreQuery) UnmarshalJSON(data []byte) error {
|
||||
child, fields, payload, err := unmarshalCustomQueryPayload(data, "custom_score")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q.Query = child
|
||||
q.Fields = fields
|
||||
q.payload = payload
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user