跳转到内容

全文搜索(基于Tantivy的FTS)

LanceDB还通过Tantivy提供全文搜索支持,使您能够在检索解决方案中集成基于关键词的搜索(基于BM25算法)。

基于tantivy的全文搜索(FTS)仅支持Python同步API,不支持在对象存储上构建索引或增量索引。如需这些功能,请尝试原生FTS native FTS

安装

要使用全文搜索功能,请安装依赖项 tantivy-py

# Say you want to use tantivy==0.20.1
pip install tantivy==0.20.1

示例

假设我们有一个名为my_table的LanceDB表,其字符串列content需要通过关键词搜索进行索引和查询,在使用关键词搜索前必须先创建FTS索引。

import lancedb

uri = "data/sample-lancedb"
db = lancedb.connect(uri)

table = db.create_table(
    "my_table",
    data=[
        {"id": 1, "vector": [3.1, 4.1], "title": "happy puppy", "content": "Frodo was a happy puppy", "meta": "foo"},
        {"id": 2, "vector": [5.9, 26.5], "title": "playing kittens", "content": "There are several kittens playing around the puppy", "meta": "bar"},
    ],
)

# passing `use_tantivy=False` to use lance FTS index
# `use_tantivy=True` by default
table.create_fts_index("content", use_tantivy=True)
table.search("puppy").limit(10).select(["content"]).to_list()
# [{'text': 'Frodo was a happy puppy', '_score': 0.6931471824645996}]
# ...

默认情况下,它会在所有已建立索引的列上进行搜索,因此在存在多个索引列时非常有用。

注意

如果搜索输入的类型是str,LanceDB会自动在现有的全文搜索索引上进行搜索。如果提供向量作为输入,LanceDB则会转而搜索近似最近邻索引。

分词

默认情况下,文本会通过标点符号和空格进行分词,并移除长度超过40个字符的词元。如需支持特定语言的分词处理,请提供参数tokenizer_name,格式为双字母语言代码加上"_stem"后缀。例如英语对应的是"en_stem"。

table.create_fts_index("content", use_tantivy=True, tokenizer_name="en_stem", replace=True)

目前支持以下语言

为多列建立索引

如果您有多个字符串列需要索引,无需手动合并它们——只需将它们全部作为列表传递给create_fts_index

table.create_fts_index(["title", "content"], use_tantivy=True, replace=True)

请注意,搜索API调用保持不变 - 您可以一次性在所有索引列上进行搜索。

筛选

目前LanceDB全文搜索功能支持后过滤,意味着过滤器会应用于全文搜索结果之上(如需预过滤请参阅native FTS)。这可以通过熟悉的where语法调用:

table.search("puppy").limit(10).where("meta='foo'").to_list()

排序

你可以在创建全文搜索索引时通过指定ordering_field_names来预先对文档进行排序。一旦预先排序完成,你就可以在搜索时指定ordering_field_name来返回按给定字段排序的结果。例如,

table.create_fts_index(["content"], use_tantivy=True, ordering_field_names=["id"], replace=True)

(table.search("puppy", ordering_field_name="id")
 .limit(20)
 .to_list())

注意

如果想在查询时指定排序字段,则必须在索引创建阶段也定义该字段。否则查询时会报错,提示类似ValueError: The field does not exist: xxx

注意

用于排序的字段必须是无符号整数类型,否则在索引过程中会出现类似TypeError: argument 'value': 'float' object cannot be interpreted as an integer的错误。

注意

您可以在索引时指定多个排序字段。但在查询时仅支持一个排序字段。

短语查询与术语查询对比

要进行全文搜索,您可以指定短语查询如"the old man and the sea", 或者术语搜索查询如"(Old AND Man) AND Sea"。有关术语查询语法的更多详细信息,请参阅Tantivy的查询解析器规则

注意

查询解析器会对存在歧义的查询抛出异常。例如,在查询they could have been dogs OR cats中,OR是大写的,因此被视为关键字查询运算符。但左侧部分的处理方式存在歧义。因此如果直接提交这个搜索查询,你会得到Syntax Error: they could have been dogs OR cats错误。

# This raises a syntax error
table.search("they could have been dogs OR cats")

另一方面,将OR转换为小写的or是可行的,因为不存在大写的逻辑运算符,查询会被视为短语查询。

# This works!
table.search("they could have been dogs or cats")

根据您想要执行的查询类型,记住哪些操作会导致语法错误可能会很麻烦。为了简化这一点,当您想要执行短语查询时,可以通过以下两种方式之一强制执行:

  1. 将双引号包裹的查询语句放在单引号内。例如,table.search('"they could have been dogs OR cats"')会被视为短语查询。
  2. 显式声明phrase_query()方法。当您的短语查询本身包含双引号时,这非常有用。例如,table.search('the cats OR dogs were not really "pets" at all').phrase_query()会被视为短语查询。

通常情况下,声明为短语查询的语句在解析时会被双引号包裹,其中嵌套的双引号会被替换为单引号。

配置

默认情况下,LanceDB为创建索引配置了1GB的堆内存大小限制。如果在较小节点上运行,可以降低此值;若需在索引较大语料库时获得更快性能,则可增加此值。

# configure a 512MB heap size
heap = 1024 * 1024 * 512
table.create_fts_index(["title", "content"], use_tantivy=True, writer_heap_size=heap, replace=True)

当前限制

  1. 创建FTS索引后新增的数据会出现在搜索结果中,但由于需要对未索引部分进行平面搜索,会导致延迟增加。使用create_fts_index重新索引可以降低延迟。LanceDB Cloud自动执行此合并过程,将对搜索速度的影响降至最低。

  2. 目前我们仅支持本地文件系统路径用于FTS索引。 这是tantivy的一个限制。我们已经实现了一个对象存储插件, 但在tantivy-py中无法指定使用它。