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

fix(index): Add type casting to raw order by #10899

Merged
merged 8 commits into from
Jan 15, 2025
5 changes: 5 additions & 0 deletions .changeset/nice-tigers-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/index": patch
---

fix(index): cast order by
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ export const schema = `
}

type Price @Listeners(values: ["price.created", "price.updated", "price.deleted"]) {
amount: Int
amount: Float
}
`
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import {
ModuleRegistrationName,
Modules,
} from "@medusajs/framework/utils"
import { initDb, TestDatabaseUtils } from "@medusajs/test-utils"
import { EntityManager } from "@mikro-orm/postgresql"
import { IndexData, IndexRelation } from "@models"
import { asValue } from "awilix"
import { initDb, TestDatabaseUtils } from "@medusajs/test-utils"
import path from "path"
import { EventBusServiceMock } from "../__fixtures__"
import { dbName } from "../__fixtures__/medusa-config"
Expand Down Expand Up @@ -343,6 +343,116 @@ describe("IndexModuleService query", function () {
])
})

it("should query all products ordered by price", async () => {
const { data } = await module.query({
fields: ["product.*", "product.variants.*", "product.variants.prices.*"],
pagination: {
order: {
product: {
variants: {
prices: {
amount: "DESC",
},
},
},
},
},
})

expect(data).toEqual([
{
id: "prod_1",
variants: [
{
id: "var_1",
sku: "aaa test aaa",
prices: [
{
id: "money_amount_1",
amount: 100,
},
],
},
{
id: "var_2",
sku: "sku 123",
prices: [
{
id: "money_amount_2",
amount: 10,
},
],
},
],
},
{
id: "prod_2",
title: "Product 2 title",
deep: {
a: 1,
obj: {
b: 15,
},
},
variants: [],
},
])

const { data: dataAsc } = await module.query({
fields: ["product.*", "product.variants.*", "product.variants.prices.*"],
pagination: {
order: {
product: {
variants: {
prices: {
amount: "ASC",
},
},
},
},
},
})

expect(dataAsc).toEqual([
{
id: "prod_2",
title: "Product 2 title",
deep: {
a: 1,
obj: {
b: 15,
},
},
variants: [],
},
{
id: "prod_1",
variants: [
{
id: "var_2",
sku: "sku 123",
prices: [
{
id: "money_amount_2",
amount: 10,
},
],
},
{
id: "var_1",
sku: "aaa test aaa",
prices: [
{
id: "money_amount_1",
amount: 100,
},
],
},
],
},
])
})

it("should query products filtering by variant sku", async () => {
const { data } = await module.query({
fields: ["product.*", "product.variants.*", "product.variants.prices.*"],
Expand Down
38 changes: 31 additions & 7 deletions packages/modules/index/src/utils/query-builder.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Knex } from "@mikro-orm/knex"
import { IndexTypes } from "@medusajs/framework/types"
import { GraphQLUtils, isObject, isString } from "@medusajs/framework/utils"
import { Knex } from "@mikro-orm/knex"
import { OrderBy, QueryFormat, QueryOptions, Select } from "@types"
import { isObject, isString, GraphQLUtils } from "@medusajs/framework/utils"

export const OPERATOR_MAP = {
$eq: "=",
Expand Down Expand Up @@ -108,14 +108,34 @@ export class QueryBuilder {
"": "",
}

const defaultValues = {
Int: "0",
Float: "0",
Boolean: "false",
Date: "1970-01-01 00:00:00",
Time: "00:00:00",
"": "",
}

const fullPath = [path, ...field]
const prop = fullPath.pop()
const fieldPath = fullPath.join(".")
let graphqlType = this.getGraphQLType(fieldPath, prop)
const isList = graphqlType.endsWith("[]")
graphqlType = graphqlType.replace("[]", "")

return (graphqlToPostgresTypeMap[graphqlType] ?? "") + (isList ? "[]" : "")
const cast =
(graphqlToPostgresTypeMap[graphqlType] ?? "") + (isList ? "[]" : "")

function generateCoalesceExpression(field) {
const defaultValue = defaultValues[graphqlType]
return `COALESCE(${field}, '${defaultValue}')${cast}`
}

return {
cast,
coalesce: generateCoalesceExpression,
}
}

private parseWhere(
Expand Down Expand Up @@ -188,7 +208,7 @@ export class QueryBuilder {
field,
value[subKey]
)
const castType = this.getPostgresCastType(attr, field)
const castType = this.getPostgresCastType(attr, [field]).cast

const val = operator === "IN" ? subValue : [subValue]
if (operator === "=" && subValue === null) {
Expand Down Expand Up @@ -219,7 +239,7 @@ export class QueryBuilder {

value = this.transformValueToType(attr, field, value)
if (Array.isArray(value)) {
const castType = this.getPostgresCastType(attr, field)
const castType = this.getPostgresCastType(attr, field).cast
const inPlaceholders = value.map(() => "?").join(",")
builder.whereRaw(
`(${aliasMapping[attr]}.data${nested}->>?)${castType} IN (${inPlaceholders})`,
Expand All @@ -237,7 +257,7 @@ export class QueryBuilder {
)}'::jsonb`
)
} else {
const castType = this.getPostgresCastType(attr, field)
const castType = this.getPostgresCastType(attr, field).cast
builder.whereRaw(
`(${aliasMapping[attr]}.data${nested}->>?)${castType} ${operator} ?`,
[...field, value]
Expand Down Expand Up @@ -496,10 +516,14 @@ export class QueryBuilder {
const path = aliasPath.split(".")
const field = path.pop()
const attr = path.join(".")

const pgType = this.getPostgresCastType(attr, [field])
const alias = aliasMapping[attr]
const direction = orderBy[aliasPath]

queryBuilder.orderByRaw(`${alias}.data->>'${field}' ${direction}`)
queryBuilder.orderByRaw(
pgType.coalesce(`${alias}.data->>'${field}'`) + " " + direction
)
}

let sql = `WITH data AS (${queryBuilder.toQuery()})
Expand Down
Loading