Skip to main content

Supabase

Langchain支持使用Supabase Postgres数据库作为向量存储使用'pgvector' postgres扩展。 有关更多信息,请参阅Supabase博客文章

设置

安装库

npm install -S @supabase/supabase-js

在您的数据库中创建表和搜索功能

在您的数据库中运行此命令:

-- Enable the pgvector extension to work with embedding vectors

create extension vector;



-- Create a table to store your documents

create table documents (

id bigserial primary key,

content text, -- corresponds to Document.pageContent

metadata jsonb, -- corresponds to Document.metadata

embedding vector(1536) -- 1536 works for OpenAI embeddings, change if needed

);



-- Create a function to search for documents

create function match_documents (

query_embedding vector(1536),

match_count int DEFAULT null,

filter jsonb DEFAULT '{}'

) returns table (

id bigint,

content text,

metadata jsonb,

similarity float

)

language plpgsql

as $$

#variable_conflict use_column

begin

return query

select

id,

content,

metadata,

1 - (documents.embedding <=> query_embedding) as similarity

from documents

where metadata @> filter

order by documents.embedding <=> query_embedding

limit match_count;

end;

$$;

使用

标准用法

下面的示例显示如何使用Supabase执行基本相似性搜索:

import { SupabaseVectorStore } from "langchain/vectorstores/supabase";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { createClient } from "@supabase/supabase-js";

// First, follow set-up instructions at
// https://js.langchain.com/docs/modules/indexes/vector_stores/integrations/supabase

const privateKey = process.env.SUPABASE_PRIVATE_KEY;
if (!privateKey) throw new Error(`Expected env var SUPABASE_PRIVATE_KEY`);

const url = process.env.SUPABASE_URL;
if (!url) throw new Error(`Expected env var SUPABASE_URL`);

export const run = async () => {
const client = createClient(url, privateKey);

const vectorStore = await SupabaseVectorStore.fromTexts(
["Hello world", "Bye bye", "What's this?"],
[{ id: 2 }, { id: 1 }, { id: 3 }],
new OpenAIEmbeddings(),
{
client,
tableName: "documents",
queryName: "match_documents",
}
);

const resultOne = await vectorStore.similaritySearch("Hello world", 1);

console.log(resultOne);
};

元数据过滤

对于上述的'match_documents' Postgres函数,您还可以传递一个过滤参数,以仅使用特定元数据字段值的文档。该过滤器参数是一个JSON对象,'match_documents'函数将使用Postgres JSONB包含操作符'@>'根据您指定的元数据字段值过滤文档。有关更多信息,请参见Postgres JSONB包含操作符

注意: 如果您之前使用过`SupabaseVectorStore',您可能需要根据上述更新的SQL放弃和重新创建'match_documents'函数以使用此功能。

import { SupabaseVectorStore } from "langchain/vectorstores/supabase";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { createClient } from "@supabase/supabase-js";

// First, follow set-up instructions at
// https://js.langchain.com/docs/modules/indexes/vector_stores/integrations/supabase

const privateKey = process.env.SUPABASE_PRIVATE_KEY;
if (!privateKey) throw new Error(`Expected env var SUPABASE_PRIVATE_KEY`);

const url = process.env.SUPABASE_URL;
if (!url) throw new Error(`Expected env var SUPABASE_URL`);

export const run = async () => {
const client = createClient(url, privateKey);

const vectorStore = await SupabaseVectorStore.fromTexts(
["Hello world", "Hello world", "Hello world"],
[{ user_id: 2 }, { user_id: 1 }, { user_id: 3 }],
new OpenAIEmbeddings(),
{
client,
tableName: "documents",
queryName: "match_documents",
}
);

const result = await vectorStore.similaritySearch("Hello world", 1, {
user_id: 3,
});

console.log(result);
};

元数据查询构建器过滤

You can also use query builder-style filtering similar to how the Supabase JavaScript library works instead of passing an object. Note that since most of the filter properties are in the metadata column, you need to use arrow operators (-> for integer or ->> for text) as defined in Postgrest API documentation and specify the data type of the property (e.g. the column should look something like metadata->some_int_value::int).

import {
SupabaseFilterRPCCall,
SupabaseVectorStore,
} from "langchain/vectorstores/supabase";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { createClient } from "@supabase/supabase-js";

// First, follow set-up instructions at
// https://js.langchain.com/docs/modules/indexes/vector_stores/integrations/supabase

const privateKey = process.env.SUPABASE_PRIVATE_KEY;
if (!privateKey) throw new Error(`Expected env var SUPABASE_PRIVATE_KEY`);

const url = process.env.SUPABASE_URL;
if (!url) throw new Error(`Expected env var SUPABASE_URL`);

export const run = async () => {
const client = createClient(url, privateKey);

const embeddings = new OpenAIEmbeddings();

const store = new SupabaseVectorStore(embeddings, {
client,
tableName: "documents",
});

const docs = [
{
pageContent:
"This is a long text, but it actually means something because vector database does not understand Lorem Ipsum. So I would need to expand upon the notion of quantum fluff, a theorectical concept where subatomic particles coalesce to form transient multidimensional spaces. Yet, this abstraction holds no real-world application or comprehensible meaning, reflecting a cosmic puzzle.",
metadata: { b: 1, c: 10, stuff: "right" },
},
{
pageContent:
"This is a long text, but it actually means something because vector database does not understand Lorem Ipsum. So I would need to proceed by discussing the echo of virtual tweets in the binary corridors of the digital universe. Each tweet, like a pixelated canary, hums in an unseen frequency, a fascinatingly perplexing phenomenon that, while conjuring vivid imagery, lacks any concrete implication or real-world relevance, portraying a paradox of multidimensional spaces in the age of cyber folklore.",
metadata: { b: 2, c: 9, stuff: "right" },
},
{ pageContent: "hello", metadata: { b: 1, c: 9, stuff: "right" } },
{ pageContent: "hello", metadata: { b: 1, c: 9, stuff: "wrong" } },
{ pageContent: "hi", metadata: { b: 2, c: 8, stuff: "right" } },
{ pageContent: "bye", metadata: { b: 3, c: 7, stuff: "right" } },
{ pageContent: "what's this", metadata: { b: 4, c: 6, stuff: "right" } },
];

await store.addDocuments(docs);

const funcFilterA: SupabaseFilterRPCCall = (rpc) =>
rpc
.filter("metadata->b::int", "lt", 3)
.filter("metadata->c::int", "gt", 7)
.textSearch("content", `'multidimensional' & 'spaces'`, {
config: "english",
});

const resultA = await store.similaritySearch("quantum", 4, funcFilterA);

const funcFilterB: SupabaseFilterRPCCall = (rpc) =>
rpc
.filter("metadata->b::int", "lt", 3)
.filter("metadata->c::int", "gt", 7)
.filter("metadata->>stuff", "eq", "right");

const resultB = await store.similaritySearch("hello", 2, funcFilterB);

console.log(resultA, resultB);
};