The
bm25 rank feature
implements the
Okapi BM25
ranking function used to estimate the relevance of a text document given a search query.
It is a pure text ranking feature which operates over an
indexed string field.
The feature is cheap to compute, about 3-4 times faster than
nativeRank,
while still providing a good rank score quality wise.
It is a good candidate to use in a first phase ranking function when ranking text documents.
Ranking function
The bm25 feature calculates a score for how good a query with terms
matches an indexed string field t in a document D.
The score is calculated as follows:
N is the total number of documents on the content node.
The local value BM25 uses in this case is exposed as the
num_docs_indexed
rank feature.
is the number of documents containing query term i for field t,
which is calculated per index existing for that field.
The max value among the indexes is used in the calculation,
which typically comes from the largest disk index.
As the IDF is calculated per content node and index, slight variations might occur.
To use the same IDF across all content nodes, set it as the significance
on each query term using annotations.
: The number of occurrences (term frequency) of query term i
in the field t of document D.
For multi-value fields we use the sum of occurrences over all elements.
: The field length (in number of words) of field t in document D.
For multi-value fields we use the sum of field lengths over all elements.
: The average field length of field t among the documents on the content node.
Can be configured using
rank-properties.
The actual index statistic is also available as the
averageFieldLength(name)
rank feature.
: A parameter used to limit how much a single query term can affect the score for document D.
With a higher value the score for a single term can continue to go up relatively more
when more occurrences for that term exists. Default value is 1.2.
Can be configured using
rank-properties.
: A parameter used to control the effect of the field length of field t
compared to the average field length. Default value is 0.75.
Can be configured using
rank-properties.
Example
In the following example we have an indexed string field content,
and a rank profile using the bm25 rank feature.
Note that the field must be enabled for usage with the bm25 feature
by setting the enable-bm25 flag in the
index
section of the field definition.
If the enable-bm25 flag is turned on after documents are
already fed then proton performs
a memory index flush
followed by a disk index
fusion to prepare the posting lists for use with bm25.
Use the custom
component state API on each content node and
examine pending_urgent_flush to determine if the
preparation is still ongoing:
bm25(content) sums the contribution of every query term searching content.
When a query is built from several parts, it is often useful to score those parts separately.
This is done by attaching a
label
to the query items, using the label annotation in
YQL, and referring to that label from the rank profile:
select*fromexamplewhere
({label:"must"}contentcontains"vespa") and
({label:"nice"}(contentcontains"ranking"orcontentcontains"relevance"))
A label set on an operator enclosing others, as on the or above, is inherited by every term
inside it, so a label can be given per clause instead of per term.
With many labels, or when the labels are not known when the schema is written,
use bm25_for_labels(field) instead.
It returns a tensor<float>(label{}) with one cell per label that scores in the field,
holding the sum of the BM25 scores of that label's terms:
Other typical uses are reduce(bm25_for_labels(content), max, label) to rank by the best
scoring labeled part, or feeding the tensor to a machine-learned model.
A label only gets a cell when it actually scores, and terms without a label are not included at all,
so reduce(bm25_for_labels(content), sum, label) equals bm25(content) only when
every scoring term carries exactly one label.
Note:
A single label can be picked out with
bm25_for_labels(content){label:must}, but bm25(field: content, label: must)
is faster, as it does not compute the full tensor.