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:

create extension if not exists vector with schema extensions;
create table embeddings (
id bigint primary key generated always as identity,
content text not null,
embedding extensions.vector (384)
);
alter table embeddings enable row level security;
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:

import { withSupabase } from 'npm:@supabase/server@^1'
const model = new Supabase.ai.Session('gte-small')
// Triggered by a Database Webhook, which authenticates with a secret key.
// Deploy with `verify_jwt = false`.
export default {
fetch: withSupabase({ auth: 'secret' }, async (req, ctx) => {
const payload: WebhookPayload = await req.json()
const { content, id } = payload.record
// Generate embedding.
const embedding = await model.run(content, {
mean_pool: true,
normalize: true,
})
// Store in database.
const { error } = await ctx.supabaseAdmin
.from('embeddings')
.update({ embedding: JSON.stringify(embedding) })
.eq('id', id)
if (error) console.warn(error.message)
return Response.json({ ok: true })
}),
}

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

🌐 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:

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

在 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:

import { withSupabase } from 'npm:@supabase/server@^1'
const model = new Supabase.ai.Session('gte-small')
export default {
fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
const { search } = await req.json()
if (!search) return Response.json({ error: 'Please provide a search param!' }, { status: 400 })
// Generate embedding for search term.
const embedding = await model.run(search, {
mean_pool: true,
normalize: true,
})
// Query embeddings.
const { data: result, error } = await ctx.supabase
.rpc('query_embeddings', {
embedding,
match_threshold: 0.8,
})
.select('content')
.limit(3)
if (error) {
return Response.json({ error: error.message }, { status: 500 })
}
return Response.json({ search, result })
}),
}

你现在已经搭建好了由 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!