@payloadcms/db-mongodb 3.48.0-canary.4 → 3.48.0-canary.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/connect.d.ts.map +1 -1
  2. package/dist/connect.js +17 -0
  3. package/dist/connect.js.map +1 -1
  4. package/dist/find.d.ts.map +1 -1
  5. package/dist/find.js +10 -0
  6. package/dist/find.js.map +1 -1
  7. package/dist/findDistinct.d.ts +3 -0
  8. package/dist/findDistinct.d.ts.map +1 -0
  9. package/dist/findDistinct.js +122 -0
  10. package/dist/findDistinct.js.map +1 -0
  11. package/dist/findOne.d.ts.map +1 -1
  12. package/dist/findOne.js +12 -0
  13. package/dist/findOne.js.map +1 -1
  14. package/dist/index.d.ts +32 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +9 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/models/buildSchema.d.ts.map +1 -1
  19. package/dist/models/buildSchema.js +6 -2
  20. package/dist/models/buildSchema.js.map +1 -1
  21. package/dist/queries/buildSortParam.d.ts.map +1 -1
  22. package/dist/queries/buildSortParam.js +35 -13
  23. package/dist/queries/buildSortParam.js.map +1 -1
  24. package/dist/queryDrafts.d.ts.map +1 -1
  25. package/dist/queryDrafts.js +11 -0
  26. package/dist/queryDrafts.js.map +1 -1
  27. package/dist/updateOne.d.ts.map +1 -1
  28. package/dist/updateOne.js +11 -2
  29. package/dist/updateOne.js.map +1 -1
  30. package/dist/utilities/aggregatePaginate.d.ts.map +1 -1
  31. package/dist/utilities/aggregatePaginate.js +4 -2
  32. package/dist/utilities/aggregatePaginate.js.map +1 -1
  33. package/dist/utilities/buildJoinAggregation.d.ts.map +1 -1
  34. package/dist/utilities/buildJoinAggregation.js +3 -0
  35. package/dist/utilities/buildJoinAggregation.js.map +1 -1
  36. package/dist/utilities/compatabilityOptions.d.ts +24 -0
  37. package/dist/utilities/compatabilityOptions.d.ts.map +1 -0
  38. package/dist/utilities/compatabilityOptions.js +24 -0
  39. package/dist/utilities/compatabilityOptions.js.map +1 -0
  40. package/dist/utilities/resolveJoins.d.ts +25 -0
  41. package/dist/utilities/resolveJoins.d.ts.map +1 -0
  42. package/dist/utilities/resolveJoins.js +469 -0
  43. package/dist/utilities/resolveJoins.js.map +1 -0
  44. package/dist/utilities/transform.d.ts +2 -1
  45. package/dist/utilities/transform.d.ts.map +1 -1
  46. package/dist/utilities/transform.js +14 -2
  47. package/dist/utilities/transform.js.map +1 -1
  48. package/package.json +3 -3
