#全文搜索 #文本搜索 #Cloudflare Workers #搜索 #位图 #完整 #文本

bin+lib edgesearch

使用 Cloudflare Workers、WebAssembly 和 Roaring Bitmaps 的无服务器全文搜索

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 次下载

MIT 许可证

780KB
15K SLoC

C 14K SLoC // 0.2% comments Rust 639 SLoC // 0.0% comments JavaScript 407 SLoC // 0.1% comments

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 的值。

要找到与术语关联的文档位集,首先进行二分查找以找到适当的块,然后在块中找到相应的对。

使用相同结构和过程来存储和检索文档内容。

将多个位集/文档打包可以减少读写成本和部署时间,并通过减少获取次数来提高缓存和执行速度。

搜索

搜索术语具有关联的模式。有三种模式以不同的方式匹配文档

模式 文档匹配条件
需要 具有此模式的全部术语。
包含 至少有一个此模式的术语。
排除 没有此模式的任何术语。

例如,包含术语 abcde 的文档将匹配查询 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

Linux | macOS | Windows

构建 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