基于 Supabase 打造超轻量级搜索引擎 | 与 AI 结伴自学(捌)
2025-03-29 21:30
Leeduckgo
2025-03-29 21:30
订阅此专栏
收藏此文章
Coze AI Agent 极速入门 | 与 AI 结伴自学(柒)

向量检索:被低估的检索算法 | 与 AI 结伴自学(陆)

去中心化推荐系统 | 与 AI 结伴自学(伍)

和 Aptos 白皮书对话 | 与 AI 结伴自学(肆)

向量数据库之基本概念 | 与 AI 结伴自学(三)

建立关于一门学科的知识树 | 与 AI 结伴自学(二)

前言 | 与 AI 结伴自学(一)


这是一个阐述如何基于 Supabase、Deno 和 Scaffold-ETH (React) 构建超轻量级 AI 友好的搜索引擎的新系列。

0x1 搜索引擎架构

相较于传统的搜索引擎算法,我们使用了更轻量级的方式来构建我们的搜索引擎:

React Frontend - Deno Function - textSearch of Supabase
                                          | Extensions
                                       pgroonga

https://supabase.com/docs/guides/database/full-text-search?queryGroups=language&language=js&queryGroups=example-view&example-view=data#search-using-the-new-column

https://supabase.com/docs/guides/database/extensions/pgroonga?queryGroups=database-method&database-method=dashboard

0x2 安装 pgroonga 扩展

通过 Dashboard > Database > Extensions 这条路径我们可安装 pgroonga 扩展。

image-20250329144425847

PGroonga 是 Postgres 的一个扩展,它添加了基于 Groonga 的全文搜索索引方法。虽然原生 Postgres 支持全文索引,但仅限于基于字母和数字的语言。PGroonga 提供更广泛的字符支持,使其适用于 Postgres 支持的超集语言,包括日语、中文等。

0x3 用 SQL 实现搜索

3.1 创建一个示例表

我们用如下 SQL 命令创建表:

CREATE TABLE "public"."cantonese" (
    "id" INT8 NOT NULL,
    "data" TEXT NOT NULL,
    "note" JSONB,
    "category" TEXT,
    "created_at" TIMESTAMP WITH TIME ZONE,
    PRIMARY KEY ("id")
);

再用如下命令插入测试数据:

INSERT INTO "public"."cantonese" ("id""data""note""category""created_at") VALUES ('1''係''[{"context":{"pron":"hai6","work class":"v"},"contributor":"0x0"}]''dict''2025-03-17 14:00:32.192995+00'), ('2''咩''[{"context":{"pron":"me1","work class":"y"},"contributor":"0x0"}]''dict''2025-03-17 14:00:32.192995+00'), ('3''咪''[{"context":{"pron":"mai6","work class":"d"},"contributor":"0x0"}]''dict''2025-03-17 14:00:32.192995+00'), ('4''帶''[{"context":{"pron":"daai3","work class":"v"},"contributor":"0x0"}]''dict''2025-03-17 14:00:32.192995+00'), ('5''定''[{"context":{"pron":"ding6","work class":"d"},"contributor":"0x0"}]''dict''2025-03-17 14:00:32.192995+00'), ('6''幾''[{"context":{"pron":"gei2","work class":"m"},"contributor":"0x0"}]''dict''2025-03-17 14:00:32.192995+00'), ('7''個''[{"context":{"pron":"go3","work class":"q"},"contributor":"0x0"}]''dict''2025-03-17 14:00:32.192995+00'), ('8''喼''[{"context":{"pron":"gip1","work class":"n"},"contributor":"0x0"}]''dict''2025-03-17 14:00:32.192995+00'), ('9''去''[{"context":{"pron":"heoi3","work class":"v"},"contributor":"0x0"}]''dict''2025-03-17 14:00:32.192995+00');

通过 Dashboard > SQL Editor > Create a new snippet,我们打开 SQL Editor,来执行 SQL 语句。


执行成功后,我们可以在  Dashboard > Table Editor 中查看到生成的表。



image-20250329151014612

3.2 生成索引

同样是通过 SQL Editor

create index [table_name]_data_pgroonga_idx on [table_name] using pgroonga(data);

在执行结束后,我们可以在  Database > Index 中查看到被创建的索引:



image-20250329191711099

3.3 通过 SQL 进行搜索

SQL Editor

select
  * 
  from cantonese_corpus
  where data &@~ '定';

image-20250329192026208

🤔 思考题:如何将这个 sql 语句改造成一个 SQL 函数?

💡 Tips:在  Dashboard > Database > Functions 中可以查看到所有函数

🤔 思考题 2:&@~是什么含义?

这两道思考题都可以在评论区留言哦,未来有抽奖~ 🎁

0x4 在 deno 中实现搜索

我们在 deno 中进行搜索实现,从而实现超轻量级后端。

4.1 源码

https://github.com/NonceGeek/bodhi-searcher/blob/main/deno/bodhi_data_getter.tsx#L361

.get("/text_search_v2"async (context) => { 
   // 从 url 中读取参数
    const queryParams = context.request.url.searchParams;
    const key = queryParams.get("keyword");
    const tableName = queryParams.get("table_name");
    const column = queryParams.get("column");
    const limit = parseInt(queryParams.get("limit"), 10); // Get limit from query and convert to integer
    const supabase_url = queryParams.get("supabase_url") || Deno.env.get("SUPABASE_URL");
    // TODO: make SUPABASE_SERVICE_ROLE_KEY for a spec table as a param.
   // 从环境变量中拿到 key
    const supabase = createClient(
      supabase_url,
      Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? ""
    );
    console.log("supabase_url", supabase_url);
   // 调用 textSearch 函数进行检索
    try {
      let query = supabase
        .from(tableName)
        .select("*"// Select all columns initially
        .textSearch(column, key)
        .order("id", { ascending: false }); // Order results by id_on_chain in descending order

      if (!isNaN(limit) && limit > 0) {
        query = query.limit(limit); // Apply limit to the query if valid
      }

      const { data, error } = await query;
   // 结果返回
      console.log("data", data);
      context.response.status = 200;
      context.response.body = data;
      if (error) {
        throw error;
      }
    } catch (error) { 
      // 错误处理
      console.error("Error fetching data:", error);
      context.response.status = 500;
      context.response.body = { error: "Failed to fetch data" };
    }
  })

💡其他语言使用者可以帮助文档中查看到相应语句。

4.2 本地启动服务

deno run bodhi_data_getter.tsx


image-20250329203002864

我们可以通过 curl 的方式来访问,也可以在浏览器里直接输入 url,需要注意的是,前一种方式下,汉字需要转码。

curl -X GET "http://localhost:8000/text_search_v2?keyword=%E5%AE%9A&table_name=cantonese_corpus_all&column=data&limit=10"

http://localhost:8000/text_search_v2?keyword=%E5%AE%9A&table_name=cantonese_corpus_all&column=data&limit=10



image-20250329202756466

4.3 部署

在本地调试成功后,我们通过 deployctl 工具进行部署:

deployctl deploy --prod --project=bodhi-data ./bodhi_data_getter.tsx


image-20250329203244947

然后我们就可以通过线上地址对 api 进行访问了!

https://bodhi-data.deno.dev/text_search_v2?keyword=%E5%AE%9A&table_name=cantonese_corpus_all&column=data&limit=10



image-20250329203408201

┓( ´∀` )┏ 最后是预告!在下一篇中,我们将对描述如何进行前端实现。


【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

Leeduckgo
数据请求中
查看更多

推荐专栏

数据请求中
在 App 打开