fix(medusa): Order by using joins and pagination (#5990)
This commit is contained in:
1 parent
b57ad67d2f
commit
47be2ad723
4 files changed
+80
-11
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(medusa): Ordering management using joins and pagination
|
||||||
@@ -23,7 +23,7 @@ const adminReqConfig = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
jest.setTimeout(50000)
|
jest.setTimeout(60000)
|
||||||
|
|
||||||
describe("sales channels", () => {
|
describe("sales channels", () => {
|
||||||
let medusaProcess
|
let medusaProcess
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export const ProductRepository = dataSource.getRepository(Product).extend({
|
|||||||
optionsWithoutRelations?.where?.discount_condition_id
|
optionsWithoutRelations?.where?.discount_condition_id
|
||||||
delete optionsWithoutRelations?.where?.discount_condition_id
|
delete optionsWithoutRelations?.where?.discount_condition_id
|
||||||
|
|
||||||
return queryEntityWithoutRelations<Product>({
|
return await queryEntityWithoutRelations<Product>({
|
||||||
repository: this,
|
repository: this,
|
||||||
optionsWithoutRelations,
|
optionsWithoutRelations,
|
||||||
shouldCount,
|
shouldCount,
|
||||||
|
|||||||
@@ -150,11 +150,7 @@ export async function queryEntityWithoutRelations<T extends ObjectLiteral>({
|
|||||||
}): Promise<[T[], number]> {
|
}): Promise<[T[], number]> {
|
||||||
const alias = repository.metadata.name.toLowerCase()
|
const alias = repository.metadata.name.toLowerCase()
|
||||||
|
|
||||||
const qb = repository
|
const qb = repository.createQueryBuilder(alias).select([`${alias}.id`])
|
||||||
.createQueryBuilder(alias)
|
|
||||||
.select([`${alias}.id`])
|
|
||||||
.skip(optionsWithoutRelations.skip)
|
|
||||||
.take(optionsWithoutRelations.take)
|
|
||||||
|
|
||||||
if (optionsWithoutRelations.where) {
|
if (optionsWithoutRelations.where) {
|
||||||
qb.where(optionsWithoutRelations.where)
|
qb.where(optionsWithoutRelations.where)
|
||||||
@@ -189,14 +185,82 @@ export async function queryEntityWithoutRelations<T extends ObjectLiteral>({
|
|||||||
qb.withDeleted()
|
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 entities: T[]
|
||||||
let count = 0
|
let count = 0
|
||||||
if (shouldCount) {
|
if (shouldCount) {
|
||||||
const result = await promiseAll([qb.getMany(), qb.getCount()])
|
const outerQbCount = new SelectQueryBuilder(
|
||||||
entities = result[0]
|
qb.connection,
|
||||||
count = result[1]
|
(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 {
|
} else {
|
||||||
entities = await qb.getMany()
|
const result = await outerQb.getRawMany()
|
||||||
|
entities = mapToEntities(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return [entities, count]
|
return [entities, count]
|
||||||
|
|||||||
Reference in new issue
Block a user