For augmenting existing models in PostgreSQL database with vector search, Langchain supports using Prisma together with PostgreSQL and pgvector Postgres extension.

Setup

Setup database instance with Supabase

Refer to the Prisma and Supabase integration guide to setup a new database instance with Supabase and Prisma.

Install Prisma

npm
npm install prisma

Setup pgvector self hosted instance with docker-compose

pgvector provides a prebuilt Docker image that can be used to quickly setup a self-hosted Postgres instance.
services:
  db:
    image: ankane/pgvector
    ports:
      - 5432:5432
    volumes:
      - db:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=
      - POSTGRES_USER=
      - POSTGRES_DB=

volumes:
  db:

Create a new schema

Assuming you haven’t created a schema yet, create a new model with a vector field of type Unsupported("vector"):
model Document {
  id      String                 @id @default(cuid())
  content String
  vector  Unsupported("vector")?
}
Afterwards, create a new migration with --create-only to avoid running the migration directly.
npm
npx prisma migrate dev --create-only
Add the following line to the newly created migration to enable pgvector extension if it hasn’t been enabled yet:
CREATE EXTENSION IF NOT EXISTS vector;
Run the migration afterwards:
npm
npx prisma migrate dev

Usage

npm
npm install @langchain/openai @langchain/community @langchain/core
Table names and column names (in fields such as tableName, vectorColumnName, columns and filter) are passed into SQL queries directly without parametrisation.These fields must be sanitized beforehand to avoid SQL injection.
import { PrismaVectorStore } from "@langchain/community/vectorstores/prisma";
import { OpenAIEmbeddings } from "@langchain/openai";
import { PrismaClient, Prisma, Document } from "@prisma/client";

export const run = async () => {
  const db = new PrismaClient();

  // Use the `withModel` method to get proper type hints for `metadata` field:
  const vectorStore = PrismaVectorStore.withModel<Document>(db).create(
    new OpenAIEmbeddings(),
    {
      prisma: Prisma,
      tableName: "Document",
      vectorColumnName: "vector",
      columns: {
        id: PrismaVectorStore.IdColumn,
        content: PrismaVectorStore.ContentColumn,
      },
    }
  );

  const texts = ["Hello world", "Bye bye", "What's this?"];
  await vectorStore.addModels(
    await db.$transaction(
      texts.map((content) => db.document.create({ data: { content } }))
    )
  );

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

  // create an instance with default filter
  const vectorStore2 = PrismaVectorStore.withModel<Document>(db).create(
    new OpenAIEmbeddings(),
    {
      prisma: Prisma,
      tableName: "Document",
      vectorColumnName: "vector",
      columns: {
        id: PrismaVectorStore.IdColumn,
        content: PrismaVectorStore.ContentColumn,
      },
      filter: {
        content: {
          equals: "default",
        },
      },
    }
  );

  await vectorStore2.addModels(
    await db.$transaction(
      texts.map((content) => db.document.create({ data: { content } }))
    )
  );

  // Use the default filter a.k.a {"content": "default"}
  const resultTwo = await vectorStore.similaritySearch("Hello world", 1);
  console.log(resultTwo);
};
The following SQL operators are available as filters: equals, in, isNull, isNotNull, like, lt, lte, gt, gte, not. The samples above uses the following schema:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Document {
  id        String                 @id @default(cuid())
  content   String
  namespace String?                @default("default")
  vector    Unsupported("vector")?
}
You can remove namespace if you don’t need it.