@@ -0,0 +1,469 @@
1
+ import { appendVersionToQueryKey, buildVersionCollectionFields, combineQueries, getQueryDraftsSort } from 'payload';
2
+ import { fieldShouldBeLocalized } from 'payload/shared';
3
+ import { buildQuery } from '../queries/buildQuery.js';
4
+ import { buildSortParam } from '../queries/buildSortParam.js';
5
+ import { transform } from './transform.js';
6
+ /**
7
+ * Resolves join relationships for a collection of documents.
8
+ * This function fetches related documents based on join configurations and
9
+ * attaches them to the original documents with pagination support.
10
+ */ export async function resolveJoins({ adapter, collectionSlug, docs, joins, locale, projection, versions = false }) {
11
+ // Early return if no joins are specified or no documents to process
12
+ if (!joins || docs.length === 0) {
13
+ return;
14
+ }
15
+ // Get the collection configuration from the adapter
16
+ const collectionConfig = adapter.payload.collections[collectionSlug]?.config;
17
+ if (!collectionConfig) {
18
+ return;
19
+ }
20
+ // Build a map of join paths to their configurations for quick lookup
21
+ // This flattens the nested join structure into a single map keyed by join path
22
+ const joinMap = {};
23
+ // Add regular joins
24
+ for (const [target, joinList] of Object.entries(collectionConfig.joins)){
25
+ for (const join of joinList){
26
+ joinMap[join.joinPath] = {
27
+ ...join,
28
+ targetCollection: target
29
+ };
30
+ }
31
+ }
32
+ // Add polymorphic joins
33
+ for (const join of collectionConfig.polymorphicJoins || []){
34
+ // For polymorphic joins, we use the collections array as the target
35
+ joinMap[join.joinPath] = {
36
+ ...join,
37
+ targetCollection: join.field.collection
38
+ };
39
+ }
40
+ // Process each requested join concurrently
41
+ const joinPromises = Object.entries(joins).map(async ([joinPath, joinQuery])=>{
42
+ if (!joinQuery) {
43
+ return null;
44
+ }
45
+ // If a projection is provided, and the join path is not in the projection, skip it
46
+ if (projection && !projection[joinPath]) {
47
+ return null;
48
+ }
49
+ // Get the join definition from our map
50
+ const joinDef = joinMap[joinPath];
51
+ if (!joinDef) {
52
+ return null;
53
+ }
54
+ // Normalize collections to always be an array for unified processing
55
+ const allCollections = Array.isArray(joinDef.field.collection) ? joinDef.field.collection : [
56
+ joinDef.field.collection
57
+ ];
58
+ // Use the provided locale or fall back to the default locale for localized fields
59
+ const localizationConfig = adapter.payload.config.localization;
60
+ const effectiveLocale = locale || typeof localizationConfig === 'object' && localizationConfig && localizationConfig.defaultLocale;
61
+ // Extract relationTo filter from the where clause to determine which collections to query
62
+ const relationToFilter = extractRelationToFilter(joinQuery.where || {});
63
+ // Determine which collections to query based on relationTo filter
64
+ const collections = relationToFilter ? allCollections.filter((col)=>relationToFilter.includes(col)) : allCollections;
65
+ // Check if this is a polymorphic collection join (where field.collection is an array)
66
+ const isPolymorphicJoin = Array.isArray(joinDef.field.collection);
67
+ // Apply pagination settings
68
+ const limit = joinQuery.limit ?? joinDef.field.defaultLimit ?? 10;
69
+ const page = joinQuery.page ?? 1;
70
+ const skip = (page - 1) * limit;
71
+ // Process collections concurrently
72
+ const collectionPromises = collections.map(async (joinCollectionSlug)=>{
73
+ const targetConfig = adapter.payload.collections[joinCollectionSlug]?.config;
74
+ if (!targetConfig) {
75
+ return null;
76
+ }
77
+ const useDrafts = versions && Boolean(targetConfig.versions?.drafts);
78
+ let JoinModel;
79
+ if (useDrafts) {
80
+ JoinModel = adapter.versions[targetConfig.slug];
81
+ } else {
82
+ JoinModel = adapter.collections[targetConfig.slug];
83
+ }
84
+ if (!JoinModel) {
85
+ return null;
86
+ }
87
+ // Extract all parent document IDs to use in the join query
88
+ const parentIDs = docs.map((d)=>versions ? d.parent ?? d._id ?? d.id : d._id ?? d.id);
89
+ // Build the base query
90
+ let whereQuery = null;
91
+ whereQuery = isPolymorphicJoin ? filterWhereForCollection(joinQuery.where || {}, targetConfig.flattenedFields, true) : joinQuery.where || {};
92
+ // Skip this collection if the WHERE clause cannot be satisfied for polymorphic collection joins
93
+ if (whereQuery === null) {
94
+ return null;
95
+ }
96
+ whereQuery = useDrafts ? await JoinModel.buildQuery({
97
+ locale,
98
+ payload: adapter.payload,
99
+ where: combineQueries(appendVersionToQueryKey(whereQuery), {
100
+ latest: {
101
+ equals: true
102
+ }
103
+ })
104
+ }) : await buildQuery({
105
+ adapter,
106
+ collectionSlug: joinCollectionSlug,
107
+ fields: targetConfig.flattenedFields,
108
+ locale,
109
+ where: whereQuery
110
+ });
111
+ // Handle localized paths and version prefixes
112
+ let dbFieldName = joinDef.field.on;
113
+ if (effectiveLocale && typeof localizationConfig === 'object' && localizationConfig) {
114
+ const pathSegments = joinDef.field.on.split('.');
115
+ const transformedSegments = [];
116
+ const fields = useDrafts ? buildVersionCollectionFields(adapter.payload.config, targetConfig, true) : targetConfig.flattenedFields;
117
+ for(let i = 0; i < pathSegments.length; i++){
118
+ const segment = pathSegments[i];
119
+ transformedSegments.push(segment);
120
+ // Check if this segment corresponds to a localized field
121
+ const fieldAtSegment = fields.find((f)=>f.name === segment);
122
+ if (fieldAtSegment && fieldAtSegment.localized) {
123
+ transformedSegments.push(effectiveLocale);
124
+ }
125
+ }
126
+ dbFieldName = transformedSegments.join('.');
127
+ }
128
+ // Add version prefix for draft queries
129
+ if (useDrafts) {
130
+ dbFieldName = `version.${dbFieldName}`;
131
+ }
132
+ // Check if the target field is a polymorphic relationship
133
+ const isPolymorphic = joinDef.targetField ? Array.isArray(joinDef.targetField.relationTo) : false;
134
+ if (isPolymorphic) {
135
+ // For polymorphic relationships, we need to match both relationTo and value
136
+ whereQuery[`${dbFieldName}.relationTo`] = collectionSlug;
137
+ whereQuery[`${dbFieldName}.value`] = {
138
+ $in: parentIDs
139
+ };
140
+ } else {
141
+ // For regular relationships and polymorphic collection joins
142
+ whereQuery[dbFieldName] = {
143
+ $in: parentIDs
144
+ };
145
+ }
146
+ // Build the sort parameters for the query
147
+ const fields = useDrafts ? buildVersionCollectionFields(adapter.payload.config, targetConfig, true) : targetConfig.flattenedFields;
148
+ const sort = buildSortParam({
149
+ adapter,
150
+ config: adapter.payload.config,
151
+ fields,
152
+ locale,
153
+ sort: useDrafts ? getQueryDraftsSort({
154
+ collectionConfig: targetConfig,
155
+ sort: joinQuery.sort || joinDef.field.defaultSort || targetConfig.defaultSort
156
+ }) : joinQuery.sort || joinDef.field.defaultSort || targetConfig.defaultSort,
157
+ timestamps: true
158
+ });
159
+ const projection = buildJoinProjection(dbFieldName, useDrafts, sort);
160
+ const [results, dbCount] = await Promise.all([
161
+ JoinModel.find(whereQuery, projection, {
162
+ sort,
163
+ ...isPolymorphicJoin ? {} : {
164
+ limit,
165
+ skip
166
+ }
167
+ }).lean(),
168
+ isPolymorphicJoin ? Promise.resolve(0) : JoinModel.countDocuments(whereQuery)
169
+ ]);
170
+ const count = isPolymorphicJoin ? results.length : dbCount;
171
+ transform({
172
+ adapter,
173
+ data: results,
174
+ fields: useDrafts ? buildVersionCollectionFields(adapter.payload.config, targetConfig, false) : targetConfig.fields,
175
+ operation: 'read'
176
+ });
177
+ // Return results with collection info for grouping
178
+ return {
179
+ collectionSlug: joinCollectionSlug,
180
+ count,
181
+ dbFieldName,
182
+ results,
183
+ sort,
184
+ useDrafts
185
+ };
186
+ });
187
+ const collectionResults = await Promise.all(collectionPromises);
188
+ // Group the results by parent ID
189
+ const grouped = {};
190
+ let totalCount = 0;
191
+ for (const collectionResult of collectionResults){
192
+ if (!collectionResult) {
193
+ continue;
194
+ }
195
+ const { collectionSlug, count, dbFieldName, results, sort, useDrafts } = collectionResult;
196
+ totalCount += count;
197
+ for (const result of results){
198
+ if (useDrafts) {
199
+ result.id = result.parent;
200
+ }
201
+ const parentValues = getByPathWithArrays(result, dbFieldName);
202
+ if (parentValues.length === 0) {
203
+ continue;
204
+ }
205
+ for (let parentValue of parentValues){
206
+ if (!parentValue) {
207
+ continue;
208
+ }
209
+ if (typeof parentValue === 'object') {
210
+ parentValue = parentValue.value;
211
+ }
212
+ const joinData = {
213
+ relationTo: collectionSlug,
214
+ value: result.id
215
+ };
216
+ const parentKey = parentValue;
217
+ if (!grouped[parentKey]) {
218
+ grouped[parentKey] = {
219
+ docs: [],
220
+ sort
221
+ };
222
+ }
223
+ // Always store the ObjectID reference in polymorphic format
224
+ grouped[parentKey].docs.push({
225
+ ...result,
226
+ __joinData: joinData
227
+ });
228
+ }
229
+ }
230
+ }
231
+ for (const results of Object.values(grouped)){
232
+ results.docs.sort((a, b)=>{
233
+ for (const [fieldName, sortOrder] of Object.entries(results.sort)){
234
+ const sort = sortOrder === 'asc' ? 1 : -1;
235
+ const aValue = a[fieldName];
236
+ const bValue = b[fieldName];
237
+ if (aValue < bValue) {
238
+ return -1 * sort;
239
+ }
240
+ if (aValue > bValue) {
241
+ return 1 * sort;
242
+ }
243
+ }
244
+ return 0;
245
+ });
246
+ results.docs = results.docs.map((doc)=>isPolymorphicJoin ? doc.__joinData : doc.id);
247
+ }
248
+ // Determine if the join field should be localized
249
+ const localeSuffix = fieldShouldBeLocalized({
250
+ field: joinDef.field,
251
+ parentIsLocalized: joinDef.parentIsLocalized
252
+ }) && adapter.payload.config.localization && effectiveLocale ? `.${effectiveLocale}` : '';
253
+ // Adjust the join path with locale suffix if needed
254
+ const localizedJoinPath = `${joinPath}${localeSuffix}`;
255
+ return {
256
+ grouped,
257
+ isPolymorphicJoin,
258
+ joinQuery,
259
+ limit,
260
+ localizedJoinPath,
261
+ page,
262
+ skip,
263
+ totalCount
264
+ };
265
+ });
266
+ // Wait for all join operations to complete
267
+ const joinResults = await Promise.all(joinPromises);
268
+ // Process the results and attach them to documents
269
+ for (const joinResult of joinResults){
270
+ if (!joinResult) {
271
+ continue;
272
+ }
273
+ const { grouped, isPolymorphicJoin, joinQuery, limit, localizedJoinPath, skip, totalCount } = joinResult;
274
+ // Attach the joined data to each parent document
275
+ for (const doc of docs){
276
+ const id = versions ? doc.parent ?? doc._id ?? doc.id : doc._id ?? doc.id;
277
+ const all = grouped[id]?.docs || [];
278
+ // Calculate the slice for pagination
279
+ // When limit is 0, it means unlimited - return all results
280
+ const slice = isPolymorphicJoin ? limit === 0 ? all : all.slice(skip, skip + limit) : all;
281
+ // Create the join result object with pagination metadata
282
+ const value = {
283
+ docs: slice,
284
+ hasNextPage: limit === 0 ? false : totalCount > skip + slice.length
285
+ };
286
+ // Include total count if requested
287
+ if (joinQuery.count) {
288
+ value.totalDocs = totalCount;
289
+ }
290
+ // Navigate to the correct nested location in the document and set the join data
291
+ // This handles nested join paths like "user.posts" by creating intermediate objects
292
+ const segments = localizedJoinPath.split('.');
293
+ let ref;
294
+ if (versions) {
295
+ if (!doc.version) {
296
+ doc.version = {};
297
+ }
298
+ ref = doc.version;
299
+ } else {
300
+ ref = doc;
301
+ }
302
+ for(let i = 0; i < segments.length - 1; i++){
303
+ const seg = segments[i];
304
+ if (!ref[seg]) {
305
+ ref[seg] = {};
306
+ }
307
+ ref = ref[seg];
308
+ }
309
+ // Set the final join data at the target path
310
+ ref[segments[segments.length - 1]] = value;
311
+ }
312
+ }
313
+ }
314
+ /**
315
+ * Extracts relationTo filter values from a WHERE clause
316
+ * @param where - The WHERE clause to search
317
+ * @returns Array of collection slugs if relationTo filter found, null otherwise
318
+ */ function extractRelationToFilter(where) {
319
+ if (!where || typeof where !== 'object') {
320
+ return null;
321
+ }
322
+ // Check for direct relationTo conditions
323
+ if (where.relationTo && typeof where.relationTo === 'object') {
324
+ const relationTo = where.relationTo;
325
+ if (relationTo.in && Array.isArray(relationTo.in)) {
326
+ return relationTo.in;
327
+ }
328
+ if (relationTo.equals) {
329
+ return [
330
+ relationTo.equals
331
+ ];
332
+ }
333
+ }
334
+ // Check for relationTo in logical operators
335
+ if (where.and && Array.isArray(where.and)) {
336
+ for (const condition of where.and){
337
+ const result = extractRelationToFilter(condition);
338
+ if (result) {
339
+ return result;
340
+ }
341
+ }
342
+ }
343
+ if (where.or && Array.isArray(where.or)) {
344
+ for (const condition of where.or){
345
+ const result = extractRelationToFilter(condition);
346
+ if (result) {
347
+ return result;
348
+ }
349
+ }
350
+ }
351
+ return null;
352
+ }
353
+ /**
354
+ * Filters a WHERE clause to only include fields that exist in the target collection
355
+ * This is needed for polymorphic joins where different collections have different fields
356
+ * @param where - The original WHERE clause
357
+ * @param availableFields - The fields available in the target collection
358
+ * @param excludeRelationTo - Whether to exclude relationTo field (for individual collections)
359
+ * @returns A filtered WHERE clause, or null if the query cannot match this collection
360
+ */ function filterWhereForCollection(where, availableFields, excludeRelationTo = false) {
361
+ if (!where || typeof where !== 'object') {
362
+ return where;
363
+ }
364
+ const fieldNames = new Set(availableFields.map((f)=>f.name));
365
+ // Add special fields that are available in polymorphic relationships
366
+ if (!excludeRelationTo) {
367
+ fieldNames.add('relationTo');
368
+ }
369
+ const filtered = {};
370
+ for (const [key, value] of Object.entries(where)){
371
+ if (key === 'and') {
372
+ // Handle AND operator - all conditions must be satisfiable
373
+ if (Array.isArray(value)) {
374
+ const filteredConditions = [];
375
+ for (const condition of value){
376
+ const filteredCondition = filterWhereForCollection(condition, availableFields, excludeRelationTo);
377
+ // If any condition in AND cannot be satisfied, the whole AND fails
378
+ if (filteredCondition === null) {
379
+ return null;
380
+ }
381
+ if (Object.keys(filteredCondition).length > 0) {
382
+ filteredConditions.push(filteredCondition);
383
+ }
384
+ }
385
+ if (filteredConditions.length > 0) {
386
+ filtered[key] = filteredConditions;
387
+ }
388
+ }
389
+ } else if (key === 'or') {
390
+ // Handle OR operator - at least one condition must be satisfiable
391
+ if (Array.isArray(value)) {
392
+ const filteredConditions = value.map((condition)=>filterWhereForCollection(condition, availableFields, excludeRelationTo)).filter((condition)=>condition !== null && Object.keys(condition).length > 0);
393
+ if (filteredConditions.length > 0) {
394
+ filtered[key] = filteredConditions;
395
+ }
396
+ // If no OR conditions can be satisfied, we still continue (OR is more permissive)
397
+ }
398
+ } else if (key === 'relationTo' && excludeRelationTo) {
399
+ continue;
400
+ } else if (fieldNames.has(key)) {
401
+ // Include the condition if the field exists in this collection
402
+ filtered[key] = value;
403
+ } else {
404
+ // Field doesn't exist in this collection - this makes the query unsatisfiable
405
+ return null;
406
+ }
407
+ }
408
+ return filtered;
409
+ }
410
+ /**
411
+ * Builds projection for join queries
412
+ */ function buildJoinProjection(baseFieldName, useDrafts, sort) {
413
+ const projection = {
414
+ _id: 1,
415
+ [baseFieldName]: 1
416
+ };
417
+ if (useDrafts) {
418
+ projection.parent = 1;
419
+ }
420
+ for (const fieldName of Object.keys(sort)){
421
+ projection[fieldName] = 1;
422
+ }
423
+ return projection;
424
+ }
425
+ /**
426
+ * Enhanced utility function to safely traverse nested object properties using dot notation
427
+ * Handles arrays by searching through array elements for matching values
428
+ * @param doc - The document to traverse
429
+ * @param path - Dot-separated path (e.g., "array.category")
430
+ * @returns Array of values found at the specified path (for arrays) or single value
431
+ */ function getByPathWithArrays(doc, path) {
432
+ const segments = path.split('.');
433
+ let current = doc;
434
+ for(let i = 0; i < segments.length; i++){
435
+ const segment = segments[i];
436
+ if (current === undefined || current === null) {
437
+ return [];
438
+ }
439
+ // Get the value at the current segment
440
+ const value = current[segment];
441
+ if (value === undefined || value === null) {
442
+ return [];
443
+ }
444
+ // If this is the last segment, return the value(s)
445
+ if (i === segments.length - 1) {
446
+ return Array.isArray(value) ? value : [
447
+ value
448
+ ];
449
+ }
450
+ // If the value is an array and we have more segments to traverse
451
+ if (Array.isArray(value)) {
452
+ const remainingPath = segments.slice(i + 1).join('.');
453
+ const results = [];
454
+ // Search through each array element
455
+ for (const item of value){
456
+ if (item && typeof item === 'object') {
457
+ const subResults = getByPathWithArrays(item, remainingPath);
458
+ results.push(...subResults);
459
+ }
460
+ }
461
+ return results;
462
+ }
463
+ // Continue traversing
464
+ current = value;
465
+ }
466
+ return [];
467
+ }
468
+
469
+ //# sourceMappingURL=resolveJoins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/resolveJoins.ts"],"sourcesContent":["import type { JoinQuery, SanitizedJoins, Where } from 'payload'\n\nimport {\n appendVersionToQueryKey,\n buildVersionCollectionFields,\n combineQueries,\n getQueryDraftsSort,\n} from 'payload'\nimport { fieldShouldBeLocalized } from 'payload/shared'\n\nimport type { MongooseAdapter } from '../index.js'\n\nimport { buildQuery } from '../queries/buildQuery.js'\nimport { buildSortParam } from '../queries/buildSortParam.js'\nimport { transform } from './transform.js'\n\nexport type ResolveJoinsArgs = {\n /** The MongoDB adapter instance */\n adapter: MongooseAdapter\n /** The slug of the collection being queried */\n collectionSlug: string\n /** Array of documents to resolve joins for */\n docs: Record<string, unknown>[]\n /** Join query specifications (which joins to resolve and how) */\n joins?: JoinQuery\n /** Optional locale for localized queries */\n locale?: string\n /** Optional projection for the join query */\n projection?: Record<string, true>\n /** Whether to resolve versions instead of published documents */\n versions?: boolean\n}\n\n/**\n * Resolves join relationships for a collection of documents.\n * This function fetches related documents based on join configurations and\n * attaches them to the original documents with pagination support.\n */\nexport async function resolveJoins({\n adapter,\n collectionSlug,\n docs,\n joins,\n locale,\n projection,\n versions = false,\n}: ResolveJoinsArgs): Promise<void> {\n // Early return if no joins are specified or no documents to process\n if (!joins || docs.length === 0) {\n return\n }\n\n // Get the collection configuration from the adapter\n const collectionConfig = adapter.payload.collections[collectionSlug]?.config\n if (!collectionConfig) {\n return\n }\n\n // Build a map of join paths to their configurations for quick lookup\n // This flattens the nested join structure into a single map keyed by join path\n const joinMap: Record<string, { targetCollection: string } & SanitizedJoin> = {}\n\n // Add regular joins\n for (const [target, joinList] of Object.entries(collectionConfig.joins)) {\n for (const join of joinList) {\n joinMap[join.joinPath] = { ...join, targetCollection: target }\n }\n }\n\n // Add polymorphic joins\n for (const join of collectionConfig.polymorphicJoins || []) {\n // For polymorphic joins, we use the collections array as the target\n joinMap[join.joinPath] = { ...join, targetCollection: join.field.collection as string }\n }\n\n // Process each requested join concurrently\n const joinPromises = Object.entries(joins).map(async ([joinPath, joinQuery]) => {\n if (!joinQuery) {\n return null\n }\n\n // If a projection is provided, and the join path is not in the projection, skip it\n if (projection && !projection[joinPath]) {\n return null\n }\n\n // Get the join definition from our map\n const joinDef = joinMap[joinPath]\n if (!joinDef) {\n return null\n }\n\n // Normalize collections to always be an array for unified processing\n const allCollections = Array.isArray(joinDef.field.collection)\n ? joinDef.field.collection\n : [joinDef.field.collection]\n\n // Use the provided locale or fall back to the default locale for localized fields\n const localizationConfig = adapter.payload.config.localization\n const effectiveLocale =\n locale ||\n (typeof localizationConfig === 'object' &&\n localizationConfig &&\n localizationConfig.defaultLocale)\n\n // Extract relationTo filter from the where clause to determine which collections to query\n const relationToFilter = extractRelationToFilter(joinQuery.where || {})\n\n // Determine which collections to query based on relationTo filter\n const collections = relationToFilter\n ? allCollections.filter((col) => relationToFilter.includes(col))\n : allCollections\n\n // Check if this is a polymorphic collection join (where field.collection is an array)\n const isPolymorphicJoin = Array.isArray(joinDef.field.collection)\n\n // Apply pagination settings\n const limit = joinQuery.limit ?? joinDef.field.defaultLimit ?? 10\n const page = joinQuery.page ?? 1\n const skip = (page - 1) * limit\n\n // Process collections concurrently\n const collectionPromises = collections.map(async (joinCollectionSlug) => {\n const targetConfig = adapter.payload.collections[joinCollectionSlug]?.config\n if (!targetConfig) {\n return null\n }\n\n const useDrafts = versions && Boolean(targetConfig.versions?.drafts)\n let JoinModel\n if (useDrafts) {\n JoinModel = adapter.versions[targetConfig.slug]\n } else {\n JoinModel = adapter.collections[targetConfig.slug]\n }\n\n if (!JoinModel) {\n return null\n }\n\n // Extract all parent document IDs to use in the join query\n const parentIDs = docs.map((d) => (versions ? (d.parent ?? d._id ?? d.id) : (d._id ?? d.id)))\n\n // Build the base query\n let whereQuery: null | Record<string, unknown> = null\n whereQuery = isPolymorphicJoin\n ? filterWhereForCollection(\n joinQuery.where || {},\n targetConfig.flattenedFields,\n true, // exclude relationTo for individual collections\n )\n : joinQuery.where || {}\n\n // Skip this collection if the WHERE clause cannot be satisfied for polymorphic collection joins\n if (whereQuery === null) {\n return null\n }\n whereQuery = useDrafts\n ? await JoinModel.buildQuery({\n locale,\n payload: adapter.payload,\n where: combineQueries(appendVersionToQueryKey(whereQuery as Where), {\n latest: {\n equals: true,\n },\n }),\n })\n : await buildQuery({\n adapter,\n collectionSlug: joinCollectionSlug,\n fields: targetConfig.flattenedFields,\n locale,\n where: whereQuery as Where,\n })\n\n // Handle localized paths and version prefixes\n let dbFieldName = joinDef.field.on\n\n if (effectiveLocale && typeof localizationConfig === 'object' && localizationConfig) {\n const pathSegments = joinDef.field.on.split('.')\n const transformedSegments: string[] = []\n const fields = useDrafts\n ? buildVersionCollectionFields(adapter.payload.config, targetConfig, true)\n : targetConfig.flattenedFields\n\n for (let i = 0; i < pathSegments.length; i++) {\n const segment = pathSegments[i]!\n transformedSegments.push(segment)\n\n // Check if this segment corresponds to a localized field\n const fieldAtSegment = fields.find((f) => f.name === segment)\n if (fieldAtSegment && fieldAtSegment.localized) {\n transformedSegments.push(effectiveLocale)\n }\n }\n\n dbFieldName = transformedSegments.join('.')\n }\n\n // Add version prefix for draft queries\n if (useDrafts) {\n dbFieldName = `version.${dbFieldName}`\n }\n\n // Check if the target field is a polymorphic relationship\n const isPolymorphic = joinDef.targetField\n ? Array.isArray(joinDef.targetField.relationTo)\n : false\n\n if (isPolymorphic) {\n // For polymorphic relationships, we need to match both relationTo and value\n whereQuery[`${dbFieldName}.relationTo`] = collectionSlug\n whereQuery[`${dbFieldName}.value`] = { $in: parentIDs }\n } else {\n // For regular relationships and polymorphic collection joins\n whereQuery[dbFieldName] = { $in: parentIDs }\n }\n\n // Build the sort parameters for the query\n const fields = useDrafts\n ? buildVersionCollectionFields(adapter.payload.config, targetConfig, true)\n : targetConfig.flattenedFields\n\n const sort = buildSortParam({\n adapter,\n config: adapter.payload.config,\n fields,\n locale,\n sort: useDrafts\n ? getQueryDraftsSort({\n collectionConfig: targetConfig,\n sort: joinQuery.sort || joinDef.field.defaultSort || targetConfig.defaultSort,\n })\n : joinQuery.sort || joinDef.field.defaultSort || targetConfig.defaultSort,\n timestamps: true,\n })\n\n const projection = buildJoinProjection(dbFieldName, useDrafts, sort)\n\n const [results, dbCount] = await Promise.all([\n JoinModel.find(whereQuery, projection, {\n sort,\n ...(isPolymorphicJoin ? {} : { limit, skip }),\n }).lean(),\n isPolymorphicJoin ? Promise.resolve(0) : JoinModel.countDocuments(whereQuery),\n ])\n\n const count = isPolymorphicJoin ? results.length : dbCount\n\n transform({\n adapter,\n data: results,\n fields: useDrafts\n ? buildVersionCollectionFields(adapter.payload.config, targetConfig, false)\n : targetConfig.fields,\n operation: 'read',\n })\n\n // Return results with collection info for grouping\n return {\n collectionSlug: joinCollectionSlug,\n count,\n dbFieldName,\n results,\n sort,\n useDrafts,\n }\n })\n\n const collectionResults = await Promise.all(collectionPromises)\n\n // Group the results by parent ID\n const grouped: Record<\n string,\n {\n docs: Record<string, unknown>[]\n sort: Record<string, string>\n }\n > = {}\n\n let totalCount = 0\n for (const collectionResult of collectionResults) {\n if (!collectionResult) {\n continue\n }\n\n const { collectionSlug, count, dbFieldName, results, sort, useDrafts } = collectionResult\n\n totalCount += count\n\n for (const result of results) {\n if (useDrafts) {\n result.id = result.parent\n }\n\n const parentValues = getByPathWithArrays(result, dbFieldName) as (\n | { relationTo: string; value: number | string }\n | number\n | string\n )[]\n\n if (parentValues.length === 0) {\n continue\n }\n\n for (let parentValue of parentValues) {\n if (!parentValue) {\n continue\n }\n\n if (typeof parentValue === 'object') {\n parentValue = parentValue.value\n }\n\n const joinData = {\n relationTo: collectionSlug,\n value: result.id,\n }\n\n const parentKey = parentValue as string\n if (!grouped[parentKey]) {\n grouped[parentKey] = {\n docs: [],\n sort,\n }\n }\n\n // Always store the ObjectID reference in polymorphic format\n grouped[parentKey].docs.push({\n ...result,\n __joinData: joinData,\n })\n }\n }\n }\n\n for (const results of Object.values(grouped)) {\n results.docs.sort((a, b) => {\n for (const [fieldName, sortOrder] of Object.entries(results.sort)) {\n const sort = sortOrder === 'asc' ? 1 : -1\n const aValue = a[fieldName] as Date | number | string\n const bValue = b[fieldName] as Date | number | string\n if (aValue < bValue) {\n return -1 * sort\n }\n if (aValue > bValue) {\n return 1 * sort\n }\n }\n return 0\n })\n results.docs = results.docs.map(\n (doc) => (isPolymorphicJoin ? doc.__joinData : doc.id) as Record<string, unknown>,\n )\n }\n\n // Determine if the join field should be localized\n const localeSuffix =\n fieldShouldBeLocalized({\n field: joinDef.field,\n parentIsLocalized: joinDef.parentIsLocalized,\n }) &&\n adapter.payload.config.localization &&\n effectiveLocale\n ? `.${effectiveLocale}`\n : ''\n\n // Adjust the join path with locale suffix if needed\n const localizedJoinPath = `${joinPath}${localeSuffix}`\n\n return {\n grouped,\n isPolymorphicJoin,\n joinQuery,\n limit,\n localizedJoinPath,\n page,\n skip,\n totalCount,\n }\n })\n\n // Wait for all join operations to complete\n const joinResults = await Promise.all(joinPromises)\n\n // Process the results and attach them to documents\n for (const joinResult of joinResults) {\n if (!joinResult) {\n continue\n }\n\n const { grouped, isPolymorphicJoin, joinQuery, limit, localizedJoinPath, skip, totalCount } =\n joinResult\n\n // Attach the joined data to each parent document\n for (const doc of docs) {\n const id = (versions ? (doc.parent ?? doc._id ?? doc.id) : (doc._id ?? doc.id)) as string\n const all = grouped[id]?.docs || []\n\n // Calculate the slice for pagination\n // When limit is 0, it means unlimited - return all results\n const slice = isPolymorphicJoin\n ? limit === 0\n ? all\n : all.slice(skip, skip + limit)\n : // For non-polymorphic joins, we assume that page and limit were applied at the database level\n all\n\n // Create the join result object with pagination metadata\n const value: Record<string, unknown> = {\n docs: slice,\n hasNextPage: limit === 0 ? false : totalCount > skip + slice.length,\n }\n\n // Include total count if requested\n if (joinQuery.count) {\n value.totalDocs = totalCount\n }\n\n // Navigate to the correct nested location in the document and set the join data\n // This handles nested join paths like \"user.posts\" by creating intermediate objects\n const segments = localizedJoinPath.split('.')\n let ref: Record<string, unknown>\n if (versions) {\n if (!doc.version) {\n doc.version = {}\n }\n ref = doc.version as Record<string, unknown>\n } else {\n ref = doc\n }\n\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]!\n if (!ref[seg]) {\n ref[seg] = {}\n }\n ref = ref[seg] as Record<string, unknown>\n }\n // Set the final join data at the target path\n ref[segments[segments.length - 1]!] = value\n }\n }\n}\n\n/**\n * Extracts relationTo filter values from a WHERE clause\n * @param where - The WHERE clause to search\n * @returns Array of collection slugs if relationTo filter found, null otherwise\n */\nfunction extractRelationToFilter(where: Record<string, unknown>): null | string[] {\n if (!where || typeof where !== 'object') {\n return null\n }\n\n // Check for direct relationTo conditions\n if (where.relationTo && typeof where.relationTo === 'object') {\n const relationTo = where.relationTo as Record<string, unknown>\n if (relationTo.in && Array.isArray(relationTo.in)) {\n return relationTo.in as string[]\n }\n if (relationTo.equals) {\n return [relationTo.equals as string]\n }\n }\n\n // Check for relationTo in logical operators\n if (where.and && Array.isArray(where.and)) {\n for (const condition of where.and) {\n const result = extractRelationToFilter(condition)\n if (result) {\n return result\n }\n }\n }\n\n if (where.or && Array.isArray(where.or)) {\n for (const condition of where.or) {\n const result = extractRelationToFilter(condition)\n if (result) {\n return result\n }\n }\n }\n\n return null\n}\n\n/**\n * Filters a WHERE clause to only include fields that exist in the target collection\n * This is needed for polymorphic joins where different collections have different fields\n * @param where - The original WHERE clause\n * @param availableFields - The fields available in the target collection\n * @param excludeRelationTo - Whether to exclude relationTo field (for individual collections)\n * @returns A filtered WHERE clause, or null if the query cannot match this collection\n */\nfunction filterWhereForCollection(\n where: Record<string, unknown>,\n availableFields: Array<{ name: string }>,\n excludeRelationTo: boolean = false,\n): null | Record<string, unknown> {\n if (!where || typeof where !== 'object') {\n return where\n }\n\n const fieldNames = new Set(availableFields.map((f) => f.name))\n // Add special fields that are available in polymorphic relationships\n if (!excludeRelationTo) {\n fieldNames.add('relationTo')\n }\n\n const filtered: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(where)) {\n if (key === 'and') {\n // Handle AND operator - all conditions must be satisfiable\n if (Array.isArray(value)) {\n const filteredConditions: Record<string, unknown>[] = []\n\n for (const condition of value) {\n const filteredCondition = filterWhereForCollection(\n condition,\n availableFields,\n excludeRelationTo,\n )\n\n // If any condition in AND cannot be satisfied, the whole AND fails\n if (filteredCondition === null) {\n return null\n }\n\n if (Object.keys(filteredCondition).length > 0) {\n filteredConditions.push(filteredCondition)\n }\n }\n\n if (filteredConditions.length > 0) {\n filtered[key] = filteredConditions\n }\n }\n } else if (key === 'or') {\n // Handle OR operator - at least one condition must be satisfiable\n if (Array.isArray(value)) {\n const filteredConditions = value\n .map((condition) =>\n filterWhereForCollection(condition, availableFields, excludeRelationTo),\n )\n .filter((condition) => condition !== null && Object.keys(condition).length > 0)\n\n if (filteredConditions.length > 0) {\n filtered[key] = filteredConditions\n }\n // If no OR conditions can be satisfied, we still continue (OR is more permissive)\n }\n } else if (key === 'relationTo' && excludeRelationTo) {\n // Skip relationTo field for non-polymorphic collections\n continue\n } else if (fieldNames.has(key)) {\n // Include the condition if the field exists in this collection\n filtered[key] = value\n } else {\n // Field doesn't exist in this collection - this makes the query unsatisfiable\n return null\n }\n }\n\n return filtered\n}\n\ntype SanitizedJoin = SanitizedJoins[string][number]\n\n/**\n * Builds projection for join queries\n */\nfunction buildJoinProjection(\n baseFieldName: string,\n useDrafts: boolean,\n sort: Record<string, string>,\n): Record<string, 1> {\n const projection: Record<string, 1> = {\n _id: 1,\n [baseFieldName]: 1,\n }\n\n if (useDrafts) {\n projection.parent = 1\n }\n\n for (const fieldName of Object.keys(sort)) {\n projection[fieldName] = 1\n }\n\n return projection\n}\n\n/**\n * Enhanced utility function to safely traverse nested object properties using dot notation\n * Handles arrays by searching through array elements for matching values\n * @param doc - The document to traverse\n * @param path - Dot-separated path (e.g., \"array.category\")\n * @returns Array of values found at the specified path (for arrays) or single value\n */\nfunction getByPathWithArrays(doc: unknown, path: string): unknown[] {\n const segments = path.split('.')\n let current = doc\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]!\n\n if (current === undefined || current === null) {\n return []\n }\n\n // Get the value at the current segment\n const value = (current as Record<string, unknown>)[segment]\n\n if (value === undefined || value === null) {\n return []\n }\n\n // If this is the last segment, return the value(s)\n if (i === segments.length - 1) {\n return Array.isArray(value) ? value : [value]\n }\n\n // If the value is an array and we have more segments to traverse\n if (Array.isArray(value)) {\n const remainingPath = segments.slice(i + 1).join('.')\n const results: unknown[] = []\n\n // Search through each array element\n for (const item of value) {\n if (item && typeof item === 'object') {\n const subResults = getByPathWithArrays(item, remainingPath)\n results.push(...subResults)\n }\n }\n\n return results\n }\n\n // Continue traversing\n current = value\n }\n\n return []\n}\n"],"names":["appendVersionToQueryKey","buildVersionCollectionFields","combineQueries","getQueryDraftsSort","fieldShouldBeLocalized","buildQuery","buildSortParam","transform","resolveJoins","adapter","collectionSlug","docs","joins","locale","projection","versions","length","collectionConfig","payload","collections","config","joinMap","target","joinList","Object","entries","join","joinPath","targetCollection","polymorphicJoins","field","collection","joinPromises","map","joinQuery","joinDef","allCollections","Array","isArray","localizationConfig","localization","effectiveLocale","defaultLocale","relationToFilter","extractRelationToFilter","where","filter","col","includes","isPolymorphicJoin","limit","defaultLimit","page","skip","collectionPromises","joinCollectionSlug","targetConfig","useDrafts","Boolean","drafts","JoinModel","slug","parentIDs","d","parent","_id","id","whereQuery","filterWhereForCollection","flattenedFields","latest","equals","fields","dbFieldName","on","pathSegments","split","transformedSegments","i","segment","push","fieldAtSegment","find","f","name","localized","isPolymorphic","targetField","relationTo","$in","sort","defaultSort","timestamps","buildJoinProjection","results","dbCount","Promise","all","lean","resolve","countDocuments","count","data","operation","collectionResults","grouped","totalCount","collectionResult","result","parentValues","getByPathWithArrays","parentValue","value","joinData","parentKey","__joinData","values","a","b","fieldName","sortOrder","aValue","bValue","doc","localeSuffix","parentIsLocalized","localizedJoinPath","joinResults","joinResult","slice","hasNextPage","totalDocs","segments","ref","version","seg","in","and","condition","or","availableFields","excludeRelationTo","fieldNames","Set","add","filtered","key","filteredConditions","filteredCondition","keys","has","baseFieldName","path","current","undefined","remainingPath","item","subResults"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import type { Field } from 'payload';
2
2
  import type { MongooseAdapter } from '../index.js';
