扩展现有的搜索和查询功能
关于查询扩展器和评分函数的扩展详情
RediSearch 支持一种扩展机制,类似于 Redis 支持的模块。目前 API 非常简洁,尚不支持在运行中的服务器上动态加载扩展。相反,扩展必须用 C(或具有与 C 接口的语言)编写,并编译成可以在启动时加载的动态库。
目前有两种扩展API:
- 查询扩展器,其作用是扩展查询标记(即词干提取器)。
- 评分函数,其作用是在查询时对搜索结果进行排序。
注册和加载扩展
扩展应编译成动态库文件(例如,.so
文件),并在初始化期间加载到 RediSearch 模块中。
编译
Extensions should be compiled and linked as dynamic libraries. An example Makefile for an extension [can be found here](https://github.com/RediSearch/RediSearch/blob/master/tests/ctests/ext-example/Makefile).
That folder also contains an example extension that is used for testing and can be taken as a skeleton for implementing your own extension.
加载中
Loading an extension is done by appending `EXTLOAD {path/to/ext.so}` after the `loadmodule` configuration directive when loading the RediSearch module. For example:
```sh
$ redis-server --loadmodule ./redisearch.so EXTLOAD ./ext/my_extension.so
```
This causes the RediSearch module to automatically load the extension and register its expanders and scorers.
初始化扩展
扩展的入口点是一个具有以下签名的函数:
int RS_ExtensionInit(RSExtensionCtx *ctx);
当加载扩展时,RediSearch 会查找此函数并调用它。此函数负责注册和初始化扩展器和评分器。
出错时应返回REDISEARCH_ERR,成功时应返回REDISEARCH_OK。
示例初始化函数
#include <redisearch.h> //must be in the include path
int RS_ExtensionInit(RSExtensionCtx *ctx) {
/* Register a scoring function with an alias my_scorer and no special private data and free function */
if (ctx->RegisterScoringFunction("my_scorer", MyCustomScorer, NULL, NULL) == REDISEARCH_ERR) {
return REDISEARCH_ERR;
}
/* Register a query expander */
if (ctx->RegisterQueryExpander("my_expander", MyExpander, NULL, NULL) ==
REDISEARCH_ERR) {
return REDISEARCH_ERR;
}
return REDISEARCH_OK;
}
调用您的自定义函数
在执行查询时,您可以通过指定带有给定别名的SCORER或EXPANDER参数来使用您的评分器或扩展器。例如:
FT.SEARCH my_index "foo bar" EXPANDER my_expander SCORER my_scorer
注意: Expander 和 scorer 的别名是区分大小写的。
查询扩展器 API
仅支持基本的查询扩展,一次一个标记。扩展器可以决定用任意数量的标记来扩展任何给定的标记,这些标记将在查询时进行联合合并。
扩展器的API如下:
#include <redisearch.h> //must be in the include path
void MyQueryExpander(RSQueryExpanderCtx *ctx, RSToken *token) {
...
}
RSQueryExpanderCtx
RSQueryExpanderCtx
是一个包含扩展私有数据和用于扩展查询的回调方法的上下文。它被定义为:
typedef struct RSQueryExpanderCtx {
/* Opaque query object used internally by the engine, and should not be accessed */
struct RSQuery *query;
/* Opaque query node object used internally by the engine, and should not be accessed */
struct RSQueryNode **currentNode;
/* Private data of the extension, set on extension initialization */
void *privdata;
/* The language of the query, defaults to "english" */
const char *language;
/* ExpandToken allows the user to add an expansion of the token in the query, that will be
* union-merged with the given token in query time. str is the expanded string, len is its length,
* and flags is a 32 bit flag mask that can be used by the extension to set private information on
* the token */
void (*ExpandToken)(struct RSQueryExpanderCtx *ctx, const char *str, size_t len,
RSTokenFlags flags);
/* SetPayload allows the query expander to set GLOBAL payload on the query (not unique per token)
*/
void (*SetPayload)(struct RSQueryExpanderCtx *ctx, RSPayload payload);
} RSQueryExpanderCtx;
RSToken
RSToken
表示要扩展的单个查询令牌,其定义如下:
/* A token in the query. The expanders receive query tokens and can expand the query with more query
* tokens */
typedef struct {
/* The token string - which may or may not be NULL terminated */
const char *str;
/* The token length */
size_t len;
/* 1 if the token is the result of query expansion */
uint8_t expanded:1;
/* Extension specific token flags that can be examined later by the scoring function */
RSTokenFlags flags;
} RSToken;
评分函数API
对于最终排名,评分函数分析查询检索到的每个文档,不仅考虑触发文档检索的术语,还考虑其先前的分数、长度等元数据。
由于评分函数对每个文档进行评估,可能数百万次,而且由于Redis是单线程的,因此重要的是它要尽可能快地工作并进行高度优化。
评分函数应用于每个文档的每个潜在结果,并通过以下签名实现:
double MyScoringFunction(RSScoringFunctionCtx *ctx, RSIndexResult *res,
RSDocumentMetadata *dmd, double minScore);
RSScoringFunctionCtx
是一个实现了一些辅助方法的上下文。
RSIndexResult
是包含文档ID、频率、术语和偏移量的结果信息。
RSDocumentMetadata
是一个包含文档全局信息的对象,例如其预设分数。
minScore
是产生与搜索相关结果的最低分数。它可以用于在中间、之前甚至开始之前停止处理。
函数的返回值是一个double
,表示结果的最终分数。
返回0会导致结果被计数,但如果存在分数大于0的结果,它们将出现在其上方。
要完全过滤掉一个结果并且不计入总数,评分器应返回特殊值RS_SCORE_FILTEROUT
,该值在内部设置为负无穷大,或-1/0。
RSScoringFunctionCtx
这是一个包含以下成员的对象:
void *privdata
: 一个指向扩展在初始化时设置的对象的指针。RSPayload payload*
: 由查询扩展器或客户端设置的Payload对象。int GetSlop(RSIndexResult *res)*
: 一个回调方法,用于生成查询词之间的总最小距离。这可以用于优先选择slop较小且词项彼此更接近的结果。
RSIndexResult
这是一个包含当前索引结果信息的对象,它是所有导致当前文档被视为有效结果的术语的聚合。详情请参见redisearch.h
。
RSDocumentMetadata
这是一个描述全局信息的对象,与当前查询无关,关于由评分函数评估的文档。
示例查询扩展器
此示例查询扩展器使用术语 foo 扩展每个标记:
#include <redisearch.h> //must be in the include path
void DummyExpander(RSQueryExpanderCtx *ctx, RSToken *token) {
ctx->ExpandToken(ctx, strdup("foo"), strlen("foo"), 0x1337);
}
示例评分函数
这是一个实际的评分函数,它计算文档的TF-IDF,将其乘以文档分数,然后除以slop:
#include <redisearch.h> //must be in the include path
double TFIDFScorer(RSScoringFunctionCtx *ctx, RSIndexResult *h, RSDocumentMetadata *dmd,
double minScore) {
// no need to evaluate documents with score 0
if (dmd->score == 0) return 0;
// calculate sum(tf-idf) for each term in the result
double tfidf = 0;
for (int i = 0; i < h->numRecords; i++) {
// take the term frequency and multiply by the term IDF, add that to the total
tfidf += (float)h->records[i].freq * (h->records[i].term ? h->records[i].term->idf : 0);
}
// normalize by the maximal frequency of any term in the document
tfidf /= (double)dmd->maxFreq;
// multiply by the document score (between 0 and 1)
tfidf *= dmd->score;
// no need to factor the slop if tfidf is already below minimal score
if (tfidf < minScore) {
return 0;
}
// get the slop and divide the result by it, making sure we prefer results with closer terms
tfidf /= (double)ctx->GetSlop(h);
return tfidf;
}