Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Pg-kit] Allow where in with when relation is 1-1 #4011

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

ramipellumbi
Copy link

@ramipellumbi ramipellumbi commented Jan 26, 2025

Closes #3911. Namely, when a relation is 1-many, with allows for use of where for filtering. The typing did not expose this option in a 1-1 relation. A caveat I want feedback on is how to ensure the typing of the with column becomes nullable, which is the behavior of the query when the filter does not match (I believe this is related to #824). Though, realistically, the record should be omitted in the case it does not match as this is very confusing behavior.

Consider the following simple example (hoisting it from the issue which has had 0 interactions):

// schemas.ts
import {
  pgTable,
  varchar,
  timestamp,
  text,
  integer,
  uniqueIndex,
  foreignKey,
  boolean,
  index,
  primaryKey,
} from "drizzle-orm/pg-core";
import { relations, sql } from "drizzle-orm";

export const location = pgTable(
  "location",
  {
    id: text().primaryKey().notNull(),
    name: text().notNull(),
  }
);

export const bedAssignment = pgTable(
  "bed_assignment",
  {
    id: text().primaryKey().notNull(),
    startDate: text("start_date").notNull(),
    endDate: text("end_date"),
    clientId: text("client_id").notNull(),
    bedId: text("bed_id").notNull(),
  },
  (table) => [
    foreignKey({
      columns: [table.clientId],
      foreignColumns: [client.id],
      name: "bed_assignment_client_id_fkey",
    })
      .onUpdate("cascade")
      .onDelete("restrict"),
    foreignKey({
      columns: [table.bedId],
      foreignColumns: [bed.id],
      name: "bed_assignment_bed_id_fkey",
    })
      .onUpdate("cascade")
      .onDelete("restrict"),
  ],
);

export const client = pgTable(
  "client",
  {
    id: text().primaryKey().notNull(),
    locationId: text("location_id"),
  },
  (table) => [
    index("client_id_idx").using("btree", table.id.asc().nullsLast().op("text_ops")),
    foreignKey({
      columns: [table.locationId],
      foreignColumns: [location.id],
      name: "client_location_id_fkey",
    })
      .onUpdate("cascade")
      .onDelete("set null"),
  ],
);

export const bed = pgTable(
  "bed",
  {
    id: text().primaryKey().notNull(),
    locationId: text().notNull(),
  },
  (table) => [
    foreignKey({
      columns: [table.locationId],
      foreignColumns: [location.id],
      name: "bed_locationId_fkey",
    })
      .onUpdate("cascade")
      .onDelete("restrict"),
  ],
);

export const clientRelations = relations(client, ({ one, many }) => ({
  bedAssignments: many(bedAssignment),
  location: one(location, {
    fields: [client.locationId],
    references: [location.id],
  }),
}));

export const locationRelations = relations(location, ({ many }) => ({
  clients: many(client),
  beds: many(bed),
}));

export const bedRelations = relations(bed, ({ one, many }) => ({
  bedAssignments: many(bedAssignment),
  location: one(location, {
    fields: [bed.locationId],
    references: [location.id],
  }),
}));

export const bedAssignmentRelations = relations(bedAssignment, ({ one }) => ({
  client: one(client, {
    fields: [bedAssignment.clientId],
    references: [client.id],
  }),
  bed: one(bed, {
    fields: [bedAssignment.bedId],
    references: [bed.id],
  }),
}));

And suppose I do the following query:

const db = drizzle({
  connection: {
    connectionString: process.env.DATABASE_URL,
    ssl: process.env.NODE_ENV === "production",
  },
  schema: {
    location,
    bedAssignment,
    client,
    bed,
    locationRelations,
    clientRelations,
    bedRelations,
    bedAssignmentRelations,
  },
});

(async () => {
  const result = await db.query.client.findMany({
    with: {
      bedAssignments: {
        with: {
          bed: {
            where: (bed, { eq }) => eq(bed.locationId, "some-location-id"),
            // ^ this should omit bed assignments that have a bed that don't match the location id
          }
        }
      }
    }
  });
})()

But the expected behavior does not happen. Instead, I retrieve bedAssignments that have their bed property as null (but are not typed as such), which is very confusing behavior. A maintainer should take it from here.

When a relation is 1-many, `with` allows for use of `where` for filtering.
The typing did not expose this option in a 1-1 relation, even though it is valid.
When a relation is 1-many, `with` allows for use of `where` for filtering.
The typing did not expose this option in a 1-1 relation, even though it is valid.
@ramipellumbi ramipellumbi force-pushed the rp/where-on-with-in-one-to-one-relation branch from 7ae1e25 to e2b789e Compare January 26, 2025 16:53
…ramipellumbi/drizzle-orm into rp/where-on-with-in-one-to-one-relation
@ramipellumbi ramipellumbi force-pushed the rp/where-on-with-in-one-to-one-relation branch from e2b789e to 71406af Compare January 26, 2025 16:55
@MickL
Copy link

MickL commented Mar 10, 2025

Is there anything blocking this PR? I feel like Drizzle Team is not looking into Github issues / PRs at all

@ramipellumbi
Copy link
Author

Is there anything blocking this PR? I feel like Drizzle Team is not looking into Github issues / PRs at all

This certainly feels like what happens. Releases seem to not focus on functionality fixes. I still use Prisma for anything that matters.

@AlexDaniel
Copy link

See #3974. They seem to be focusing on their roadmap right now.

@esam091
Copy link

esam091 commented Mar 15, 2025

The quick fix that seems to work for me is by using patch-package.

Install it by using npm i -D patch-package

Modify node_modules/drizzle-orm/relations.d.ts and apply the patch in this PR.

Add these two scripts in package.json.
The first one is used to generate the patch.
The second one applies the patch whenever you run npm install.

{
  "scripts": {
    "patch-packages": "patch-package drizzle-orm",
    "postinstall": "patch-package"
  }
}

now run npm run patch-packages, you should see a new file named patches/drizzle-orm+x.y.z.patch, commit that to your repo.

Now the where clause should work for one on one relation.

I'm not sure whether there's a bad side effect caused by this, but everything seems to work fine so far.

@ramipellumbi
Copy link
Author

The quick fix that seems to work for me is by using patch-package.

Install it by using npm i -D patch-package

Modify node_modules/drizzle-orm/relations.d.ts and apply the patch in this PR.

Add these two scripts in package.json.

The first one is used to generate the patch.

The second one applies the patch whenever you run npm install.

{

  "scripts": {

    "patch-packages": "patch-package drizzle-orm",

    "postinstall": "patch-package"

  }

}

now run npm run patch-packages, you should see a new file named patches/drizzle-orm+x.y.z.patch, commit that to your repo.

Now the where clause should work for one on one relation.

I'm not sure whether there's a bad side effect caused by this, but everything seems to work fine so far.

Just be careful with this. In the event the filter does not match, the relation is included with value null rather than omitting the row where the filter does not match. This can lead to bugs if you're not manually handling that case (which isn't obvious with the typing).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG]:Error Typescript for query where in relation (version "drizzle-orm": "^0.38.3")
4 participants