fix(medusa): Order by using joins and pagination (#5990)

This commit is contained in:
Adrien de Peretti
2024-01-07 13:58:30 +01:00
committed by GitHub
parent b57ad67d2f
commit 47be2ad723
4 changed files with 80 additions and 11 deletions

View File

@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---
fix(medusa): Ordering management using joins and pagination

View File

@@ -23,7 +23,7 @@ const adminReqConfig = {
},
}
jest.setTimeout(50000)
jest.setTimeout(60000)
describe("sales channels", () => {
let medusaProcess

View File

@@ -74,7 +74,7 @@ export const ProductRepository = dataSource.getRepository(Product).extend({
optionsWithoutRelations?.where?.discount_condition_id
delete optionsWithoutRelations?.where?.discount_condition_id
return queryEntityWithoutRelations<Product>({
return await queryEntityWithoutRelations<Product>({
repository: this,
optionsWithoutRelations,
shouldCount,

View File

@@ -150,11 +150,7 @@ export async function queryEntityWithoutRelations<T extends ObjectLiteral>({
}): Promise<[T[], number]> {
const alias = repository.metadata.name.toLowerCase()
const qb = repository
.createQueryBuilder(alias)
.select([`${alias}.id`])
.skip(optionsWithoutRelations.skip)
.take(optionsWithoutRelations.take)
const qb = repository.createQueryBuilder(alias).select([`${alias}.id`])
if (optionsWithoutRelations.where) {
qb.where(optionsWithoutRelations.where)
@@ -189,14 +185,82 @@ export async function queryEntityWithoutRelations<T extends ObjectLiteral>({
qb.withDeleted()
}
/*
* Deduplicate tuples for join + ordering (e.g. variants.prices.amount) since typeorm doesnt
* know how to manage it by itself
*/
const expressionMapAllOrderBys = qb.expressionMap.allOrderBys
if (
expressionMapAllOrderBys &&
Object.keys(expressionMapAllOrderBys).length
) {
const orderBysString = Object.keys(expressionMapAllOrderBys)
.map((column) => {
return `${column} ${expressionMapAllOrderBys[column]}`
})
.join(", ")
qb.addSelect(
`row_number() OVER (PARTITION BY ${alias}.id ORDER BY ${orderBysString}) AS rownum`
)
} else {
qb.addSelect(`1 AS rownum`)
}
/*
* In typeorm SelectQueryBuilder, the orderBy is removed from the original query when there is pagination
* and join involved together.
*
* This workaround allows us to include the order as part of the original query (including joins) before
* selecting the distinct ids of the main alias entity. The distinct ids deduplication
* is managed by the rownum column added to the select below.
*
* see: node_modules/typeorm/query-builder/SelectQueryBuilder.js(1973)
*/
const outerQb = new SelectQueryBuilder(qb.connection, (qb as any).queryRunner)
.select(`${qb.escape(`${alias}_id`)}`)
.from(`(${qb.getQuery()})`, alias)
.where(`${alias}.rownum = 1`)
.setParameters(qb.getParameters())
.setNativeParameters(qb.expressionMap.nativeParameters)
.offset(optionsWithoutRelations.skip)
.limit(optionsWithoutRelations.take)
const mapToEntities = (array: any) => {
return array.map((rawProduct) => ({
id: rawProduct[`${alias}_id`],
})) as unknown as T[]
}
let entities: T[]
let count = 0
if (shouldCount) {
const result = await promiseAll([qb.getMany(), qb.getCount()])
entities = result[0]
count = result[1]
const outerQbCount = new SelectQueryBuilder(
qb.connection,
(qb as any).queryRunner
)
.select(`COUNT(1)`, `count`)
.from(`(${qb.getQuery()})`, alias)
.where(`${alias}.rownum = 1`)
.setParameters(qb.getParameters())
.setNativeParameters(qb.expressionMap.nativeParameters)
.orderBy()
.groupBy()
.offset(undefined)
.limit(undefined)
.skip(undefined)
.take(undefined)
const result = await promiseAll([
outerQb.getRawMany(),
outerQbCount.getRawOne(),
])
entities = mapToEntities(result[0])
count = Number(result[1].count)
} else {
entities = await qb.getMany()
const result = await outerQb.getRawMany()
entities = mapToEntities(result)
}
return [entities, count]