diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
index c621ec05368..0ae444aef33 100644
--- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
+++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
@@ -873,6 +873,7 @@ export const ai: NavMenuConstant = {
items: [
{ name: 'Managing collections', url: '/guides/ai/managing-collections' },
{ name: 'Managing indexes', url: '/guides/ai/managing-indexes' },
+ { name: 'Vector columns', url: '/guides/ai/vector-columns' },
{ name: 'Engineering for scale', url: '/guides/ai/engineering-for-scale' },
],
},
diff --git a/apps/docs/pages/guides/ai/managing-collections.mdx b/apps/docs/pages/guides/ai/managing-collections.mdx
index c5d90811391..423489169bb 100644
--- a/apps/docs/pages/guides/ai/managing-collections.mdx
+++ b/apps/docs/pages/guides/ai/managing-collections.mdx
@@ -7,9 +7,11 @@ export const meta = {
sidebar_label: 'Managing collections',
}
-A collection is an group of vector records. Records can be added to or updated in a collection. Collections can be queried at any time, but should be indexed for scalable query performance.
+A collection is an group of vector records managed by the `vecs` Python library. Records can be added to or updated in a collection. Collections can be queried at any time, but should be indexed for scalable query performance.
-Supabase provides a [Python client](/docs/guides/ai/vecs-python-client) called `vecs` for managing unstructured vector stores in Postgres. If you come from a data science background, this unstructured data approach will feel familiar. If you are more interested in a structured data approach, see our guide on [Structured & Unstructured Embeddings](/docs/guides/ai/structured-unstructured-embeddings).
+Supabase provides a [Python client](/docs/guides/ai/vecs-python-client) called `vecs` for managing unstructured vector stores in Postgres. If you come from a data science background, this unstructured data approach will feel familiar. If you are more interested in a structured data approach, see [Vector columns](/docs/guides/ai/vector-columns) or read our guide on [Structured & Unstructured Embeddings](/docs/guides/ai/structured-unstructured-embeddings).
+
+Under the hood `vecs` will manage the necessary Postgres tables and columns to store and query your collections.
## API
diff --git a/apps/docs/pages/guides/ai/vector-columns.mdx b/apps/docs/pages/guides/ai/vector-columns.mdx
new file mode 100644
index 00000000000..5e2c25f9a16
--- /dev/null
+++ b/apps/docs/pages/guides/ai/vector-columns.mdx
@@ -0,0 +1,153 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'ai-vector-columns',
+ title: 'Vector columns',
+ description: 'TBD',
+ sidebar_label: 'Vector columns',
+}
+
+Supabase offers a number of different ways to store and query vectors within Postgres. If you prefer to use Python to store and query your vectors using collections, see [Managing collections](/docs/guides/ai/managing-collections). If you want more control over vectors within your own Postgres tables or would like to interact with them using a different language like JavaScript, keep reading.
+
+Vectors in Supabase are enabled via [pgvector](https://github.com/pgvector/pgvector/), a PostgreSQL extension for storing and querying vectors in Postgres. It can be used to store [embeddings](/docs/guides/ai/concepts#what-are-embeddings).
+
+## Usage
+
+### Enable the extension
+
+
+
+
+1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard.
+2. Click on **Extensions** in the sidebar.
+3. Search for "vector" and enable the extension.
+
+
+
+
+```sql
+ -- Example: enable the "vector" extension.
+create extension vector
+with
+ schema extensions;
+
+-- Example: disable the "vector" extension
+drop
+ extension if exists vector;
+```
+
+Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension".
+To disable an extension, call `drop extension`.
+
+
+
+
+### Create a table to store vectors
+
+After enabling the `vector` extension, you will get access to a new data type called `vector`. The size of the vector (indicated in parenthesis) represents the number of dimensions stored in that vector.
+
+```sql
+create table documents (
+ id serial primary key,
+ title text not null,
+ body text not null,
+ embedding vector(1536)
+);
+```
+
+In the above SQL snippet, we create a `documents` table with a column called `embedding` (note this is just a regular Postgres column - you can name it whatever you like). We give the `embedding` column a `vector` data type with 1536 dimensions. Change this to the number of dimensions used in your vector application. For example, if you are generating embeddings using OpenAI's `text-embeddings-ada-002` model, you would set this number as 1536 since that model produces 1536 dimensions.
+
+### Storing a vector / embedding
+
+In this example we'll generate a vector using the OpenAI API client, then store it in the database using the Supabase JavaScript client.
+
+```js
+const title = 'First post!'
+const body = 'Hello world!'
+
+// Generate a vector using OpenAI
+const embeddingResponse = await openai.createEmbedding({
+ model: 'text-embedding-ada-002',
+ input: body,
+})
+
+const [{ embedding }] = embeddingResponse.data.data
+
+// Store the vector in Postgres
+const { data, error } = await supabase.from('documents').insert({
+ title,
+ body,
+ embedding,
+})
+```
+
+This example uses the JavaScript Supabase client, but you can modify it to work with any [supported language library](/docs#client-libraries).
+
+### Querying a vector / embedding
+
+Similarity search is the most common use case for vectors. `pgvector` support 3 new operators for performing similarity search:
+
+| Operator | Description |
+| -------- | ---------------------- |
+| `<->` | Euclidean distance |
+| `<#>` | negative inner product |
+| `<=>` | cosine distance |
+
+Choosing the right operator depends on your needs. If you are searching over OpenAI embeddings, OpenAI recommends using cosine simliarity. For more information on how embeddings work and how they relate to each other, see [What are Embeddings?](/docs/guides/ai/concepts#what-are-embeddings).
+
+Supabase client libraries like `supabase-js` connect to your Postgres instance via [PostgREST](docs/guides/getting-started/architecture#postgrest-api). PostgREST does not currently support `pgvector` similarity operators, so we'll need to wrap our query in a Postgres function and call it via the `rpc()` method:
+
+```sql
+create or replace function match_documents (
+ query_embedding vector(1536),
+ match_threshold float,
+ match_count int
+)
+returns table (
+ id bigint,
+ content text,
+ similarity float
+)
+language sql stable
+as $$
+ select
+ documents.id,
+ documents.content,
+ 1 - (documents.embedding <=> query_embedding) as similarity
+ from documents
+ where 1 - (documents.embedding <=> query_embedding) > match_threshold
+ order by similarity desc
+ limit match_count;
+$$;
+```
+
+This function takes a `query_embedding` argument and compares it to all other embeddings in the `documents` table. Each comparison returns a similarity score. If the similarity is greater than the `match_threshold` argument, it is returned. The number of rows returned is limited by the `match_count` argument.
+
+Feel free to modify this method to fit the needs of your application. The `match_threhold` ensures that only documents that have a minimum similarity to the `query_embedding` are returned. Without this, you may end up returning documents that subjectively don't match. This value will vary for each application - you will need to perform your own testing to determine the threshold that makes sense for your app.
+
+To execute the function from your client library, call `rpc()` with the name of your Postgres function:
+
+```ts
+const { data: documents } = await supabaseClient.rpc('match_documents', {
+ query_embedding: embedding, // Pass the embedding you want to compare
+ match_threshold: 0.78, // Choose an appropriate threshold for your data
+ match_count: 10, // Choose the number of matches
+})
+```
+
+In this example `embedding` would be another embedding you wish to compare against your table of pre-generated embedding documents. For example if you were building a search engine, every time the user submits their query you would first generate an embedding on the search query itself (using `openai.createEmbedding()`), then pass it into the above `rpc()` function to match.
+
+Vectors and embedding can be used for much more than search. Learn more about embeddings at [What are Embeddings?](/docs/guides/ai/concepts#what-are-embeddings).
+
+### Indexes
+
+Once your vector table starts to grow, you will likely want to add an index to speed up queries. See [Managing indexes](/docs/guides/ai/managing-indexes) to learn how vector indexes work and how to create them.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/extensions/pgvector.mdx b/apps/docs/pages/guides/database/extensions/pgvector.mdx
index c3377b688ad..e9ce3274f8d 100644
--- a/apps/docs/pages/guides/database/extensions/pgvector.mdx
+++ b/apps/docs/pages/guides/database/extensions/pgvector.mdx
@@ -83,13 +83,14 @@ const embeddingResponse = await openai.createEmbedding({
model: 'text-embedding-ada-002',
input: body,
})
-const [responseData] = embeddingResponse.data.data.
+
+const [{ embedding }] = embeddingResponse.data.data
// Store the vector in Postgres
const { data, error } = await supabase.from('posts').insert({
title,
body,
- embedding: responseData.embedding,
+ embedding,
})
```