3
3
  type Args = {
4
+ $inc?: Record<string, number>;
4
5
  /** instance of the adapter */
5
6
  adapter: MongooseAdapter;
6
7
  /** data to transform, can be an array of documents or a single document */
@@ -22,6 +23,6 @@ type Args = {
22
23
  */
23
24
  validateRelationships?: boolean;
24
25
  };
25
- export declare const transform: ({ adapter, data, fields, globalSlug, operation, parentIsLocalized, validateRelationships, }: Args) => void;
26
+ export declare const transform: ({ $inc, adapter, data, fields, globalSlug, operation, parentIsLocalized, validateRelationships, }: Args) => void;
26
27
  export {};
27
28
  //# sourceMappingURL=transform.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/utilities/transform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,KAAK,EAQN,MAAM,SAAS,CAAA;AAMhB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAgMlD,KAAK,IAAI,GAAG;IACV,8BAA8B;IAC9B,OAAO,EAAE,eAAe,CAAA;IACxB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IACzD,uCAAuC;IACvC,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,SAAS,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC,CAAA;AAuKD,eAAO,MAAM,SAAS,gGAQnB,IAAI,SAwHN,CAAA"}
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/utilities/transform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,KAAK,EAQN,MAAM,SAAS,CAAA;AAMhB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAgMlD,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,8BAA8B;IAC9B,OAAO,EAAE,eAAe,CAAA;IACxB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IACzD,uCAAuC;IACvC,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,SAAS,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC,CAAA;AAuKD,eAAO,MAAM,SAAS,sGASnB,IAAI,SA2IN,CAAA"}
@@ -261,10 +261,11 @@ const stripFields = ({ config, data, fields, reservedKeys = [] })=>{
261
261
  }
262
262
  }
