- 向量数据库
- 数据库
- 人工智能
- 后端
【免费下载链接】lancedb
Developer-friendly OSS embedded retrieval library for multimodal AI. Search More; Manage Less.
导读
AutoQuery是@lancedb/lancedbNode.js SDK 中面向字符串搜索的自动路由查询构建器:它不要求开发者预先判断查询应该走全文检索(Full-Text Search)还是向量搜索(Vector Search),而是在每次执行时根据当前表版本(revision)的 schema 元数据动态决定路由。本文将完整讲解AutoQuery的触发入口、路由决策原理、全部可用的查询构建方法与执行/调试 API,并结合 query.ts 与 table.ts 的源码实现及 table.test.ts 的测试用例,帮助读者掌握"同一段搜索代码随表结构变化自动切换检索模式"的实战方案。
AutoQuery 是什么
在 AutoQuery 类文档 中,AutoQuery被定义为:
A builder for automatic string searches. Automatic search determines whether to use full-text or vector search from the table revision selected for each execution. This builder exposes the common operations supported by both query families.
即:自动字符串搜索构建器。它的核心特征是"每次执行时基于所选定的表版本决定使用全文搜索还是向量搜索",并对外暴露两类查询族(Query与VectorQuery)共同支持的通用操作。
从继承关系看,AutoQuery继承自StandardQueryBase<NativeQuery | NativeVectorQuery>(见 query.ts),因此它天然拥有全文查询与向量查询共享的构建能力;其内部持有inner: Query | VectorQuery | Promise<Query | VectorQuery>属性,最终真正执行时该属性会被解析为具体的原生查询对象。
需要强调的关键点:路由决策发生在执行时,而非构建时。也就是说,即使你在构建查询时表还没有 embedding 元数据,只要在真正执行(如调用toArray())前表被覆写为携带 embedding 函数的 schema,查询就会自动切换为向量搜索;反之亦然。这正是"automatic"一词的含义。
触发入口:Table.search 与 queryType 分派
AutoQuery并非由用户直接new出来(构造函数被标记为@hidden),而是通过Table.search()在默认参数下返回。在 table.ts 中:
search( query: string | IntoVector | MultiVector | FullTextQuery, queryType: string = "auto", ftsColumns?: string | string[], ): VectorQuery | Query | AutoQuery其分派逻辑(table.ts)如下:
- 若
query不是字符串且不是FullTextQuery实例,则视为向量输入,直接走vectorSearch(query); - 若
queryType === "fts",返回query().fullTextSearch(query, { columns: ftsColumns }),强制全文检索; - 若
queryType === "auto"(默认值):- 若传入的是
FullTextQuery实例(如MatchQuery、PhraseQuery等),直接构造全文查询; - 否则调用
createAutoQuery(...)创建AutoQuery;
- 若传入的是
- 其他
queryType(如显式"vector")走nearestTo(queryPromise),其中查询向量由表上注册的 embedding 函数计算,若表未定义 embedding 函数则 reject"No embedding functions are defined in the table"(对应测试见 table.test.ts)。
因此最典型的用法就是:
import * as lancedb from "@lancedb/lancedb"; const db = await lancedb.connect("./.lancedb"); const table = await db.createTable("my_table", [{ text: "hello world" }]); // 默认 queryType 为 "auto",返回 AutoQuery const results = await table.search("hello").toArray();底层路由原理:createAutoQuery 与表快照
AutoQuery的路由逻辑实现在createAutoQuery(query.ts),其核心是"每次执行时对表做一次快照并读取 schema 元数据":
const snapshotRoute = async (): Promise<RouteSnapshot> => { const snapshot = await table.querySnapshot(); const schema = tableFromIPC(await snapshot.schema()).schema; return { table: snapshot, embeddingMetadata: schema.metadata.get("embedding_functions"), }; };路由判定规则非常直观:
- 若表 schema 的元数据中没有
embedding_functions,则内部构造inner.fullTextSearch({ query, columns }),走全文检索; - 若存在
embedding_functions,则解析 embedding 函数并通过computeQueryEmbeddings(query)计算查询向量,然后走nearestToNative(...)的向量搜索。
也就是说:表上是否注册了 embedding 函数,决定了字符串搜索的路由方向。在 table.ts 中,embedding 元数据通过getRegistry().parseFunctions(new Map([["embedding_functions", metadata]]))解析,并调用首个 embedding 函数的computeQueryEmbeddings生成查询向量(当前实现仅支持单个 embedding 函数,源码中以TODO: Support multiple embedding functions标注)。
AutoQuery类本身(query.ts)使用"调用延迟绑定"模式实现"构建期统一、执行期路由":
- 所有链式调用(如
where、limit、select)通过doCall被收集进calls数组,而不是立即作用到内部对象; - 每次执行(终端操作如
toArray、toArrow)时getInner()先调用createInner()生成当前路由对应的原生查询,再把已收集的全部调用依次apply上去。
这样既保证了构建器可以被重复执行(每次执行都重新路由),又避免了在构建阶段就锁定查询类型。测试 table.test.ts 专门验证了这一点:同一个autoQuery对象在表被覆写为带 embedding schema 后执行,结果从全文检索语义切换到向量检索语义;同时验证了"构建器可复用执行时只新增快照调用、embedding 计算有缓存"的行为(snapshotCalls逐次 +1,而initCalls/queryCalls保持不增长)。
通用过滤与排序方法(StandardQueryBase 族)
AutoQuery继承了StandardQueryBase提供的以下通用方法(源码见 query.ts),这些方法同时被全文查询与向量查询支持:
where() / filter()
以 SQL 字符串作为过滤条件:
where(predicate: string): this示例:
table.search("hello").where("x > 10").toArray(); table.search("hello").where("y > 0 AND y < 100").toArray(); table.search("hello").where("x > 5 OR y = 'test'").toArray();两点使用要点(文档明确说明):
- 过滤性能可以通过在过滤列上创建标量索引(scalar index)来提升;
- 多次调用
where时,多个过滤条件以逻辑 AND 合并,而不是后者替换前者。底层实现为inner.onlyIf(predicate)(见 query.ts)。
filter()是where的已废弃别名(@deprecated Use where instead),新代码应直接使用where。
fullTextSearch()
即使在AutoQuery上也可以显式叠加全文搜索约束(query.ts):
fullTextSearch(query: string | FullTextQuery, options?: Partial<FullTextSearchOptions>): thisquery可以是普通字符串,也可以是 FullTextQuery 实例(如MatchQuery、PhraseQuery、BoostQuery、MultiMatchQuery、BooleanQuery,定义见 query.ts);options.columns可以是单个字符串或字符串数组,指定参与全文检索的列;当传入FullTextQuery实例时直接使用其内部定义。
limit() 与 offset()
limit(limit: number): this offset(offset: number): this- 默认无 limit:文档明确指出,普通搜索(plain search)若不调用
limit,将返回表中所有合法行;而向量搜索则默认limit为 10(见 query.ts 中nearestTo的注释)。 offset用于跳过指定数量的行后再返回结果,典型场景是分页。
orderBy()
按指定列排序(query.ts):
orderBy(ordering: ColumnOrdering | ColumnOrdering[]): thisColumnOrdering(见 ColumnOrdering 接口)支持columnName、ascending(默认true)、nullsFirst(默认false)字段,也可传入数组实现多列排序。注意:文档在useLsm中提示,MemWAL 表上的 LSM 扫描器不支持orderBy,此时需配合useLsm(false)使用。
fastSearch()
fastSearch(): this跳过未索引数据的搜索,可显著加快查询速度,但会漏掉尚未建立索引的数据。文档明确建议:先用 Table#optimize 将全部未索引数据建立索引,再考虑使用fastSearch。
useLsm():MemWAL 读路由控制
useLsm用于控制 MemWAL 表的读取路由(query.ts):
useLsm(enable: boolean): this其语义如下(文档原文要点):
- 默认(不调用):当表带有 MemWAL 写入配置(通过 Table#setLsmWriteSpec 设置)时,读取会路由到 LSM 扫描器,从而一并返回通过
mergeInsertLSM 路径写入、尚未压实(compact)进基表的数据(包括 active/frozen 内存 memtable 与已刷新的 generation),并按主键去重;没有该配置的表则直接读取基表。 useLsm(true):强制使用 LSM 扫描器;若表没有 MemWAL 写入配置则直接报错。useLsm(false):绕过 MemWAL,只读取基表(即使表带有配置)。- 限制:LSM 扫描器不支持所有查询形态(如 reranking、hybrid search、
orderBy)。在 MemWAL 表上使用这些形态时,除非设置useLsm(false),否则会报错——因为只读基表会静默丢失尚未压实的 MemWAL 数据。
结果控制与执行方法(QueryBase 族)
select():列裁剪与动态列
select控制返回列(query.ts):
select(columns: string | string[] | Record<string, string> | Map<string, string>): this- 默认返回全部列,但这会显著影响延迟。LanceDB 以列式(columnar)存储,可以精细地只读取所需列,因此文档强调最佳实践是总是将查询裁剪到所需列;
- 传入字符串或字符串数组时,只返回这些列;
- 传入
Map<string, string>或Record<string, string>可创建"动态列",键为返回列名,值为计算该列的 SQL 表达式。例如 SQL 的SELECT a + b AS combined, c等价于:
table.search("hello").select(new Map([["combined", "a + b"], ["c", "c"]])).toArray();- 列始终按给定顺序返回,即使该顺序与写入数据时的列序不同;
- 文档提示:
Record<string, string>(对象字面量)依赖Object.entries的插入顺序,容易出错,Map更可靠。
withRowId():返回行 ID
withRowId(): this在结果中返回行 ID 列,可用于跨查询匹配结果——例如同时执行全文检索与向量检索,再依据行 ID 做混合搜索(hybrid search)的融合。
execute() 与迭代
execute(options?)返回AsyncGenerator<RecordBatch<any>>,即 RecordBatch 的异步迭代器(query.ts)。文档指出:默认情况下 LanceDB 使用多线程计算结果,结果集较大时会同时处理多个 batch;但这种预读(readahead)是受限的,若消费速度慢会施加背压(backpressure),从而约束单次查询的最大内存占用。因此流式处理大批量结果时,可以直接for await消费。
toArray() 与 toArrow()
toArray(options?): Promise<any[]> // 收集结果为对象数组 toArrow(options?): Promise<ArrowTable> // 收集结果为 Arrow Table两者均接受可选的QueryExecutionOptions(见 QueryExecutionOptions 接口,包含maxBatchLength、timeoutMs等执行参数),是AutoQuery最常见的终端操作。
outputSchema():执行前预览输出结构
outputSchema(): Promise<Schema<any>>返回本次查询将要输出结果的 Arrow Schema,可在真正执行前检查返回列的类型与名称(query.ts)。
查询计划调试:explainPlan 与 analyzePlan
这两个方法对排查"查询到底走了哪条路径、消耗在哪"非常关键,尤其适合验证AutoQuery的路由结果。
explainPlan()
explainPlan(verbose: boolean = false): Promise<string>生成查询执行计划的文字说明;verbose为true时提供更详细的信息。示例:
import * as lancedb from "@lancedb/lancedb"; const db = await lancedb.connect("./.lancedb"); const table = await db.createTable("my_table", [ { vector: [1.1, 0.9], id: "1" }, ]); const plan = await table.query().nearestTo([0.5, 0.2]).explainPlan();analyzePlan()
analyzePlan(distributedMetrics?): Promise<string>执行查询并返回带运行时指标(runtime metrics)的物理查询计划,适合性能分析与调试——它展示查询是如何被执行的,并包含各步骤的耗时、处理行数、I/O 统计等。参数distributedMetrics(类型见 AnalyzePlanDistributedMetrics)控制远程查询计划中分布式工作节点指标的展示方式,默认值为"aggregate"(聚合)。
官方文档给出的完整示例输出(向量查询路径,指标已内联):
AnalyzeExec verbose=true, metrics=[] ProjectionExec: expr=[id@3 as id, vector@0 as vector, _distance@2 as _distance], metrics=[output_rows=1, elapsed_compute=3.292µs] Take: columns="vector, _rowid, _distance, (id)", metrics=[output_rows=1, elapsed_compute=66.001µs, batches_processed=1, bytes_read=8, iops=1, requests=1] CoalesceBatchesExec: target_batch_size=1024, metrics=[output_rows=1, elapsed_compute=3.333µs] GlobalLimitExec: skip=0, fetch=10, metrics=[output_rows=1, elapsed_compute=167ns] FilterExec: _distance@2 IS NOT NULL, metrics=[output_rows=1, elapsed_compute=8.542µs] SortExec: TopK(fetch=10), expr=[_distance@2 ASC NULLS LAST], metrics=[output_rows=1, elapsed_compute=63.25µs, row_replacements=1] KNNVectorDistance: metric=l2, metrics=[output_rows=1, elapsed_compute=114.333µs, output_batches=1] LanceScan: uri=/path/to/data, projection=[vector], row_id=true, row_addr=false, ordered=false, metrics=[output_rows=1, elapsed_compute=103.626µs, bytes_read=549, iops=2, requests=2]从该输出可以看出向量搜索的完整执行链:LanceScan(表扫描)→KNNVectorDistance(L2 距离计算)→SortExec(TopK 排序)→FilterExec→GlobalLimitExec→CoalesceBatchesExec→Take(回取所需列)→ProjectionExec。若AutoQuery在无 embedding 元数据的表上路由到全文检索,计划树中的KNNVectorDistance会被全文检索相关算子取代,因此analyzePlan也是验证自动路由方向的实用手段。
实战模式与注意事项
综合文档与源码,使用AutoQuery时有几个值得注意的实践要点:
- 依赖 embedding 元数据自动路由:只要表 schema 带有
embedding_functions元数据(通常通过LanceSchema+ 注册的EmbeddingFunction创建),table.search("文本")就会自动做向量搜索;否则退化为全文检索。创建表时使用 embedding 函数生成 schema 的示例可参考 embedding 模块 与LanceSchema(embedding/functions/LanceSchema.md)。 - 构建器可复用:
AutoQuery允许同一个构建器重复执行(每次执行重新取表快照并路由),适合需要跟随表版本演进的场景;配合只读一致性配置(如readConsistencyInterval)可观察到一致的快照行为。 - 全文检索需要索引:无 embedding 表上的自动路由是全文检索,若未在文本列创建 FTS 索引(
Index.fts()),检索可能无法返回预期结果;创建索引可用 Table#createIndex。 - 明确指定类型:需要强制某种检索方式时,可在
search(query, "fts", ftsColumns)或search(query, "vector")中显式指定queryType,避免依赖自动路由;向量检索模式下若表未注册 embedding 函数会直接报错(见 table.test.ts)。 - 配合 where 与 select 控制开销:自动路由本身不改变列式存储的 I/O 优化特性,始终建议用
select裁剪列、用where收紧范围,并用analyzePlan观察各阶段指标。
小结
AutoQuery是 LanceDB Node.js SDK 中"一个查询、两种模式"的抽象:它以表快照的embedding_functions元数据为路由依据,在每次执行时自动在全文检索与向量检索之间切换,并把两类查询共有的过滤、排序、分页、列裁剪、行 ID、MemWAL 路由与执行计划调试能力统一暴露给调用方。对于"同一份数据既可能带 embedding 又可能不带"的动态表结构场景,AutoQuery让应用代码无需随表结构变化而改动,值得作为字符串搜索的默认入口使用。
- 向量数据库
- 数据库
- 人工智能
- 后端
【免费下载链接】lancedb
Developer-friendly OSS embedded retrieval library for multimodal AI. Search More; Manage Less.
相关推荐
LanceDB Node.js 查询构建器 Query 类完全指南:从向量检索到执行计划分析
LanceDB Node.js 查询构建器 Query 类完全指南:从向量检索到执行计划分析 本文以 LanceDB 官方 Node.js SDK( @lanc
向量数据库数据库人工智能后端MariaDB 集成指南:在 Haystack 中构建基于向量检索与全文检索的 RAG 应用
MariaDB 集成指南:在 Haystack 中构建基于向量检索与全文检索的 RAG 应用 MariaDB 11.7+ 原生引入 VECTOR 数据类型与 M
人工智能大模型RAGAI AgentNLPLLM Zoomcamp 向量检索进阶路线:从文本搜索起步,用评估驱动向向量搜索与混合检索演进
LLM Zoomcamp 向量检索进阶路线:从文本搜索起步,用评估驱动向向量搜索与混合检索演进 向量搜索能够按语义匹配文档,弥补关键词搜索无法理解"换一种说法"
示例工程教程人工智能大模型
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考