15 个版本 (4 个重大变更)
0.4.1 | 2021 年 1 月 19 日 |
---|---|
0.4.0 | 2020 年 6 月 12 日 |
0.3.2 | 2020 年 6 月 11 日 |
0.2.0 | 2020 年 5 月 24 日 |
0.0.2 | 2020 年 2 月 29 日 |
#10 in #cloudflare-workers
每月 30 次下载
780KB
15K SLoC
Edgesearch
使用 Cloudflare Workers 和 WebAssembly 构建全文搜索 API。
功能
- 使用倒排索引和压缩位集(使用 Roaring Bitmaps)。
- 无需创建、管理或扩展服务器或数据库。
- 在相对较少的 KV 条目 中打包大量数据。
- 在 Cloudflare 边缘 PoP 上运行快速的 WASM 代码,以实现低延迟请求。
演示
请查看 demo 文件夹以获取带有源代码的实时演示。
工作原理
Edgesearch 通过将术语映射到包含该术语的文档 ID 的压缩位集(使用 Roaring Bitmaps)来构建反向索引,并创建自定义工作脚本和数据以上传到 Cloudflare Workers。
数据
构建一个按术语排序的术语-文档对数组,其中 术语 是一个字符串,而 文档 是一个压缩位集。
然后,将此数组分成最多 25 MiB 的大小块,因为每个 Cloudflare Workers KV 条目可以存储最多 25 MiB 的值。
要找到与术语关联的文档位集,首先进行二分查找以找到适当的块,然后在块中找到相应的对。
使用相同结构和过程来存储和检索文档内容。
将多个位集/文档打包可以减少读写成本和部署时间,并通过减少获取次数来提高缓存和执行速度。
搜索
搜索术语具有关联的模式。有三种模式以不同的方式匹配文档
模式 | 文档匹配条件 |
---|---|
需要 | 具有此模式的全部术语。 |
包含 | 至少有一个此模式的术语。 |
排除 | 没有此模式的任何术语。 |
例如,包含术语 a
、b
、c
、d
和 e
的文档将匹配查询 require (d, a) contain (g, b, f) exclude (h, i)
。
结果是通过在多个位集之间执行位操作生成的。一般计算可以概括为
result = (req_a & req_b & req_c & ...) & (con_a | con_b | con_c | ...) & ~(exc_a | exc_b | exc_c | ...)
Cloudflare
仅使用 Cloudflare Workers 时有一些优点
- 比 VM 或容器快,因为代码是在 V8 Isolate 上运行的。
- 自然地分布到边缘,以实现非常低的延迟。
- 利用 Cloudflare 进行 SSL、缓存和分发。
- 无需担心扩展、网络或服务器。
WebAssembly
Roaring Bitmaps 的 C 实现 编译为 WebAssembly。实现了 基本实现 的必要 C 标准库功能,以使编译成为可能。
使用方法
获取 CLI
预编译的二进制文件适用于 x86-64
构建 worker
数据需要格式化为两个文件
- Documents:所有文档的内容,以 NULL(ASCII 0)分隔,包括末尾。
- Document terms:对应文档的术语。每个术语和文档都必须以 NULL(ASCII 0)结尾。
此格式允许在不使用库、解析器或将所有数据加载到内存的情况下进行简单的读写。术语与文档分开,以便轻松切换或测试不同的文档-术语映射。
文档的术语与内容之间的关系对 Edgesearch 无关,并且术语不一定是文档中的单词。
文档必须是 JSON 序列化的值,例如 "hello"
、123
或 {"prop1": 1, "prop2": {}}
。
例如
文件 | 内容 |
---|---|
documents | {"title":"Stupid Love","artist":"Lady Gaga","year":2020} \0 {"title":"Don't Start Now","artist":"Dua Lipa","year":2020} \0 ... |
文档术语 | title_stupid \0 title_love \0 artist_lady \0 artist_gaga \0 year_2020 \0 \0 title_dont \0 title_start \0 title_now \0 artist_dua \0 artist_lipa \0 year_2020 \0 \0 ... |
需要提供一个文件夹供Edgesearch写入临时和构建的代码和数据文件。建议提供一个仅用于Edgesearch的文件夹,其中不包含其他内容。
edgesearch \
--documents documents \
--document-terms document-terms \
--maximum-query-results 20 \
--output-dir /path/to/edgesearch/build/output/dir/
部署工作器
edgesearch-deploy-cloudflare负责部署到Cloudflare。
这会将工作脚本和相关的WASM上传到Cloudflare Workers,并将每个键写入Cloudflare Workers KV
npx edgesearch-deploy-cloudflare \
--account-id CF_ACCOUNT_ID \
--account-email [email protected] \
--global-api-key CF_GLOBAL_API_KEY \
--name my-edgesearch \
--output-dir /path/to/edgesearch/build/output/dir/ \
--namespace CF_KV_NAMESPACE_ID \
--upload-data
本地测试
edgesearch-test-server加载构建的工作器以在本地运行。
这将在8080端口创建一个本地服务器
npx edgesearch-test-server \
--output-dir /path/to/edgesearch/build/output/dir/ \
--port 8080
客户端可以与本地测试服务器一起使用;将源(例如 http://localhost:8080
)提供给构造函数(见下文)。
调用API
适用于浏览器和Node.js的JavaScript 客户端可用于使用已部署的Edgesearch工作器
import * as Edgesearch from 'edgesearch-client';
type Document = {
title: string;
artist: string;
year: number;
};
const client = new Edgesearch.Client<Document>('https://my-edgesearch.me.workers.dev');
const query = new Edgesearch.Query();
query.add(Edgesearch.Mode.REQUIRE, 'world');
query.add(Edgesearch.Mode.CONTAIN, 'hello', 'welcome', 'greetings');
query.add(Edgesearch.Mode.EXCLUDE, 'bye', 'goodbye');
let response = await client.search(query);
query.setContinuation(response.continuation);
response = await client.search(query);
性能
检索不在边缘位置缓存的条目的搜索将较慢。为了减少缓存未命中,请确保流量一致。
依赖关系
~4–6.5MB
~115K SLoC