import 'dotenv/config'; import Docker from 'dockerode'; import { desc, DrizzleError, eq, gt, gte, or, placeholder, sql, TransactionRollbackError } from 'drizzle-orm'; import { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js'; import getPort from 'get-port'; import postgres from 'postgres'; import { v4 as uuid } from 'uuid'; import { afterAll, beforeAll, beforeEach, expect, expectTypeOf, test } from 'vitest'; import * as schema from './pg.schema.ts'; const ENABLE_LOGGING = false; const { usersTable, postsTable, commentsTable, usersToGroupsTable, groupsTable, usersV1, usersTableV1 } = schema; /* Test cases: - querying nested relation without PK with additional fields */ declare module 'vitest' { export interface TestContext { docker: Docker; pgContainer: Docker.Container; pgjsDb: PostgresJsDatabase; pgjsClient: postgres.Sql<{}>; } } let globalDocker: Docker; let pgContainer: Docker.Container; let db: PostgresJsDatabase; let client: postgres.Sql<{}>; async function createDockerDB(): Promise { const docker = (globalDocker = new Docker()); const port = await getPort({ port: 5432 }); const image = 'postgres:14'; const pullStream = await docker.pull(image); await new Promise((resolve, reject) => docker.modem.followProgress(pullStream, (err) => err ? reject(err) : resolve(err)) ); pgContainer = await docker.createContainer({ Image: image, Env: [ 'POSTGRES_PASSWORD=postgres', 'POSTGRES_USER=postgres', 'POSTGRES_DB=postgres', ], name: `drizzle-integration-tests-${uuid()}`, HostConfig: { AutoRemove: true, PortBindings: { '5432/tcp': [{ HostPort: `${port}` }], }, }, }); await pgContainer.start(); return `postgres://postgres:postgres@localhost:${port}/postgres`; } beforeAll(async () => { const connectionString = process.env['PG_CONNECTION_STRING'] ?? (await createDockerDB()); const sleep = 250; let timeLeft = 5000; let connected = false; let lastError: unknown | undefined; do { try { client = postgres(connectionString, { max: 1, onnotice: () => { // disable notices }, }); await client`select 1`; connected = true; break; } catch (e) { lastError = e; await new Promise((resolve) => setTimeout(resolve, sleep)); timeLeft -= sleep; } } while (timeLeft > 0); if (!connected) { console.error('Cannot connect to Postgres'); await client?.end().catch(console.error); await pgContainer?.stop().catch(console.error); throw lastError; } db = drizzle(client, { schema, logger: ENABLE_LOGGING }); }); afterAll(async () => { await client?.end().catch(console.error); await pgContainer?.stop().catch(console.error); }); beforeEach(async (ctx) => { ctx.pgjsDb = db; ctx.pgjsClient = client; ctx.docker = globalDocker; ctx.pgContainer = pgContainer; await ctx.pgjsDb.execute(sql`drop schema public cascade`); await ctx.pgjsDb.execute(sql`drop schema if exists "schemaV1" cascade`); await ctx.pgjsDb.execute(sql`create schema public`); await ctx.pgjsDb.execute(sql`create schema "schemaV1"`); await ctx.pgjsDb.execute( sql` CREATE TABLE "users" ( "id" serial PRIMARY KEY NOT NULL, "name" text NOT NULL, "verified" boolean DEFAULT false NOT NULL, "invited_by" int REFERENCES "users"("id") ); `, ); await ctx.pgjsDb.execute( sql` CREATE TABLE "schemaV1"."usersV1" ( "id" serial PRIMARY KEY NOT NULL, "name" text NOT NULL, "verified" boolean DEFAULT false NOT NULL, "invited_by" int ); `, ); await ctx.pgjsDb.execute( sql` CREATE TABLE "schemaV1"."users_table_V1" ( "id" serial PRIMARY KEY NOT NULL, "name" text NOT NULL, "verified" boolean DEFAULT false NOT NULL, "invited_by" int ); `, ); await ctx.pgjsDb.execute( sql` CREATE TABLE IF NOT EXISTS "groups" ( "id" serial PRIMARY KEY NOT NULL, "name" text NOT NULL, "description" text ); `, ); await ctx.pgjsDb.execute( sql` CREATE TABLE IF NOT EXISTS "users_to_groups" ( "id" serial PRIMARY KEY NOT NULL, "user_id" int REFERENCES "users"("id"), "group_id" int REFERENCES "groups"("id") ); `, ); await ctx.pgjsDb.execute( sql` CREATE TABLE IF NOT EXISTS "posts" ( "id" serial PRIMARY KEY NOT NULL, "content" text NOT NULL, "owner_id" int REFERENCES "users"("id"), "created_at" timestamp with time zone DEFAULT now() NOT NULL ); `, ); await ctx.pgjsDb.execute( sql` CREATE TABLE IF NOT EXISTS "comments" ( "id" serial PRIMARY KEY NOT NULL, "content" text NOT NULL, "creator" int REFERENCES "users"("id"), "post_id" int REFERENCES "posts"("id"), "created_at" timestamp with time zone DEFAULT now() NOT NULL ); `, ); await ctx.pgjsDb.execute( sql` CREATE TABLE IF NOT EXISTS "comment_likes" ( "id" serial PRIMARY KEY NOT NULL, "creator" int REFERENCES "users"("id"), "comment_id" int REFERENCES "comments"("id"), "created_at" timestamp with time zone DEFAULT now() NOT NULL ); `, ); }); /* [Find Many] One relation users+posts */ test('[Find Many] Get users with posts', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ with: { posts: true, }, }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); usersWithPosts.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(usersWithPosts.length).eq(3); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[1]?.posts.length).eq(1); expect(usersWithPosts[2]?.posts.length).eq(1); expect(usersWithPosts[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); expect(usersWithPosts[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, posts: [{ id: 2, ownerId: 2, content: 'Post2', createdAt: usersWithPosts[1]?.posts[0]?.createdAt }], }); expect(usersWithPosts[2]).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, posts: [{ id: 3, ownerId: 3, content: 'Post3', createdAt: usersWithPosts[2]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts + limit posts', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ with: { posts: { limit: 1, }, }, }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); usersWithPosts.sort((a, b) => (a.id > b.id) ? 1 : -1); usersWithPosts[0]?.posts.sort((a, b) => (a.id > b.id) ? 1 : -1); usersWithPosts[1]?.posts.sort((a, b) => (a.id > b.id) ? 1 : -1); usersWithPosts[2]?.posts.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(usersWithPosts.length).eq(3); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[1]?.posts.length).eq(1); expect(usersWithPosts[2]?.posts.length).eq(1); expect(usersWithPosts[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); expect(usersWithPosts[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, posts: [{ id: 4, ownerId: 2, content: 'Post2', createdAt: usersWithPosts[1]?.posts[0]?.createdAt }], }); expect(usersWithPosts[2]).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, posts: [{ id: 6, ownerId: 3, content: 'Post3', createdAt: usersWithPosts[2]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts + limit posts and users', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ limit: 2, with: { posts: { limit: 1, }, }, }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); usersWithPosts.sort((a, b) => (a.id > b.id) ? 1 : -1); usersWithPosts[0]?.posts.sort((a, b) => (a.id > b.id) ? 1 : -1); usersWithPosts[1]?.posts.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(usersWithPosts.length).eq(2); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[1]?.posts.length).eq(1); expect(usersWithPosts[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); expect(usersWithPosts[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, posts: [{ id: 4, ownerId: 2, content: 'Post2', createdAt: usersWithPosts[1]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts + custom fields', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ with: { posts: true, }, extras: ({ name }) => ({ lowerName: sql`lower(${name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; lowerName: string; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); usersWithPosts.sort((a, b) => (a.id > b.id) ? 1 : -1); usersWithPosts[0]?.posts.sort((a, b) => (a.id > b.id) ? 1 : -1); usersWithPosts[1]?.posts.sort((a, b) => (a.id > b.id) ? 1 : -1); usersWithPosts[2]?.posts.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(usersWithPosts.length).toEqual(3); expect(usersWithPosts[0]?.posts.length).toEqual(3); expect(usersWithPosts[1]?.posts.length).toEqual(2); expect(usersWithPosts[2]?.posts.length).toEqual(2); expect(usersWithPosts[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, lowerName: 'dan', posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }, { id: 2, ownerId: 1, content: 'Post1.2', createdAt: usersWithPosts[0]?.posts[1]?.createdAt, }, { id: 3, ownerId: 1, content: 'Post1.3', createdAt: usersWithPosts[0]?.posts[2]?.createdAt }], }); expect(usersWithPosts[1]).toEqual({ id: 2, name: 'Andrew', lowerName: 'andrew', verified: false, invitedBy: null, posts: [{ id: 4, ownerId: 2, content: 'Post2', createdAt: usersWithPosts[1]?.posts[0]?.createdAt }, { id: 5, ownerId: 2, content: 'Post2.1', createdAt: usersWithPosts[1]?.posts[1]?.createdAt, }], }); expect(usersWithPosts[2]).toEqual({ id: 3, name: 'Alex', lowerName: 'alex', verified: false, invitedBy: null, posts: [{ id: 6, ownerId: 3, content: 'Post3', createdAt: usersWithPosts[2]?.posts[0]?.createdAt }, { id: 7, ownerId: 3, content: 'Post3.1', createdAt: usersWithPosts[2]?.posts[1]?.createdAt, }], }); }); test('[Find Many] Get users with posts + custom fields + limits', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ limit: 1, with: { posts: { limit: 1, }, }, extras: (usersTable, { sql }) => ({ lowerName: sql`lower(${usersTable.name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; lowerName: string; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); expect(usersWithPosts.length).toEqual(1); expect(usersWithPosts[0]?.posts.length).toEqual(1); expect(usersWithPosts[0]).toEqual({ id: 1, name: 'Dan', lowerName: 'dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts + orderBy', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: '1' }, { ownerId: 1, content: '2' }, { ownerId: 1, content: '3' }, { ownerId: 2, content: '4' }, { ownerId: 2, content: '5' }, { ownerId: 3, content: '6' }, { ownerId: 3, content: '7' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ with: { posts: { orderBy: (postsTable, { desc }) => [desc(postsTable.content)], }, }, orderBy: (usersTable, { desc }) => [desc(usersTable.id)], }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); expect(usersWithPosts.length).eq(3); expect(usersWithPosts[0]?.posts.length).eq(2); expect(usersWithPosts[1]?.posts.length).eq(2); expect(usersWithPosts[2]?.posts.length).eq(3); expect(usersWithPosts[2]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 3, ownerId: 1, content: '3', createdAt: usersWithPosts[2]?.posts[2]?.createdAt }, { id: 2, ownerId: 1, content: '2', createdAt: usersWithPosts[2]?.posts[1]?.createdAt, }, { id: 1, ownerId: 1, content: '1', createdAt: usersWithPosts[2]?.posts[0]?.createdAt }], }); expect(usersWithPosts[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, posts: [{ id: 5, ownerId: 2, content: '5', createdAt: usersWithPosts[1]?.posts[1]?.createdAt, }, { id: 4, ownerId: 2, content: '4', createdAt: usersWithPosts[1]?.posts[0]?.createdAt }], }); expect(usersWithPosts[0]).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, posts: [{ id: 7, ownerId: 3, content: '7', createdAt: usersWithPosts[0]?.posts[1]?.createdAt, }, { id: 6, ownerId: 3, content: '6', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ where: (({ id }, { eq }) => eq(id, 1)), with: { posts: { where: (({ id }, { eq }) => eq(id, 1)), }, }, }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); expect(usersWithPosts.length).eq(1); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts + where + partial', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ columns: { id: true, name: true, }, with: { posts: { columns: { id: true, content: true, }, where: (({ id }, { eq }) => eq(id, 1)), }, }, where: (({ id }, { eq }) => eq(id, 1)), }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; posts: { id: number; content: string; }[]; }[]>(); expect(usersWithPosts.length).eq(1); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[0]).toEqual({ id: 1, name: 'Dan', posts: [{ id: 1, content: 'Post1' }], }); }); test('[Find Many] Get users with posts + where + partial. Did not select posts id, but used it in where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ columns: { id: true, name: true, }, with: { posts: { columns: { id: true, content: true, }, where: (({ id }, { eq }) => eq(id, 1)), }, }, where: (({ id }, { eq }) => eq(id, 1)), }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; posts: { id: number; content: string; }[]; }[]>(); expect(usersWithPosts.length).eq(1); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[0]).toEqual({ id: 1, name: 'Dan', posts: [{ id: 1, content: 'Post1' }], }); }); test('[Find Many] Get users with posts + where + partial(true + false)', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ columns: { id: true, name: false, }, with: { posts: { columns: { id: true, content: false, }, where: (({ id }, { eq }) => eq(id, 1)), }, }, where: (({ id }, { eq }) => eq(id, 1)), }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; posts: { id: number; }[]; }[]>(); expect(usersWithPosts.length).eq(1); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[0]).toEqual({ id: 1, posts: [{ id: 1 }], }); }); test('[Find Many] Get users with posts + where + partial(false)', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ columns: { name: false, }, with: { posts: { columns: { content: false, }, where: (({ id }, { eq }) => eq(id, 1)), }, }, where: (({ id }, { eq }) => eq(id, 1)), }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; verified: boolean; invitedBy: number | null; posts: { id: number; ownerId: number | null; createdAt: Date; }[]; }[]>(); expect(usersWithPosts.length).eq(1); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[0]).toEqual({ id: 1, verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts in transaction', async (t) => { const { pgjsDb: db } = t; let usersWithPosts: { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[] = []; await db.transaction(async (tx) => { await tx.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await tx.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); usersWithPosts = await tx.query.usersTable.findMany({ where: (({ id }, { eq }) => eq(id, 1)), with: { posts: { where: (({ id }, { eq }) => eq(id, 1)), }, }, }); }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); expect(usersWithPosts.length).eq(1); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts in rollbacked transaction', async (t) => { const { pgjsDb: db } = t; let usersWithPosts: { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[] = []; await expect(db.transaction(async (tx) => { await tx.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await tx.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); tx.rollback(); usersWithPosts = await tx.query.usersTable.findMany({ where: (({ id }, { eq }) => eq(id, 1)), with: { posts: { where: (({ id }, { eq }) => eq(id, 1)), }, }, }); })).rejects.toThrowError(new TransactionRollbackError()); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); expect(usersWithPosts.length).eq(0); }); // select only custom test('[Find Many] Get only custom fields', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ columns: {}, with: { posts: { columns: {}, extras: ({ content }) => ({ lowerName: sql`lower(${content})`.as('content_lower'), }), }, }, extras: ({ name }) => ({ lowerName: sql`lower(${name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ lowerName: string; posts: { lowerName: string; }[]; }[]>(); expect(usersWithPosts.length).toEqual(3); expect(usersWithPosts[0]?.posts.length).toEqual(3); expect(usersWithPosts[1]?.posts.length).toEqual(2); expect(usersWithPosts[2]?.posts.length).toEqual(2); expect(usersWithPosts).toContainEqual({ lowerName: 'dan', posts: [{ lowerName: 'post1' }, { lowerName: 'post1.2', }, { lowerName: 'post1.3' }], }); expect(usersWithPosts).toContainEqual({ lowerName: 'andrew', posts: [{ lowerName: 'post2' }, { lowerName: 'post2.1', }], }); expect(usersWithPosts).toContainEqual({ lowerName: 'alex', posts: [{ lowerName: 'post3' }, { lowerName: 'post3.1', }], }); }); test('[Find Many] Get only custom fields + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ columns: {}, with: { posts: { columns: {}, where: gte(postsTable.id, 2), extras: ({ content }) => ({ lowerName: sql`lower(${content})`.as('content_lower'), }), }, }, where: eq(usersTable.id, 1), extras: ({ name }) => ({ lowerName: sql`lower(${name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ lowerName: string; posts: { lowerName: string; }[]; }[]>(); expect(usersWithPosts.length).toEqual(1); expect(usersWithPosts[0]?.posts.length).toEqual(2); expect(usersWithPosts).toContainEqual({ lowerName: 'dan', posts: [{ lowerName: 'post1.2' }, { lowerName: 'post1.3' }], }); }); test('[Find Many] Get only custom fields + where + limit', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ columns: {}, with: { posts: { columns: {}, where: gte(postsTable.id, 2), limit: 1, extras: ({ content }) => ({ lowerName: sql`lower(${content})`.as('content_lower'), }), }, }, where: eq(usersTable.id, 1), extras: ({ name }) => ({ lowerName: sql`lower(${name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ lowerName: string; posts: { lowerName: string; }[]; }[]>(); expect(usersWithPosts.length).toEqual(1); expect(usersWithPosts[0]?.posts.length).toEqual(1); expect(usersWithPosts).toContainEqual({ lowerName: 'dan', posts: [{ lowerName: 'post1.2' }], }); }); test('[Find Many] Get only custom fields + where + orderBy', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findMany({ columns: {}, with: { posts: { columns: {}, where: gte(postsTable.id, 2), orderBy: [desc(postsTable.id)], extras: ({ content }) => ({ lowerName: sql`lower(${content})`.as('content_lower'), }), }, }, where: eq(usersTable.id, 1), extras: ({ name }) => ({ lowerName: sql`lower(${name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ lowerName: string; posts: { lowerName: string; }[]; }[]>(); expect(usersWithPosts.length).toEqual(1); expect(usersWithPosts[0]?.posts.length).toEqual(2); expect(usersWithPosts).toContainEqual({ lowerName: 'dan', posts: [{ lowerName: 'post1.3' }, { lowerName: 'post1.2' }], }); }); // select only custom find one test('[Find One] Get only custom fields', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ columns: {}, with: { posts: { columns: {}, extras: ({ content }) => ({ lowerName: sql`lower(${content})`.as('content_lower'), }), }, }, extras: ({ name }) => ({ lowerName: sql`lower(${name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf< { lowerName: string; posts: { lowerName: string; }[]; } | undefined >(); expect(usersWithPosts?.posts.length).toEqual(3); expect(usersWithPosts).toEqual({ lowerName: 'dan', posts: [{ lowerName: 'post1' }, { lowerName: 'post1.2', }, { lowerName: 'post1.3' }], }); }); test('[Find One] Get only custom fields + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ columns: {}, with: { posts: { columns: {}, where: gte(postsTable.id, 2), extras: ({ content }) => ({ lowerName: sql`lower(${content})`.as('content_lower'), }), }, }, where: eq(usersTable.id, 1), extras: ({ name }) => ({ lowerName: sql`lower(${name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf< { lowerName: string; posts: { lowerName: string; }[]; } | undefined >(); expect(usersWithPosts?.posts.length).toEqual(2); expect(usersWithPosts).toEqual({ lowerName: 'dan', posts: [{ lowerName: 'post1.2' }, { lowerName: 'post1.3' }], }); }); test('[Find One] Get only custom fields + where + limit', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ columns: {}, with: { posts: { columns: {}, where: gte(postsTable.id, 2), limit: 1, extras: ({ content }) => ({ lowerName: sql`lower(${content})`.as('content_lower'), }), }, }, where: eq(usersTable.id, 1), extras: ({ name }) => ({ lowerName: sql`lower(${name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf< { lowerName: string; posts: { lowerName: string; }[]; } | undefined >(); expect(usersWithPosts?.posts.length).toEqual(1); expect(usersWithPosts).toEqual({ lowerName: 'dan', posts: [{ lowerName: 'post1.2' }], }); }); test('[Find One] Get only custom fields + where + orderBy', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ columns: {}, with: { posts: { columns: {}, where: gte(postsTable.id, 2), orderBy: [desc(postsTable.id)], extras: ({ content }) => ({ lowerName: sql`lower(${content})`.as('content_lower'), }), }, }, where: eq(usersTable.id, 1), extras: ({ name }) => ({ lowerName: sql`lower(${name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf< { lowerName: string; posts: { lowerName: string; }[]; } | undefined >(); expect(usersWithPosts?.posts.length).toEqual(2); expect(usersWithPosts).toEqual({ lowerName: 'dan', posts: [{ lowerName: 'post1.3' }, { lowerName: 'post1.2' }], }); }); // columns {} test('[Find Many] Get select {}', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await expect(async () => await db.query.usersTable.findMany({ columns: {}, }) ).rejects.toThrow(DrizzleError); }); // columns {} test('[Find One] Get select {}', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await expect(async () => await db.query.usersTable.findFirst({ columns: {}, }) ).rejects.toThrow(DrizzleError); }); // deep select {} test('[Find Many] Get deep select {}', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); await expect(async () => await db.query.usersTable.findMany({ columns: {}, with: { posts: { columns: {}, }, }, }) ).rejects.toThrow(DrizzleError); }); // deep select {} test('[Find One] Get deep select {}', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); await expect(async () => await db.query.usersTable.findFirst({ columns: {}, with: { posts: { columns: {}, }, }, }) ).rejects.toThrow(DrizzleError); }); /* Prepared statements for users+posts */ test('[Find Many] Get users with posts + prepared limit', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const prepared = db.query.usersTable.findMany({ with: { posts: { limit: placeholder('limit'), }, }, }).prepare('query1'); const usersWithPosts = await prepared.execute({ limit: 1 }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); expect(usersWithPosts.length).eq(3); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[1]?.posts.length).eq(1); expect(usersWithPosts[2]?.posts.length).eq(1); expect(usersWithPosts).toContainEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); expect(usersWithPosts).toContainEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, posts: [{ id: 4, ownerId: 2, content: 'Post2', createdAt: usersWithPosts[1]?.posts[0]?.createdAt }], }); expect(usersWithPosts).toContainEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, posts: [{ id: 6, ownerId: 3, content: 'Post3', createdAt: usersWithPosts[2]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts + prepared limit + offset', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const prepared = db.query.usersTable.findMany({ limit: placeholder('uLimit'), offset: placeholder('uOffset'), with: { posts: { limit: placeholder('pLimit'), }, }, }).prepare('query2'); const usersWithPosts = await prepared.execute({ pLimit: 1, uLimit: 3, uOffset: 1 }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); expect(usersWithPosts.length).eq(2); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[1]?.posts.length).eq(1); expect(usersWithPosts).toContainEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, posts: [{ id: 4, ownerId: 2, content: 'Post2', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); expect(usersWithPosts).toContainEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, posts: [{ id: 6, ownerId: 3, content: 'Post3', createdAt: usersWithPosts[1]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts + prepared where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const prepared = db.query.usersTable.findMany({ where: (({ id }, { eq }) => eq(id, placeholder('id'))), with: { posts: { where: (({ id }, { eq }) => eq(id, 1)), }, }, }).prepare('query3'); const usersWithPosts = await prepared.execute({ id: 1 }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); expect(usersWithPosts.length).eq(1); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); }); test('[Find Many] Get users with posts + prepared + limit + offset + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const prepared = db.query.usersTable.findMany({ limit: placeholder('uLimit'), offset: placeholder('uOffset'), where: (({ id }, { eq, or }) => or(eq(id, placeholder('id')), eq(id, 3))), with: { posts: { where: (({ id }, { eq }) => eq(id, placeholder('pid'))), limit: placeholder('pLimit'), }, }, }).prepare('query4'); const usersWithPosts = await prepared.execute({ pLimit: 1, uLimit: 3, uOffset: 1, id: 2, pid: 6 }); expectTypeOf(usersWithPosts).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; }[]>(); expect(usersWithPosts.length).eq(1); expect(usersWithPosts[0]?.posts.length).eq(1); expect(usersWithPosts).toContainEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, posts: [{ id: 6, ownerId: 3, content: 'Post3', createdAt: usersWithPosts[0]?.posts[0]?.createdAt }], }); }); /* [Find One] One relation users+posts */ test('[Find One] Get users with posts', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ with: { posts: true, }, }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).eq(1); expect(usersWithPosts).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts?.posts[0]?.createdAt }], }); }); test('[Find One] Get users with posts + limit posts', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ with: { posts: { limit: 1, }, }, }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).eq(1); expect(usersWithPosts).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts?.posts[0]?.createdAt }], }); }); test('[Find One] Get users with posts no results found', async (t) => { const { pgjsDb: db } = t; const usersWithPosts = await db.query.usersTable.findFirst({ with: { posts: { limit: 1, }, }, }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; } | undefined >(); expect(usersWithPosts).toBeUndefined(); }); test('[Find One] Get users with posts + limit posts and users', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ with: { posts: { limit: 1, }, }, }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).eq(1); expect(usersWithPosts).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts?.posts[0]?.createdAt }], }); }); test('[Find One] Get users with posts + custom fields', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ with: { posts: true, }, extras: ({ name }) => ({ lowerName: sql`lower(${name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; lowerName: string; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).toEqual(3); expect(usersWithPosts).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, lowerName: 'dan', posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts?.posts[0]?.createdAt }, { id: 2, ownerId: 1, content: 'Post1.2', createdAt: usersWithPosts?.posts[1]?.createdAt, }, { id: 3, ownerId: 1, content: 'Post1.3', createdAt: usersWithPosts?.posts[2]?.createdAt }], }); }); test('[Find One] Get users with posts + custom fields + limits', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.2' }, { ownerId: 1, content: 'Post1.3' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ with: { posts: { limit: 1, }, }, extras: (usersTable, { sql }) => ({ lowerName: sql`lower(${usersTable.name})`.as('name_lower'), }), }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; lowerName: string; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).toEqual(1); expect(usersWithPosts).toEqual({ id: 1, name: 'Dan', lowerName: 'dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts?.posts[0]?.createdAt }], }); }); test('[Find One] Get users with posts + orderBy', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: '1' }, { ownerId: 1, content: '2' }, { ownerId: 1, content: '3' }, { ownerId: 2, content: '4' }, { ownerId: 2, content: '5' }, { ownerId: 3, content: '6' }, { ownerId: 3, content: '7' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ with: { posts: { orderBy: (postsTable, { desc }) => [desc(postsTable.content)], }, }, orderBy: (usersTable, { desc }) => [desc(usersTable.id)], }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).eq(2); expect(usersWithPosts).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, posts: [{ id: 7, ownerId: 3, content: '7', createdAt: usersWithPosts?.posts[1]?.createdAt, }, { id: 6, ownerId: 3, content: '6', createdAt: usersWithPosts?.posts[0]?.createdAt }], }); }); test('[Find One] Get users with posts + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ where: (({ id }, { eq }) => eq(id, 1)), with: { posts: { where: (({ id }, { eq }) => eq(id, 1)), }, }, }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).eq(1); expect(usersWithPosts).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: usersWithPosts?.posts[0]?.createdAt }], }); }); test('[Find One] Get users with posts + where + partial', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ columns: { id: true, name: true, }, with: { posts: { columns: { id: true, content: true, }, where: (({ id }, { eq }) => eq(id, 1)), }, }, where: (({ id }, { eq }) => eq(id, 1)), }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; name: string; posts: { id: number; content: string; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).eq(1); expect(usersWithPosts).toEqual({ id: 1, name: 'Dan', posts: [{ id: 1, content: 'Post1' }], }); }); test('[Find One] Get users with posts + where + partial. Did not select posts id, but used it in where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ columns: { id: true, name: true, }, with: { posts: { columns: { id: true, content: true, }, where: (({ id }, { eq }) => eq(id, 1)), }, }, where: (({ id }, { eq }) => eq(id, 1)), }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; name: string; posts: { id: number; content: string; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).eq(1); expect(usersWithPosts).toEqual({ id: 1, name: 'Dan', posts: [{ id: 1, content: 'Post1' }], }); }); test('[Find One] Get users with posts + where + partial(true + false)', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ columns: { id: true, name: false, }, with: { posts: { columns: { id: true, content: false, }, where: (({ id }, { eq }) => eq(id, 1)), }, }, where: (({ id }, { eq }) => eq(id, 1)), }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; posts: { id: number; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).eq(1); expect(usersWithPosts).toEqual({ id: 1, posts: [{ id: 1 }], }); }); test('[Find One] Get users with posts + where + partial(false)', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const usersWithPosts = await db.query.usersTable.findFirst({ columns: { name: false, }, with: { posts: { columns: { content: false, }, where: (({ id }, { eq }) => eq(id, 1)), }, }, where: (({ id }, { eq }) => eq(id, 1)), }); expectTypeOf(usersWithPosts).toEqualTypeOf< { id: number; verified: boolean; invitedBy: number | null; posts: { id: number; ownerId: number | null; createdAt: Date; }[]; } | undefined >(); expect(usersWithPosts!.posts.length).eq(1); expect(usersWithPosts).toEqual({ id: 1, verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, createdAt: usersWithPosts?.posts[0]?.createdAt }], }); }); /* One relation users+users. Self referencing */ test('Get user with invitee', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); const usersWithInvitee = await db.query.usersTable.findMany({ with: { invitee: true, }, }); expectTypeOf(usersWithInvitee).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; invitee: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); usersWithInvitee.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(usersWithInvitee.length).eq(4); expect(usersWithInvitee[0]?.invitee).toBeNull(); expect(usersWithInvitee[1]?.invitee).toBeNull(); expect(usersWithInvitee[2]?.invitee).not.toBeNull(); expect(usersWithInvitee[3]?.invitee).not.toBeNull(); expect(usersWithInvitee[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, invitee: null, }); expect(usersWithInvitee[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, invitee: null, }); expect(usersWithInvitee[2]).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', verified: false, invitedBy: null }, }); expect(usersWithInvitee[3]).toEqual({ id: 4, name: 'John', verified: false, invitedBy: 2, invitee: { id: 2, name: 'Andrew', verified: false, invitedBy: null }, }); }); test('Get user + limit with invitee', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew', invitedBy: 1 }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); const usersWithInvitee = await db.query.usersTable.findMany({ with: { invitee: true, }, limit: 2, }); expectTypeOf(usersWithInvitee).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; invitee: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); usersWithInvitee.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(usersWithInvitee.length).eq(2); expect(usersWithInvitee[0]?.invitee).toBeNull(); expect(usersWithInvitee[1]?.invitee).not.toBeNull(); expect(usersWithInvitee[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, invitee: null, }); expect(usersWithInvitee[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', verified: false, invitedBy: null }, }); }); test('Get user with invitee and custom fields', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); const usersWithInvitee = await db.query.usersTable.findMany({ extras: (users, { sql }) => ({ lower: sql`lower(${users.name})`.as('lower_name') }), with: { invitee: { extras: (invitee, { sql }) => ({ lower: sql`lower(${invitee.name})`.as('lower_name') }), }, }, }); expectTypeOf(usersWithInvitee).toEqualTypeOf< { id: number; name: string; verified: boolean; lower: string; invitedBy: number | null; invitee: { id: number; name: string; verified: boolean; lower: string; invitedBy: number | null; } | null; }[] >(); usersWithInvitee.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(usersWithInvitee.length).eq(4); expect(usersWithInvitee[0]?.invitee).toBeNull(); expect(usersWithInvitee[1]?.invitee).toBeNull(); expect(usersWithInvitee[2]?.invitee).not.toBeNull(); expect(usersWithInvitee[3]?.invitee).not.toBeNull(); expect(usersWithInvitee[0]).toEqual({ id: 1, name: 'Dan', lower: 'dan', verified: false, invitedBy: null, invitee: null, }); expect(usersWithInvitee[1]).toEqual({ id: 2, name: 'Andrew', lower: 'andrew', verified: false, invitedBy: null, invitee: null, }); expect(usersWithInvitee[2]).toEqual({ id: 3, name: 'Alex', lower: 'alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', lower: 'dan', verified: false, invitedBy: null }, }); expect(usersWithInvitee[3]).toEqual({ id: 4, name: 'John', lower: 'john', verified: false, invitedBy: 2, invitee: { id: 2, name: 'Andrew', lower: 'andrew', verified: false, invitedBy: null }, }); }); test('Get user with invitee and custom fields + limits', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); const usersWithInvitee = await db.query.usersTable.findMany({ extras: (users, { sql }) => ({ lower: sql`lower(${users.name})`.as('lower_name') }), limit: 3, with: { invitee: { extras: (invitee, { sql }) => ({ lower: sql`lower(${invitee.name})`.as('lower_name') }), }, }, }); expectTypeOf(usersWithInvitee).toEqualTypeOf< { id: number; name: string; verified: boolean; lower: string; invitedBy: number | null; invitee: { id: number; name: string; verified: boolean; lower: string; invitedBy: number | null; } | null; }[] >(); usersWithInvitee.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(usersWithInvitee.length).eq(3); expect(usersWithInvitee[0]?.invitee).toBeNull(); expect(usersWithInvitee[1]?.invitee).toBeNull(); expect(usersWithInvitee[2]?.invitee).not.toBeNull(); expect(usersWithInvitee[0]).toEqual({ id: 1, name: 'Dan', lower: 'dan', verified: false, invitedBy: null, invitee: null, }); expect(usersWithInvitee[1]).toEqual({ id: 2, name: 'Andrew', lower: 'andrew', verified: false, invitedBy: null, invitee: null, }); expect(usersWithInvitee[2]).toEqual({ id: 3, name: 'Alex', lower: 'alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', lower: 'dan', verified: false, invitedBy: null }, }); }); test('Get user with invitee + order by', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); const usersWithInvitee = await db.query.usersTable.findMany({ orderBy: (users, { desc }) => [desc(users.id)], with: { invitee: true, }, }); expectTypeOf(usersWithInvitee).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; invitee: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); expect(usersWithInvitee.length).eq(4); expect(usersWithInvitee[3]?.invitee).toBeNull(); expect(usersWithInvitee[2]?.invitee).toBeNull(); expect(usersWithInvitee[1]?.invitee).not.toBeNull(); expect(usersWithInvitee[0]?.invitee).not.toBeNull(); expect(usersWithInvitee[3]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, invitee: null, }); expect(usersWithInvitee[2]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, invitee: null, }); expect(usersWithInvitee[1]).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', verified: false, invitedBy: null }, }); expect(usersWithInvitee[0]).toEqual({ id: 4, name: 'John', verified: false, invitedBy: 2, invitee: { id: 2, name: 'Andrew', verified: false, invitedBy: null }, }); }); test('Get user with invitee + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); const usersWithInvitee = await db.query.usersTable.findMany({ where: (users, { eq, or }) => (or(eq(users.id, 3), eq(users.id, 4))), with: { invitee: true, }, }); expectTypeOf(usersWithInvitee).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; invitee: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); expect(usersWithInvitee.length).eq(2); expect(usersWithInvitee[0]?.invitee).not.toBeNull(); expect(usersWithInvitee[1]?.invitee).not.toBeNull(); expect(usersWithInvitee).toContainEqual({ id: 3, name: 'Alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', verified: false, invitedBy: null }, }); expect(usersWithInvitee).toContainEqual({ id: 4, name: 'John', verified: false, invitedBy: 2, invitee: { id: 2, name: 'Andrew', verified: false, invitedBy: null }, }); }); test('Get user with invitee + where + partial', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); const usersWithInvitee = await db.query.usersTable.findMany({ where: (users, { eq, or }) => (or(eq(users.id, 3), eq(users.id, 4))), columns: { id: true, name: true, }, with: { invitee: { columns: { id: true, name: true, }, }, }, }); expectTypeOf(usersWithInvitee).toEqualTypeOf< { id: number; name: string; invitee: { id: number; name: string; } | null; }[] >(); expect(usersWithInvitee.length).eq(2); expect(usersWithInvitee[0]?.invitee).not.toBeNull(); expect(usersWithInvitee[1]?.invitee).not.toBeNull(); expect(usersWithInvitee).toContainEqual({ id: 3, name: 'Alex', invitee: { id: 1, name: 'Dan' }, }); expect(usersWithInvitee).toContainEqual({ id: 4, name: 'John', invitee: { id: 2, name: 'Andrew' }, }); }); test('Get user with invitee + where + partial. Did not select users id, but used it in where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); const usersWithInvitee = await db.query.usersTable.findMany({ where: (users, { eq, or }) => (or(eq(users.id, 3), eq(users.id, 4))), columns: { name: true, }, with: { invitee: { columns: { id: true, name: true, }, }, }, }); expectTypeOf(usersWithInvitee).toEqualTypeOf< { name: string; invitee: { id: number; name: string; } | null; }[] >(); expect(usersWithInvitee.length).eq(2); expect(usersWithInvitee[0]?.invitee).not.toBeNull(); expect(usersWithInvitee[1]?.invitee).not.toBeNull(); expect(usersWithInvitee).toContainEqual({ name: 'Alex', invitee: { id: 1, name: 'Dan' }, }); expect(usersWithInvitee).toContainEqual({ name: 'John', invitee: { id: 2, name: 'Andrew' }, }); }); test('Get user with invitee + where + partial(true+false)', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); const usersWithInvitee = await db.query.usersTable.findMany({ where: (users, { eq, or }) => (or(eq(users.id, 3), eq(users.id, 4))), columns: { id: true, name: true, verified: false, }, with: { invitee: { columns: { id: true, name: true, verified: false, }, }, }, }); expectTypeOf(usersWithInvitee).toEqualTypeOf< { id: number; name: string; invitee: { id: number; name: string; } | null; }[] >(); expect(usersWithInvitee.length).eq(2); expect(usersWithInvitee[0]?.invitee).not.toBeNull(); expect(usersWithInvitee[1]?.invitee).not.toBeNull(); expect(usersWithInvitee).toContainEqual({ id: 3, name: 'Alex', invitee: { id: 1, name: 'Dan' }, }); expect(usersWithInvitee).toContainEqual({ id: 4, name: 'John', invitee: { id: 2, name: 'Andrew' }, }); }); test('Get user with invitee + where + partial(false)', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); const usersWithInvitee = await db.query.usersTable.findMany({ where: (users, { eq, or }) => (or(eq(users.id, 3), eq(users.id, 4))), columns: { verified: false, }, with: { invitee: { columns: { name: false, }, }, }, }); expectTypeOf(usersWithInvitee).toEqualTypeOf< { id: number; name: string; invitedBy: number | null; invitee: { id: number; verified: boolean; invitedBy: number | null; } | null; }[] >(); expect(usersWithInvitee.length).eq(2); expect(usersWithInvitee[0]?.invitee).not.toBeNull(); expect(usersWithInvitee[1]?.invitee).not.toBeNull(); expect(usersWithInvitee).toContainEqual({ id: 3, name: 'Alex', invitedBy: 1, invitee: { id: 1, verified: false, invitedBy: null }, }); expect(usersWithInvitee).toContainEqual({ id: 4, name: 'John', invitedBy: 2, invitee: { id: 2, verified: false, invitedBy: null }, }); }); /* Two first-level relations users+users and users+posts */ test('Get user with invitee and posts', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const response = await db.query.usersTable.findMany({ with: { invitee: true, posts: true, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; ownerId: number | null; content: string; createdAt: Date }[]; invitee: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).eq(4); expect(response[0]?.invitee).toBeNull(); expect(response[1]?.invitee).toBeNull(); expect(response[2]?.invitee).not.toBeNull(); expect(response[3]?.invitee).not.toBeNull(); expect(response[0]?.posts.length).eq(1); expect(response[1]?.posts.length).eq(1); expect(response[2]?.posts.length).eq(1); expect(response).toContainEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, invitee: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: response[0]?.posts[0]?.createdAt }], }); expect(response).toContainEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, invitee: null, posts: [{ id: 2, ownerId: 2, content: 'Post2', createdAt: response[1]?.posts[0]?.createdAt }], }); expect(response).toContainEqual({ id: 3, name: 'Alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', verified: false, invitedBy: null }, posts: [{ id: 3, ownerId: 3, content: 'Post3', createdAt: response[2]?.posts[0]?.createdAt }], }); expect(response).toContainEqual({ id: 4, name: 'John', verified: false, invitedBy: 2, invitee: { id: 2, name: 'Andrew', verified: false, invitedBy: null }, posts: [], }); }); test('Get user with invitee and posts + limit posts and users', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const response = await db.query.usersTable.findMany({ limit: 3, with: { invitee: true, posts: { limit: 1, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; ownerId: number | null; content: string; createdAt: Date }[]; invitee: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).eq(3); expect(response[0]?.invitee).toBeNull(); expect(response[1]?.invitee).toBeNull(); expect(response[2]?.invitee).not.toBeNull(); expect(response[0]?.posts.length).eq(1); expect(response[1]?.posts.length).eq(1); expect(response[2]?.posts.length).eq(1); expect(response).toContainEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, invitee: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: response[0]?.posts[0]?.createdAt }], }); expect(response).toContainEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, invitee: null, posts: [{ id: 3, ownerId: 2, content: 'Post2', createdAt: response[1]?.posts[0]?.createdAt }], }); expect(response).toContainEqual({ id: 3, name: 'Alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', verified: false, invitedBy: null }, posts: [{ id: 5, ownerId: 3, content: 'Post3', createdAt: response[2]?.posts[0]?.createdAt }], }); }); test('Get user with invitee and posts + limits + custom fields in each', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const response = await db.query.usersTable.findMany({ limit: 3, extras: (users, { sql }) => ({ lower: sql`lower(${users.name})`.as('lower_name') }), with: { invitee: { extras: (users, { sql }) => ({ lower: sql`lower(${users.name})`.as('lower_invitee_name') }), }, posts: { limit: 1, extras: (posts, { sql }) => ({ lower: sql`lower(${posts.content})`.as('lower_content') }), }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; lower: string; invitedBy: number | null; posts: { id: number; lower: string; ownerId: number | null; content: string; createdAt: Date }[]; invitee: { id: number; name: string; lower: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).eq(3); expect(response[0]?.invitee).toBeNull(); expect(response[1]?.invitee).toBeNull(); expect(response[2]?.invitee).not.toBeNull(); expect(response[0]?.posts.length).eq(1); expect(response[1]?.posts.length).eq(1); expect(response[2]?.posts.length).eq(1); expect(response).toContainEqual({ id: 1, name: 'Dan', lower: 'dan', verified: false, invitedBy: null, invitee: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', lower: 'post1', createdAt: response[0]?.posts[0]?.createdAt }], }); expect(response).toContainEqual({ id: 2, name: 'Andrew', lower: 'andrew', verified: false, invitedBy: null, invitee: null, posts: [{ id: 3, ownerId: 2, content: 'Post2', lower: 'post2', createdAt: response[1]?.posts[0]?.createdAt }], }); expect(response).toContainEqual({ id: 3, name: 'Alex', lower: 'alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', lower: 'dan', verified: false, invitedBy: null }, posts: [{ id: 5, ownerId: 3, content: 'Post3', lower: 'post3', createdAt: response[2]?.posts[0]?.createdAt }], }); }); test('Get user with invitee and posts + custom fields in each', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const response = await db.query.usersTable.findMany({ extras: (users, { sql }) => ({ lower: sql`lower(${users.name})`.as('lower_name') }), with: { invitee: { extras: (users, { sql }) => ({ lower: sql`lower(${users.name})`.as('lower_name') }), }, posts: { extras: (posts, { sql }) => ({ lower: sql`lower(${posts.content})`.as('lower_name') }), }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; lower: string; invitedBy: number | null; posts: { id: number; lower: string; ownerId: number | null; content: string; createdAt: Date }[]; invitee: { id: number; name: string; lower: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).eq(4); expect(response[0]?.invitee).toBeNull(); expect(response[1]?.invitee).toBeNull(); expect(response[2]?.invitee).not.toBeNull(); expect(response[3]?.invitee).not.toBeNull(); expect(response[0]?.posts.length).eq(2); expect(response[1]?.posts.length).eq(2); expect(response[2]?.posts.length).eq(2); expect(response[3]?.posts.length).eq(0); expect(response).toContainEqual({ id: 1, name: 'Dan', lower: 'dan', verified: false, invitedBy: null, invitee: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', lower: 'post1', createdAt: response[0]?.posts[0]?.createdAt }, { id: 2, ownerId: 1, content: 'Post1.1', lower: 'post1.1', createdAt: response[0]?.posts[1]?.createdAt, }], }); expect(response).toContainEqual({ id: 2, name: 'Andrew', lower: 'andrew', verified: false, invitedBy: null, invitee: null, posts: [{ id: 3, ownerId: 2, content: 'Post2', lower: 'post2', createdAt: response[1]?.posts[0]?.createdAt }, { id: 4, ownerId: 2, content: 'Post2.1', lower: 'post2.1', createdAt: response[1]?.posts[1]?.createdAt, }], }); expect(response).toContainEqual({ id: 3, name: 'Alex', lower: 'alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', lower: 'dan', verified: false, invitedBy: null }, posts: [{ id: 5, ownerId: 3, content: 'Post3', lower: 'post3', createdAt: response[2]?.posts[0]?.createdAt }, { id: 6, ownerId: 3, content: 'Post3.1', lower: 'post3.1', createdAt: response[2]?.posts[1]?.createdAt, }], }); expect(response).toContainEqual({ id: 4, name: 'John', lower: 'john', verified: false, invitedBy: 2, invitee: { id: 2, name: 'Andrew', lower: 'andrew', verified: false, invitedBy: null }, posts: [], }); }); test('Get user with invitee and posts + orderBy', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, ]); const response = await db.query.usersTable.findMany({ orderBy: (users, { desc }) => [desc(users.id)], with: { invitee: true, posts: { orderBy: (posts, { desc }) => [desc(posts.id)], }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; ownerId: number | null; content: string; createdAt: Date }[]; invitee: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); expect(response.length).eq(4); expect(response[3]?.invitee).toBeNull(); expect(response[2]?.invitee).toBeNull(); expect(response[1]?.invitee).not.toBeNull(); expect(response[0]?.invitee).not.toBeNull(); expect(response[0]?.posts.length).eq(0); expect(response[1]?.posts.length).eq(1); expect(response[2]?.posts.length).eq(2); expect(response[3]?.posts.length).eq(2); expect(response[3]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, invitee: null, posts: [{ id: 2, ownerId: 1, content: 'Post1.1', createdAt: response[3]?.posts[0]?.createdAt }, { id: 1, ownerId: 1, content: 'Post1', createdAt: response[3]?.posts[1]?.createdAt, }], }); expect(response[2]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, invitee: null, posts: [{ id: 4, ownerId: 2, content: 'Post2.1', createdAt: response[2]?.posts[0]?.createdAt }, { id: 3, ownerId: 2, content: 'Post2', createdAt: response[2]?.posts[1]?.createdAt, }], }); expect(response[1]).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', verified: false, invitedBy: null }, posts: [{ id: 5, ownerId: 3, content: 'Post3', createdAt: response[3]?.posts[1]?.createdAt, }], }); expect(response[0]).toEqual({ id: 4, name: 'John', verified: false, invitedBy: 2, invitee: { id: 2, name: 'Andrew', verified: false, invitedBy: null }, posts: [], }); }); test('Get user with invitee and posts + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 3, content: 'Post3' }, ]); const response = await db.query.usersTable.findMany({ where: (users, { eq, or }) => (or(eq(users.id, 2), eq(users.id, 3))), with: { invitee: true, posts: { where: (posts, { eq }) => (eq(posts.ownerId, 2)), }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; ownerId: number | null; content: string; createdAt: Date }[]; invitee: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).eq(2); expect(response[0]?.invitee).toBeNull(); expect(response[1]?.invitee).not.toBeNull(); expect(response[0]?.posts.length).eq(1); expect(response[1]?.posts.length).eq(0); expect(response).toContainEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, invitee: null, posts: [{ id: 2, ownerId: 2, content: 'Post2', createdAt: response[0]?.posts[0]?.createdAt }], }); expect(response).toContainEqual({ id: 3, name: 'Alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', verified: false, invitedBy: null }, posts: [], }); }); test('Get user with invitee and posts + limit posts and users + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, { ownerId: 3, content: 'Post3.1' }, ]); const response = await db.query.usersTable.findMany({ where: (users, { eq, or }) => (or(eq(users.id, 3), eq(users.id, 4))), limit: 1, with: { invitee: true, posts: { where: (posts, { eq }) => (eq(posts.ownerId, 3)), limit: 1, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; ownerId: number | null; content: string; createdAt: Date }[]; invitee: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); expect(response.length).eq(1); expect(response[0]?.invitee).not.toBeNull(); expect(response[0]?.posts.length).eq(1); expect(response).toContainEqual({ id: 3, name: 'Alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', verified: false, invitedBy: null }, posts: [{ id: 5, ownerId: 3, content: 'Post3', createdAt: response[0]?.posts[0]?.createdAt }], }); }); test('Get user with invitee and posts + orderBy + where + custom', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, ]); const response = await db.query.usersTable.findMany({ orderBy: [desc(usersTable.id)], where: or(eq(usersTable.id, 3), eq(usersTable.id, 4)), extras: { lower: sql`lower(${usersTable.name})`.as('lower_name'), }, with: { invitee: true, posts: { where: eq(postsTable.ownerId, 3), orderBy: [desc(postsTable.id)], extras: { lower: sql`lower(${postsTable.content})`.as('lower_name'), }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; lower: string; posts: { id: number; lower: string; ownerId: number | null; content: string; createdAt: Date }[]; invitee: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[] >(); expect(response.length).eq(2); expect(response[1]?.invitee).not.toBeNull(); expect(response[0]?.invitee).not.toBeNull(); expect(response[0]?.posts.length).eq(0); expect(response[1]?.posts.length).eq(1); expect(response[1]).toEqual({ id: 3, name: 'Alex', lower: 'alex', verified: false, invitedBy: 1, invitee: { id: 1, name: 'Dan', verified: false, invitedBy: null }, posts: [{ id: 5, ownerId: 3, content: 'Post3', lower: 'post3', createdAt: response[1]?.posts[0]?.createdAt, }], }); expect(response[0]).toEqual({ id: 4, name: 'John', lower: 'john', verified: false, invitedBy: 2, invitee: { id: 2, name: 'Andrew', verified: false, invitedBy: null }, posts: [], }); }); test('Get user with invitee and posts + orderBy + where + partial + custom', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex', invitedBy: 1 }, { id: 4, name: 'John', invitedBy: 2 }, ]); await db.insert(postsTable).values([ { ownerId: 1, content: 'Post1' }, { ownerId: 1, content: 'Post1.1' }, { ownerId: 2, content: 'Post2' }, { ownerId: 2, content: 'Post2.1' }, { ownerId: 3, content: 'Post3' }, ]); const response = await db.query.usersTable.findMany({ orderBy: [desc(usersTable.id)], where: or(eq(usersTable.id, 3), eq(usersTable.id, 4)), extras: { lower: sql`lower(${usersTable.name})`.as('lower_name'), }, columns: { id: true, name: true, }, with: { invitee: { columns: { id: true, name: true, }, extras: { lower: sql`lower(${usersTable.name})`.as('lower_name'), }, }, posts: { columns: { id: true, content: true, }, where: eq(postsTable.ownerId, 3), orderBy: [desc(postsTable.id)], extras: { lower: sql`lower(${postsTable.content})`.as('lower_name'), }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; lower: string; posts: { id: number; lower: string; content: string }[]; invitee: { id: number; name: string; lower: string; } | null; }[] >(); expect(response.length).eq(2); expect(response[1]?.invitee).not.toBeNull(); expect(response[0]?.invitee).not.toBeNull(); expect(response[0]?.posts.length).eq(0); expect(response[1]?.posts.length).eq(1); expect(response[1]).toEqual({ id: 3, name: 'Alex', lower: 'alex', invitee: { id: 1, name: 'Dan', lower: 'dan' }, posts: [{ id: 5, content: 'Post3', lower: 'post3', }], }); expect(response[0]).toEqual({ id: 4, name: 'John', lower: 'john', invitee: { id: 2, name: 'Andrew', lower: 'andrew' }, posts: [], }); }); /* One two-level relation users+posts+comments */ test('Get user with posts and posts with comments', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { id: 1, ownerId: 1, content: 'Post1' }, { id: 2, ownerId: 2, content: 'Post2' }, { id: 3, ownerId: 3, content: 'Post3' }, ]); await db.insert(commentsTable).values([ { postId: 1, content: 'Comment1', creator: 2 }, { postId: 2, content: 'Comment2', creator: 2 }, { postId: 3, content: 'Comment3', creator: 3 }, ]); const response = await db.query.usersTable.findMany({ with: { posts: { with: { comments: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; comments: { id: number; content: string; createdAt: Date; creator: number | null; postId: number | null; }[]; }[]; }[] >(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).eq(3); expect(response[0]?.posts.length).eq(1); expect(response[1]?.posts.length).eq(1); expect(response[2]?.posts.length).eq(1); expect(response[0]?.posts[0]?.comments.length).eq(1); expect(response[1]?.posts[0]?.comments.length).eq(1); expect(response[2]?.posts[0]?.comments.length).eq(1); expect(response[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: response[0]?.posts[0]?.createdAt, comments: [ { id: 1, content: 'Comment1', creator: 2, postId: 1, createdAt: response[0]?.posts[0]?.comments[0]?.createdAt, }, ], }], }); expect(response[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, posts: [{ id: 2, ownerId: 2, content: 'Post2', createdAt: response[1]?.posts[0]?.createdAt, comments: [ { id: 2, content: 'Comment2', creator: 2, postId: 2, createdAt: response[1]?.posts[0]?.comments[0]?.createdAt, }, ], }], }); // expect(response[2]).toEqual({ // id: 3, // name: 'Alex', // verified: false, // invitedBy: null, // posts: [{ // id: 3, // ownerId: 3, // content: 'Post3', // createdAt: response[2]?.posts[0]?.createdAt, // comments: [ // { // id: , // content: 'Comment3', // creator: 3, // postId: 3, // createdAt: response[2]?.posts[0]?.comments[0]?.createdAt, // }, // ], // }], // }); }); // Get user with limit posts and limit comments // Get user with custom field + post + comment with custom field // Get user with limit + posts orderBy + comment orderBy // Get user with where + posts where + comment where // Get user with where + posts partial where + comment where // Get user with where + posts partial where + comment partial(false) where // Get user with where partial(false) + posts partial where partial(false) + comment partial(false+true) where // Get user with where + posts partial where + comment where. Didn't select field from where in posts // Get user with where + posts partial where + comment where. Didn't select field from where for all // Get with limit+offset in each /* One two-level + One first-level relation users+posts+comments and users+users */ /* One three-level relation users+posts+comments+comment_owner */ test('Get user with posts and posts with comments and comments with owner', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { id: 1, ownerId: 1, content: 'Post1' }, { id: 2, ownerId: 2, content: 'Post2' }, { id: 3, ownerId: 3, content: 'Post3' }, ]); await db.insert(commentsTable).values([ { postId: 1, content: 'Comment1', creator: 2 }, { postId: 2, content: 'Comment2', creator: 2 }, { postId: 3, content: 'Comment3', creator: 3 }, ]); const response = await db.query.usersTable.findMany({ with: { posts: { with: { comments: { with: { author: true, }, }, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; comments: { id: number; content: string; createdAt: Date; creator: number | null; postId: number | null; author: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[]; }[]; }[]>(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).eq(3); expect(response[0]?.posts.length).eq(1); expect(response[1]?.posts.length).eq(1); expect(response[2]?.posts.length).eq(1); expect(response[0]?.posts[0]?.comments.length).eq(1); expect(response[1]?.posts[0]?.comments.length).eq(1); expect(response[2]?.posts[0]?.comments.length).eq(1); expect(response[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, posts: [{ id: 1, ownerId: 1, content: 'Post1', createdAt: response[0]?.posts[0]?.createdAt, comments: [ { id: 1, content: 'Comment1', creator: 2, author: { id: 2, name: 'Andrew', verified: false, invitedBy: null, }, postId: 1, createdAt: response[0]?.posts[0]?.comments[0]?.createdAt, }, ], }], }); expect(response[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, posts: [{ id: 2, ownerId: 2, content: 'Post2', createdAt: response[1]?.posts[0]?.createdAt, comments: [ { id: 2, content: 'Comment2', creator: 2, author: { id: 2, name: 'Andrew', verified: false, invitedBy: null, }, postId: 2, createdAt: response[1]?.posts[0]?.comments[0]?.createdAt, }, ], }], }); }); test('Get user with posts and posts with comments and comments with owner where exists', async () => { await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(postsTable).values([ { id: 1, ownerId: 1, content: 'Post1' }, { id: 2, ownerId: 2, content: 'Post2' }, { id: 3, ownerId: 3, content: 'Post3' }, ]); await db.insert(commentsTable).values([ { postId: 1, content: 'Comment1', creator: 2 }, { postId: 2, content: 'Comment2', creator: 2 }, { postId: 3, content: 'Comment3', creator: 3 }, ]); const response = await db.query.usersTable.findMany({ with: { posts: { with: { comments: { with: { author: true, }, }, }, }, }, where: (table, { notExists, eq }) => notExists(db.select({ one: sql`1` }).from(usersTable).where(eq(sql`1`, table.id))), }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; posts: { id: number; content: string; ownerId: number | null; createdAt: Date; comments: { id: number; content: string; createdAt: Date; creator: number | null; postId: number | null; author: { id: number; name: string; verified: boolean; invitedBy: number | null; } | null; }[]; }[]; }[]>(); expect(response.length).eq(2); expect(response[0]?.posts.length).eq(1); expect(response[0]?.posts[0]?.comments.length).eq(1); expect(response[0]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, posts: [{ id: 2, ownerId: 2, content: 'Post2', createdAt: response[0]?.posts[0]?.createdAt, comments: [ { id: 2, content: 'Comment2', creator: 2, author: { id: 2, name: 'Andrew', verified: false, invitedBy: null, }, postId: 2, createdAt: response[0]?.posts[0]?.comments[0]?.createdAt, }, ], }], }); }); /* One three-level relation + 1 first-level relatioon 1. users+posts+comments+comment_owner 2. users+users */ /* One four-level relation users+posts+comments+coment_likes */ /* [Find Many] Many-to-many cases Users+users_to_groups+groups */ test('[Find Many] Get users with groups', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findMany({ with: { usersToGroups: { columns: {}, with: { group: true, }, orderBy: usersToGroupsTable.groupId, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; }[]>(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).toEqual(3); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response[1]?.usersToGroups.length).toEqual(1); expect(response[2]?.usersToGroups.length).toEqual(2); expect(response).toContainEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 1, name: 'Group1', description: null, }, }], }); expect(response).toContainEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 2, name: 'Group2', description: null, }, }], }); expect(response).toContainEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, usersToGroups: expect.arrayContaining([ { group: { id: 2, name: 'Group2', description: null, }, }, { group: { id: 3, name: 'Group3', description: null, }, }, ]), }); }); test('[Find Many] Get groups with users', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findMany({ with: { usersToGroups: { columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; }[]>(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).toEqual(3); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response[1]?.usersToGroups.length).toEqual(2); expect(response[2]?.usersToGroups.length).toEqual(1); expect(response).toContainEqual({ id: 1, name: 'Group1', description: null, usersToGroups: [{ user: { id: 1, name: 'Dan', verified: false, invitedBy: null, }, }], }); expect(response).toContainEqual({ id: 2, name: 'Group2', description: null, usersToGroups: [{ user: { id: 2, name: 'Andrew', verified: false, invitedBy: null, }, }, { user: { id: 3, name: 'Alex', verified: false, invitedBy: null, }, }], }); expect(response).toContainEqual({ id: 3, name: 'Group3', description: null, usersToGroups: [{ user: { id: 3, name: 'Alex', verified: false, invitedBy: null, }, }], }); }); test('[Find Many] Get users with groups + limit', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 2, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findMany({ limit: 2, with: { usersToGroups: { limit: 1, columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; }[]>(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).toEqual(2); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response[1]?.usersToGroups.length).toEqual(1); expect(response).toContainEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 1, name: 'Group1', description: null, }, }], }); expect(response).toContainEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 2, name: 'Group2', description: null, }, }], }); }); test('[Find Many] Get groups with users + limit', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findMany({ limit: 2, with: { usersToGroups: { limit: 1, columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; }[]>(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).toEqual(2); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response[1]?.usersToGroups.length).toEqual(1); expect(response).toContainEqual({ id: 1, name: 'Group1', description: null, usersToGroups: [{ user: { id: 1, name: 'Dan', verified: false, invitedBy: null, }, }], }); expect(response).toContainEqual({ id: 2, name: 'Group2', description: null, usersToGroups: [{ user: { id: 2, name: 'Andrew', verified: false, invitedBy: null, }, }], }); }); test('[Find Many] Get users with groups + limit + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 2, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findMany({ limit: 1, where: (_, { eq, or }) => or(eq(usersTable.id, 1), eq(usersTable.id, 2)), with: { usersToGroups: { where: eq(usersToGroupsTable.groupId, 1), columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; }[]>(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).toEqual(1); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response).toContainEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 1, name: 'Group1', description: null, }, }], }); }); test('[Find Many] Get groups with users + limit + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findMany({ limit: 1, where: gt(groupsTable.id, 1), with: { usersToGroups: { where: eq(usersToGroupsTable.userId, 2), limit: 1, columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; }[]>(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).toEqual(1); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response).toContainEqual({ id: 2, name: 'Group2', description: null, usersToGroups: [{ user: { id: 2, name: 'Andrew', verified: false, invitedBy: null, }, }], }); }); test('[Find Many] Get users with groups + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 2, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findMany({ where: (_, { eq, or }) => or(eq(usersTable.id, 1), eq(usersTable.id, 2)), with: { usersToGroups: { where: eq(usersToGroupsTable.groupId, 2), columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; }[]>(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).toEqual(2); expect(response[0]?.usersToGroups.length).toEqual(0); expect(response[1]?.usersToGroups.length).toEqual(1); expect(response).toContainEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, usersToGroups: [], }); expect(response).toContainEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 2, name: 'Group2', description: null, }, }], }); }); test('[Find Many] Get groups with users + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findMany({ where: gt(groupsTable.id, 1), with: { usersToGroups: { where: eq(usersToGroupsTable.userId, 2), columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; }[]>(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).toEqual(2); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response[1]?.usersToGroups.length).toEqual(0); expect(response).toContainEqual({ id: 2, name: 'Group2', description: null, usersToGroups: [{ user: { id: 2, name: 'Andrew', verified: false, invitedBy: null, }, }], }); expect(response).toContainEqual({ id: 3, name: 'Group3', description: null, usersToGroups: [], }); }); test('[Find Many] Get users with groups + orderBy', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findMany({ orderBy: (users, { desc }) => [desc(users.id)], with: { usersToGroups: { orderBy: [desc(usersToGroupsTable.groupId)], columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; }[]>(); expect(response.length).toEqual(3); expect(response[0]?.usersToGroups.length).toEqual(2); expect(response[1]?.usersToGroups.length).toEqual(1); expect(response[2]?.usersToGroups.length).toEqual(1); expect(response[2]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 1, name: 'Group1', description: null, }, }], }); expect(response[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 2, name: 'Group2', description: null, }, }], }); expect(response[0]).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 3, name: 'Group3', description: null, }, }, { group: { id: 2, name: 'Group2', description: null, }, }], }); }); test('[Find Many] Get groups with users + orderBy', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findMany({ orderBy: [desc(groupsTable.id)], with: { usersToGroups: { orderBy: (utg, { desc }) => [desc(utg.userId)], columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; }[]>(); expect(response.length).toEqual(3); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response[1]?.usersToGroups.length).toEqual(2); expect(response[2]?.usersToGroups.length).toEqual(1); expect(response[2]).toEqual({ id: 1, name: 'Group1', description: null, usersToGroups: [{ user: { id: 1, name: 'Dan', verified: false, invitedBy: null, }, }], }); expect(response[1]).toEqual({ id: 2, name: 'Group2', description: null, usersToGroups: [{ user: { id: 3, name: 'Alex', verified: false, invitedBy: null, }, }, { user: { id: 2, name: 'Andrew', verified: false, invitedBy: null, }, }], }); expect(response[0]).toEqual({ id: 3, name: 'Group3', description: null, usersToGroups: [{ user: { id: 3, name: 'Alex', verified: false, invitedBy: null, }, }], }); }); test('[Find Many] Get users with groups + orderBy + limit', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findMany({ orderBy: (users, { desc }) => [desc(users.id)], limit: 2, with: { usersToGroups: { limit: 1, orderBy: [desc(usersToGroupsTable.groupId)], columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; }[]>(); expect(response.length).toEqual(2); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response[1]?.usersToGroups.length).toEqual(1); expect(response[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 2, name: 'Group2', description: null, }, }], }); expect(response[0]).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 3, name: 'Group3', description: null, }, }], }); }); /* [Find One] Many-to-many cases Users+users_to_groups+groups */ test('[Find One] Get users with groups', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findFirst({ with: { usersToGroups: { columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(1); expect(response).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 1, name: 'Group1', description: null, }, }], }); }); test('[Find One] Get groups with users', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findFirst({ with: { usersToGroups: { columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(1); expect(response).toEqual({ id: 1, name: 'Group1', description: null, usersToGroups: [{ user: { id: 1, name: 'Dan', verified: false, invitedBy: null, }, }], }); }); test('[Find One] Get users with groups + limit', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 2, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findFirst({ with: { usersToGroups: { limit: 1, columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(1); expect(response).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 1, name: 'Group1', description: null, }, }], }); }); test('[Find One] Get groups with users + limit', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findFirst({ with: { usersToGroups: { limit: 1, columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(1); expect(response).toEqual({ id: 1, name: 'Group1', description: null, usersToGroups: [{ user: { id: 1, name: 'Dan', verified: false, invitedBy: null, }, }], }); }); test('[Find One] Get users with groups + limit + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 2, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findFirst({ where: (_, { eq, or }) => or(eq(usersTable.id, 1), eq(usersTable.id, 2)), with: { usersToGroups: { where: eq(usersToGroupsTable.groupId, 1), columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(1); expect(response).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 1, name: 'Group1', description: null, }, }], }); }); test('[Find One] Get groups with users + limit + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findFirst({ where: gt(groupsTable.id, 1), with: { usersToGroups: { where: eq(usersToGroupsTable.userId, 2), limit: 1, columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(1); expect(response).toEqual({ id: 2, name: 'Group2', description: null, usersToGroups: [{ user: { id: 2, name: 'Andrew', verified: false, invitedBy: null, }, }], }); }); test('[Find One] Get users with groups + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 2, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findFirst({ where: (_, { eq, or }) => or(eq(usersTable.id, 1), eq(usersTable.id, 2)), with: { usersToGroups: { where: eq(usersToGroupsTable.groupId, 2), columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(0); expect(response).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, usersToGroups: [], }); }); test('[Find One] Get groups with users + where', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findFirst({ where: gt(groupsTable.id, 1), with: { usersToGroups: { where: eq(usersToGroupsTable.userId, 2), columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(1); expect(response).toEqual({ id: 2, name: 'Group2', description: null, usersToGroups: [{ user: { id: 2, name: 'Andrew', verified: false, invitedBy: null, }, }], }); }); test('[Find One] Get users with groups + orderBy', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findFirst({ orderBy: (users, { desc }) => [desc(users.id)], with: { usersToGroups: { orderBy: [desc(usersToGroupsTable.groupId)], columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(2); expect(response).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 3, name: 'Group3', description: null, }, }, { group: { id: 2, name: 'Group2', description: null, }, }], }); }); test('[Find One] Get groups with users + orderBy', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findFirst({ orderBy: [desc(groupsTable.id)], with: { usersToGroups: { orderBy: (utg, { desc }) => [desc(utg.userId)], columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(1); expect(response).toEqual({ id: 3, name: 'Group3', description: null, usersToGroups: [{ user: { id: 3, name: 'Alex', verified: false, invitedBy: null, }, }], }); }); test('[Find One] Get users with groups + orderBy + limit', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findFirst({ orderBy: (users, { desc }) => [desc(users.id)], with: { usersToGroups: { limit: 1, orderBy: [desc(usersToGroupsTable.groupId)], columns: {}, with: { group: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; usersToGroups: { group: { id: number; name: string; description: string | null; }; }[]; } | undefined >(); expect(response?.usersToGroups.length).toEqual(1); expect(response).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 3, name: 'Group3', description: null, }, }], }); }); test('Get groups with users + orderBy + limit', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findMany({ orderBy: [desc(groupsTable.id)], limit: 2, with: { usersToGroups: { limit: 1, orderBy: (utg, { desc }) => [desc(utg.userId)], columns: {}, with: { user: true, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; description: string | null; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; }; }[]; }[] >(); expect(response.length).toEqual(2); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response[1]?.usersToGroups.length).toEqual(1); expect(response[1]).toEqual({ id: 2, name: 'Group2', description: null, usersToGroups: [{ user: { id: 3, name: 'Alex', verified: false, invitedBy: null, }, }], }); expect(response[0]).toEqual({ id: 3, name: 'Group3', description: null, usersToGroups: [{ user: { id: 3, name: 'Alex', verified: false, invitedBy: null, }, }], }); }); test('Get users with groups + custom', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.usersTable.findMany({ extras: { lower: sql`lower(${usersTable.name})`.as('lower_name'), }, with: { usersToGroups: { columns: {}, with: { group: { extras: { lower: sql`lower(${groupsTable.name})`.as('lower_name'), }, }, }, orderBy: usersToGroupsTable.groupId, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; verified: boolean; invitedBy: number | null; lower: string; usersToGroups: { group: { id: number; name: string; description: string | null; lower: string; }; }[]; }[] >(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).toEqual(3); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response[1]?.usersToGroups.length).toEqual(1); expect(response[2]?.usersToGroups.length).toEqual(2); expect(response).toContainEqual({ id: 1, name: 'Dan', lower: 'dan', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 1, name: 'Group1', lower: 'group1', description: null, }, }], }); expect(response).toContainEqual({ id: 2, name: 'Andrew', lower: 'andrew', verified: false, invitedBy: null, usersToGroups: [{ group: { id: 2, name: 'Group2', lower: 'group2', description: null, }, }], }); expect(response).toContainEqual({ id: 3, name: 'Alex', lower: 'alex', verified: false, invitedBy: null, usersToGroups: [ { group: { id: 2, name: 'Group2', lower: 'group2', description: null, }, }, { group: { id: 3, name: 'Group3', lower: 'group3', description: null, }, }, ], }); }); test('Get groups with users + custom', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTable).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); await db.insert(groupsTable).values([ { id: 1, name: 'Group1' }, { id: 2, name: 'Group2' }, { id: 3, name: 'Group3' }, ]); await db.insert(usersToGroupsTable).values([ { userId: 1, groupId: 1 }, { userId: 2, groupId: 2 }, { userId: 3, groupId: 3 }, { userId: 3, groupId: 2 }, ]); const response = await db.query.groupsTable.findMany({ extras: (table, { sql }) => ({ lower: sql`lower(${table.name})`.as('lower_name'), }), with: { usersToGroups: { columns: {}, with: { user: { extras: (table, { sql }) => ({ lower: sql`lower(${table.name})`.as('lower_name'), }), }, }, }, }, }); expectTypeOf(response).toEqualTypeOf< { id: number; name: string; description: string | null; lower: string; usersToGroups: { user: { id: number; name: string; verified: boolean; invitedBy: number | null; lower: string; }; }[]; }[] >(); response.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(response.length).toEqual(3); expect(response[0]?.usersToGroups.length).toEqual(1); expect(response[1]?.usersToGroups.length).toEqual(2); expect(response[2]?.usersToGroups.length).toEqual(1); expect(response).toContainEqual({ id: 1, name: 'Group1', lower: 'group1', description: null, usersToGroups: [{ user: { id: 1, name: 'Dan', lower: 'dan', verified: false, invitedBy: null, }, }], }); expect(response).toContainEqual({ id: 2, name: 'Group2', lower: 'group2', description: null, usersToGroups: [{ user: { id: 2, name: 'Andrew', lower: 'andrew', verified: false, invitedBy: null, }, }, { user: { id: 3, name: 'Alex', lower: 'alex', verified: false, invitedBy: null, }, }], }); expect(response).toContainEqual({ id: 3, name: 'Group3', lower: 'group3', description: null, usersToGroups: [{ user: { id: 3, name: 'Alex', lower: 'alex', verified: false, invitedBy: null, }, }], }); }); test('[Find Many] Get schema users - dbName & tsName match', async (t) => { const { pgjsDb: db } = t; await db.insert(usersV1).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); const schemaUsers = await db.query.usersV1.findMany(); expectTypeOf(schemaUsers).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; }[]>(); schemaUsers.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(schemaUsers.length).eq(3); expect(schemaUsers[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, }); expect(schemaUsers[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, }); expect(schemaUsers[2]).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, }); }); test('[Find Many] Get schema users - dbName & tsName mismatch', async (t) => { const { pgjsDb: db } = t; await db.insert(usersTableV1).values([ { id: 1, name: 'Dan' }, { id: 2, name: 'Andrew' }, { id: 3, name: 'Alex' }, ]); const schemaUsers = await db.query.usersTableV1.findMany(); expectTypeOf(schemaUsers).toEqualTypeOf<{ id: number; name: string; verified: boolean; invitedBy: number | null; }[]>(); schemaUsers.sort((a, b) => (a.id > b.id) ? 1 : -1); expect(schemaUsers.length).eq(3); expect(schemaUsers[0]).toEqual({ id: 1, name: 'Dan', verified: false, invitedBy: null, }); expect(schemaUsers[1]).toEqual({ id: 2, name: 'Andrew', verified: false, invitedBy: null, }); expect(schemaUsers[2]).toEqual({ id: 3, name: 'Alex', verified: false, invitedBy: null, }); }); test('.toSQL()', () => { const query = db.query.usersTable.findFirst().toSQL(); expect(query).toHaveProperty('sql', expect.any(String)); expect(query).toHaveProperty('params', expect.any(Array)); }); // + custom + where + orderby // + custom + where + orderby + limit // + partial // + partial(false) // + partial + orderBy + where (all not selected) /* One four-level relation users+posts+comments+coment_likes + users+users_to_groups+groups */ /* Really hard case 1. users+posts+comments+coment_likes 2. users+users_to_groups+groups 3. users+users */