feat(medusa): Ordering products on retrieval (#2815)
**What** Move to transformQuery which adds a default ordering and also allows to order the product list from the store API **How** Among other things, fix the product repo to allow ordering by either a key from the product or a key from a relation FIXES CORE-911 FIXES CORE-901
This commit is contained in:
@@ -50,7 +50,7 @@ export function getListConfig<TModel extends BaseEntity>(
|
||||
expand?: string[],
|
||||
limit = 50,
|
||||
offset = 0,
|
||||
order?: { [k: symbol]: "DESC" | "ASC" }
|
||||
order: { [k: string | symbol]: "DESC" | "ASC" } = {}
|
||||
): FindConfig<TModel> {
|
||||
let includeFields: (keyof TModel)[] = []
|
||||
if (isDefined(fields)) {
|
||||
@@ -66,8 +66,10 @@ export function getListConfig<TModel extends BaseEntity>(
|
||||
expandFields = expand
|
||||
}
|
||||
|
||||
const orderBy: Record<string, "DESC" | "ASC"> = order ?? {
|
||||
created_at: "DESC",
|
||||
const orderBy = order
|
||||
|
||||
if (!Object.keys(order).length) {
|
||||
orderBy["created_at"] = "DESC"
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { flatten, groupBy, map, merge } from "lodash"
|
||||
import { Repository, SelectQueryBuilder } from "typeorm"
|
||||
import { EntityMetadata, Repository, SelectQueryBuilder } from "typeorm"
|
||||
import { FindWithoutRelationsOptions } from "../repositories/customer-group"
|
||||
|
||||
// TODO: All the utilities except applyOrdering needs to be re worked depending on the outcome of the product repository
|
||||
|
||||
/**
|
||||
* Custom query entity, it is part of the creation of a custom findWithRelationsAndCount needs.
|
||||
* Allow to query the relations for the specified entity ids
|
||||
@@ -163,3 +165,80 @@ export function mergeEntitiesWithRelations<T>(
|
||||
merge({}, ...entityAndRelations)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the appropriate order depending on the requirements
|
||||
* @param repository
|
||||
* @param order The field on which to apply the order (e.g { "variants.prices.amount": "DESC" })
|
||||
* @param qb
|
||||
* @param alias
|
||||
* @param shouldJoin In case a join is already applied elsewhere and therefore you want to avoid to re joining the data in that case you can return false for specific relations
|
||||
*/
|
||||
export function applyOrdering<T>({
|
||||
repository,
|
||||
order,
|
||||
qb,
|
||||
alias,
|
||||
shouldJoin,
|
||||
}: {
|
||||
repository: Repository<T>
|
||||
order: Record<string, "ASC" | "DESC">
|
||||
qb: SelectQueryBuilder<T>
|
||||
alias: string
|
||||
shouldJoin: (relation: string) => boolean
|
||||
}) {
|
||||
const toSelect: string[] = []
|
||||
|
||||
const parsed = Object.entries(order).reduce(
|
||||
(acc, [orderPath, orderDirection]) => {
|
||||
// If the orderPath (e.g variants.prices.amount) includes a point it means that it is to access
|
||||
// a child relation of an unknown depth
|
||||
if (orderPath.includes(".")) {
|
||||
// We are spliting the path and separating the relations from the property to order. (e.g relations ["variants", "prices"] and property "amount"
|
||||
const relationsToJoin = orderPath.split(".")
|
||||
const propToOrder = relationsToJoin.pop()
|
||||
|
||||
// For each relation we will retrieve the metadata in order to use the right property name from the relation registered in the entity.
|
||||
// Each time we will return the child (i.e the relation) and the inverse metadata (corresponding to the child metadata from the parent point of view)
|
||||
// In order for the next child to know its parent
|
||||
relationsToJoin.reduce(
|
||||
([parent, parentMetadata], child) => {
|
||||
// Find the relation metadata from the parent entity
|
||||
const relationMetadata = (
|
||||
parentMetadata as EntityMetadata
|
||||
).relations.find(
|
||||
(relationMetadata) => relationMetadata.propertyName === child
|
||||
)
|
||||
|
||||
// The consumer can refuse to apply a join on a relation if the join has already been applied before calling this util
|
||||
const shouldApplyJoin = shouldJoin(child)
|
||||
if (shouldApplyJoin) {
|
||||
qb.leftJoin(`${parent}.${relationMetadata!.propertyPath}`, child)
|
||||
}
|
||||
|
||||
// Return the child relation to be the parent for the next one, as well as the metadata corresponding the child in order
|
||||
// to find the next relation metadata for the next child
|
||||
return [child, relationMetadata!.inverseEntityMetadata]
|
||||
},
|
||||
[alias, repository.metadata]
|
||||
)
|
||||
|
||||
// The key for variants.prices.amount will be "prices.amount" since we are ordering on the join added to its parent "variants" in this example
|
||||
const key = `${
|
||||
relationsToJoin[relationsToJoin.length - 1]
|
||||
}.${propToOrder}`
|
||||
acc[key] = orderDirection
|
||||
toSelect.push(key)
|
||||
return acc
|
||||
}
|
||||
|
||||
const key = `${alias}.${orderPath}`
|
||||
toSelect.push(key)
|
||||
acc[key] = orderDirection
|
||||
return acc
|
||||
},
|
||||
{}
|
||||
)
|
||||
qb.addSelect(toSelect)
|
||||
qb.orderBy(parsed)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user