263
263
  };
264
- export const transform = ({ adapter, data, fields, globalSlug, operation, parentIsLocalized = false, validateRelationships = true })=>{
264
+ export const transform = ({ $inc, adapter, data, fields, globalSlug, operation, parentIsLocalized = false, validateRelationships = true })=>{
265
265
  if (Array.isArray(data)) {
266
266
  for (const item of data){
267
267
  transform({
268
+ $inc,
268
269
  adapter,
269
270
  data: item,
270
271
  fields,
@@ -283,6 +284,10 @@ export const transform = ({ adapter, data, fields, globalSlug, operation, parent
283
284
  if (data.id instanceof Types.ObjectId) {
284
285
  data.id = data.id.toHexString();
285
286
  }
287
+ // Handle BigInt conversion for custom ID fields of type 'number'
288
+ if (adapter.useBigIntForNumberIDs && typeof data.id === 'bigint') {
289
+ data.id = Number(data.id);
290
+ }
286
291
  if (!adapter.allowAdditionalKeys) {
287
292
  stripFields({
288
293
  config,
@@ -301,11 +306,18 @@ export const transform = ({ adapter, data, fields, globalSlug, operation, parent
301
306
  if (operation === 'write' && globalSlug) {
302
307
  data.globalType = globalSlug;
303
308
  }
304
- const sanitize = ({ field, ref: incomingRef })=>{
309
+ const sanitize = ({ field, parentPath, ref: incomingRef })=>{
305
310
  if (!incomingRef || typeof incomingRef !== 'object') {
306
311
  return;
307
312
  }
308
313
  const ref = incomingRef;
314
+ if ($inc && field.type === 'number' && operation === 'write' && field.name in ref && ref[field.name]) {
315
+ const value = ref[field.name];
316
+ if (value && typeof value === 'object' && '$inc' in value && typeof value.$inc === 'number') {
317
+ $inc[`${parentPath}${field.name}`] = value.$inc;
318
+ delete ref[field.name];
319
+ }
320
+ }
309
321
  if (field.type === 'date' && operation === 'read' && field.name in ref && ref[field.name]) {
310
322
  if (config.localization && fieldShouldBeLocalized({
311
323
  field,