Skip to content
Edge Functions

语义搜索

Semantic Search with pgvector and Supabase Edge Functions

语义搜索 解释用户查询背后的含义,而不是仅仅关注关键词。它使用机器学习来捕捉查询背后的意图和上下文,处理语言细微差别,比如同义词、表述变化和词语关系。

自从 Supabase Edge Runtime v1.36.0 起,你可以在 Supabase Edge Functions 中原生运行 gte-small 模型,无需任何外部依赖!这样你就可以生成文本嵌入,而无需调用任何外部 API!

🌐 Since Supabase Edge Runtime v1.36.0 you can run the gte-small model natively within Supabase Edge Functions without any external dependencies! This allows you to generate text embeddings without calling any external APIs!

在本教程中,你将实现三个部分:

🌐 In this tutorial you're implementing three parts:

  1. 一个 generate-embedding 数据库 webhook 边缘函数,当在 public.embeddings 表中添加(或更新)内容行时生成嵌入。
  2. 一个 query_embeddings Postgres 函数,它允许我们通过 远程过程调用 (RPC) 从 Edge Function 执行相似性搜索。
  3. 一个 search 边缘函数,它用于生成搜索词的嵌入,通过 RPC 函数调用执行相似度搜索,并返回结果。

你可以在 GitHub 找到完整的示例代码

🌐 You can find the complete example code on GitHub

创建数据库表和网络钩子 #

🌐 Create the database table and webhook

给定下列表定义

🌐 Given the following table definition:

1
create extension if not exists vector with schema extensions;
2
3
create table embeddings (
4
id bigint primary key generated always as identity,
5
content text not null,
6
embedding extensions.vector (384)
7
);
8
alter table embeddings enable row level security;
9
10
create index on embeddings using hnsw (embedding vector_ip_ops);

你可以将以下边缘函数部署为数据库 webhook,以生成插入表中的任何文本内容的嵌入:

🌐 You can deploy the following edge function as a database webhook to generate the embeddings for any text content inserted into the table:

1
import { withSupabase } from 'npm:@supabase/server@^1'
2
3
const model = new Supabase.ai.Session('gte-small')
4
5
// Triggered by a Database Webhook, which authenticates with a secret key.
6
// Deploy with `verify_jwt = false`.
7
export default {
8
fetch: withSupabase({ auth: 'secret' }, async (req, ctx) => {
9
const payload: WebhookPayload = await req.json()
10
const { content, id } = payload.record
11
12
// Generate embedding.
13
const embedding = await model.run(content, {
14
mean_pool: true,
15
normalize: true,
16
})
17
18
// Store in database.
19
const { error } = await ctx.supabaseAdmin
20
.from('embeddings')
21
.update({ embedding: JSON.stringify(embedding) })
22
.eq('id', id)
23
if (error) console.warn(error.message)
24
25
return Response.json({ ok: true })
26
}),
27
}

创建一个数据库函数和远程过程调用 #

🌐 Create a Database Function and RPC

现在你的嵌入已经存储在 Postgres 数据库表中,你可以通过 Supabase Edge Functions 使用 远程过程调用 (RPC) 来查询它们。

🌐 With the embeddings now stored in your Postgres database table, you can query them from Supabase Edge Functions by using Remote Procedure Calls (RPC).

给定以下 Postgres 函数:

🌐 Given the following Postgres Function:

1
-- Matches document sections using vector similarity search on embeddings
2
--
3
-- Returns a setof embeddings so that we can use PostgREST resource embeddings (joins with other tables)
4
-- Additional filtering like limits can be chained to this function call
5
create or replace function query_embeddings(embedding extensions.vector(384), match_threshold float)
6
returns setof embeddings
7
language plpgsql
8
as $$
9
begin
10
return query
11
select *
12
from embeddings
13
14
-- The inner product is negative, so we negate match_threshold
15
where embeddings.embedding <#> embedding < -match_threshold
16
17
-- Our embeddings are normalized to length 1, so cosine similarity
18
-- and inner product will produce the same query results.
19
-- Using inner product which can be computed faster.
20
--
21
-- For the different distance functions, see https://github.com/pgvector/pgvector
22
order by embeddings.embedding <#> embedding;
23
end;
24
$$;

在 Supabase Edge Functions 中查询向量 #

🌐 Query vectors in Supabase Edge Functions

你可以先用 supabase-js 生成搜索词的嵌入,然后调用 Postgres 函数从你存储的嵌入中找到相关结果,直接在你的 Supabase Edge Function 中操作:

🌐 You can use supabase-js to first generate the embedding for the search term and then invoke the Postgres function to find the relevant results from your stored embeddings, right from your Supabase Edge Function:

1
import { withSupabase } from 'npm:@supabase/server@^1'
2
3
const model = new Supabase.ai.Session('gte-small')
4
5
export default {
6
fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
7
const { search } = await req.json()
8
if (!search) return Response.json({ error: 'Please provide a search param!' }, { status: 400 })
9
// Generate embedding for search term.
10
const embedding = await model.run(search, {
11
mean_pool: true,
12
normalize: true,
13
})
14
15
// Query embeddings.
16
const { data: result, error } = await ctx.supabase
17
.rpc('query_embeddings', {
18
embedding,
19
match_threshold: 0.8,
20
})
21
.select('content')
22
.limit(3)
23
if (error) {
24
return Response.json({ error: error.message }, { status: 500 })
25
}
26
27
return Response.json({ search, result })
28
}),
29
}

你现在已经搭建好了由 AI 驱动的语义搜索,而且完全不依赖外部服务!你只需要你自己、pgvector 和 Supabase Edge Functions 就行了!

🌐 You now have AI powered semantic search set up without any external dependencies! All you need: you, pgvector, and Supabase Edge Functions!