Files
payloadcms/packages/db-mongodb/src/utilities/buildJoinAggregation.ts
Sasha 8af00f2deb fix: join field works on collections with versions enabled (#8715)
- Fixes errors with drizzle when building the schema
https://github.com/payloadcms/payload/issues/8680
- Adds `joins` to `db.queryDrafts` to have them when doing `.find` with
`draft: true`
2024-10-22 11:05:55 -04:00

179 lines
4.8 KiB
TypeScript

import type { PipelineStage } from 'mongoose'
import type { CollectionSlug, JoinQuery, SanitizedCollectionConfig, Where } from 'payload'
import type { MongooseAdapter } from '../index.js'
import { buildSortParam } from '../queries/buildSortParam.js'
type BuildJoinAggregationArgs = {
adapter: MongooseAdapter
collection: CollectionSlug
collectionConfig: SanitizedCollectionConfig
joins: JoinQuery
// the number of docs to get at the top collection level
limit?: number
locale: string
// the where clause for the top collection
query?: Where
/** whether the query is from drafts */
versions?: boolean
}
export const buildJoinAggregation = async ({
adapter,
collection,
collectionConfig,
joins,
limit,
locale,
query,
versions,
}: BuildJoinAggregationArgs): Promise<PipelineStage[] | undefined> => {
if (Object.keys(collectionConfig.joins).length === 0 || joins === false) {
return
}
const joinConfig = adapter.payload.collections[collection].config.joins
const aggregate: PipelineStage[] = [
{
$sort: { createdAt: -1 },
},
]
if (query) {
aggregate.push({
$match: query,
})
}
if (limit) {
aggregate.push({
$limit: limit,
})
}
for (const slug of Object.keys(joinConfig)) {
for (const join of joinConfig[slug]) {
const joinModel = adapter.collections[join.field.collection]
const {
limit: limitJoin = 10,
sort: sortJoin,
where: whereJoin,
} = joins?.[join.schemaPath] || {}
const sort = buildSortParam({
config: adapter.payload.config,
fields: adapter.payload.collections[slug].config.fields,
locale,
sort: sortJoin || collectionConfig.defaultSort,
timestamps: true,
})
const sortProperty = Object.keys(sort)[0]
const sortDirection = sort[sortProperty] === 'asc' ? 1 : -1
const $match = await joinModel.buildQuery({
locale,
payload: adapter.payload,
where: whereJoin,
})
const pipeline: Exclude<PipelineStage, PipelineStage.Merge | PipelineStage.Out>[] = [
{ $match },
{
$sort: { [sortProperty]: sortDirection },
},
]
if (limitJoin > 0) {
pipeline.push({
$limit: limitJoin + 1,
})
}
if (adapter.payload.config.localization && locale === 'all') {
adapter.payload.config.localization.localeCodes.forEach((code) => {
const as = `${versions ? `version.${join.schemaPath}` : join.schemaPath}${code}`
aggregate.push(
{
$lookup: {
as: `${as}.docs`,
foreignField: `${join.field.on}${code}`,
from: slug,
localField: versions ? 'parent' : '_id',
pipeline,
},
},
{
$addFields: {
[`${as}.docs`]: {
$map: {
as: 'doc',
in: '$$doc._id',
input: `$${as}.docs`,
},
}, // Slicing the docs to match the limit
[`${as}.hasNextPage`]: limitJoin
? { $gt: [{ $size: `$${as}.docs` }, limitJoin] }
: false,
// Boolean indicating if more docs than limit
},
},
)
if (limitJoin > 0) {
aggregate.push({
$addFields: {
[`${as}.docs`]: {
$slice: [`$${as}.docs`, limitJoin],
},
},
})
}
})
} else {
const localeSuffix =
join.field.localized && adapter.payload.config.localization && locale ? `.${locale}` : ''
const as = `${versions ? `version.${join.schemaPath}` : join.schemaPath}${localeSuffix}`
aggregate.push(
{
$lookup: {
as: `${as}.docs`,
foreignField: `${join.field.on}${localeSuffix}`,
from: slug,
localField: versions ? 'parent' : '_id',
pipeline,
},
},
{
$addFields: {
[`${as}.docs`]: {
$map: {
as: 'doc',
in: '$$doc._id',
input: `$${as}.docs`,
},
}, // Slicing the docs to match the limit
[`${as}.hasNextPage`]: {
$gt: [{ $size: `$${as}.docs` }, limitJoin || Number.MAX_VALUE],
}, // Boolean indicating if more docs than limit
},
},
)
if (limitJoin > 0) {
aggregate.push({
$addFields: {
[`${as}.docs`]: {
$slice: [`$${as}.docs`, limitJoin],
},
},
})
}
}
}
}
return aggregate
}