drizzle-team / drizzle-orm

Headless TypeScript ORM with a head. Runs on Node, Bun and Deno. Lives on the Edge and yes, it's a JavaScript ORM too 😅
https://orm.drizzle.team
Apache License 2.0
24.87k stars 664 forks source link

[BUG]: `mapWith` isn't working on `extras` when doing relational queries with `findFirst` or `findMany` #1157

Open Odas0R opened 1 year ago

Odas0R commented 1 year ago

What version of drizzle-orm are you using?

0.28.5

What version of drizzle-kit are you using?

0.19.13

Describe the Bug

extras: {
   polygon: sql`ST_AsGeoJSON(${polygon.geometry})`
   .mapWith(polygon.geometry)
   .as("polygon"),
   centroid: sql`ST_AsGeoJSON(${polygon.centroid})`
   .mapWith(polygon.centroid)
   .as("centroid"),
}

The mapWith isn't executing the fromDriver callback on the custom type:

export const geometry = customType<{ data: Geometry; driverData: string }>({
  dataType() {
    return `geometry(Polygon, 4326)`;
  },

  // BUG: this is not being executed when doing `mapWith`
  fromDriver(value: string): Geometry { 
    try {
      if (typeof value === "string") {
        return JSON.parse(value);
      }
      return value;
    } catch {
      throw new Error(`Invalid geometry: ${value}`);
    }
  },
});

Expected behavior

It should parse the value received from the driver, as for now I fix the result with custom hacks.

Environment & setup

NODE: 16.19.1 POP_os: 22.04

Odas0R commented 1 year ago

Just figured out that if I cast it to json, there's a JSON.parse being done? Confused, maybe this might not be a bug.

// This works
extras: {
   polygon: sql`ST_AsGeoJSON(${polygon.geometry})::json`
   .mapWith(polygon.geometry)
   .as("polygon"),
   centroid: sql`ST_AsGeoJSON(${polygon.centroid})::json`
   .mapWith(polygon.centroid)
   .as("centroid"),
}

Still the fromDriver is not being executed on the example above? I'm a bit confused.

luxaritas commented 1 year ago

I'm noticing that decoders on sql statements that are part of subqueries aren't being executed - I'm wondering if that's related?

andersoncardoso commented 1 year ago

I think I have the same issue. For example, a program table that has many(student):

const res = await db.query.program.findMany({
    extras: {
        studentsCount: sql<number>`(select count(*) from student s where s.program_id = program.id)`
            .mapWith(Number)
            .as("students_count")
        }
});

studentsCount is always returning as a string.

davlet61 commented 4 months ago

I am experiencing the same, where mapWith is not doing anything when used with Query API:

    const user = await db.query.users.findFirst({
      extras: (user, { sql }) => ({
        displayCorrections: sql<boolean>`COALESCE(JSON_VALUE(${user.settings}, '$.displayCorrections'), 0)`
          .mapWith({ mapFromDriverValue: (value) => value === 1 })
          .as('displayCorrections'),
        dashboardLayout: sql<object>`JSON_VALUE(${user.settings}, '$.dashboardLayout')`.as('dashboardLayout'),
      }),
       where: (user, { eq }) => eq(user.username, username),
      })

Also, I couldn't get it to work when the value is NULL, so I had to COALESCE to default to false.