diff --git a/docs/fields/join.mdx b/docs/fields/join.mdx
index 8ee3eea79..151567e9b 100644
--- a/docs/fields/join.mdx
+++ b/docs/fields/join.mdx
@@ -6,8 +6,10 @@ desc: The Join field provides the ability to work on related documents. Learn ho
keywords: join, relationship, junction, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
---
-The Join Field is used to make Relationship and Upload fields available in the opposite direction. With a Join you can edit and view collections
-having reference to a specific collection document. The field itself acts as a virtual field, in that no new data is stored on the collection with a Join
+The Join Field is used to make Relationship and Upload fields available in the opposite direction. With a Join you can
+edit and view collections
+having reference to a specific collection document. The field itself acts as a virtual field, in that no new data is
+stored on the collection with a Join
field. Instead, the Admin UI surfaces the related documents for a better editing experience and is surfaced by Payload's
APIs.
@@ -19,10 +21,10 @@ The Join field is useful in scenarios including:
- Displaying where a document or upload is used in other documents
For the Join field to work, you must have an existing [relationship](./relationship) or [upload](./upload) field in the
@@ -111,9 +113,11 @@ related docs from a new pseudo-junction collection called `categories_posts`. No
third junction collection, and can be surfaced on both Posts and Categories. But, importantly, you could add
additional "context" fields to this shared junction collection.
-For example, on this `categories_posts` collection, in addition to having the `category` and `post` fields, we could add custom "context" fields like `featured` or `spotlight`,
+For example, on this `categories_posts` collection, in addition to having the `category` and `post` fields, we could add
+custom "context" fields like `featured` or `spotlight`,
which would allow you to store additional information directly on relationships.
-The `join` field gives you complete control over any type of relational architecture in Payload, all wrapped up in a powerful Admin UI.
+The `join` field gives you complete control over any type of relational architecture in Payload, all wrapped up in a
+powerful Admin UI.
## Config Options
@@ -126,11 +130,11 @@ The `join` field gives you complete control over any type of relational architec
| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |
| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
-| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
-| **`required`** | Require this field to have a value. |
+| **`defaultLimit`** | The number of documents to return. Set to 0 to return all related documents. |
+| **`defaultSort`** | The field name used to specify the order the joined documents are returned. |
| **`admin`** | Admin-specific configuration. |
-| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
-| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
+| **`custom`** | Extension point for adding custom data (e.g. for plugins). |
+| **`typescriptSchema`** | Override field type generation with providing a JSON schema. |
_\* An asterisk denotes that a property is required._
@@ -150,12 +154,12 @@ object with:
{
"id": "66e3431a3f23e684075aaeb9",
// other fields...
- "category": "66e3431a3f23e684075aae9c",
- },
+ "category": "66e3431a3f23e684075aae9c"
+ }
// { ... }
],
"hasNextPage": false
- },
+ }
// other fields...
}
```
@@ -213,7 +217,8 @@ You can specify as many `joins` parameters as needed for the same or different j
### GraphQL
-The GraphQL API supports the same query options as the local and REST APIs. You can specify the query options for each join field in your query.
+The GraphQL API supports the same query options as the local and REST APIs. You can specify the query options for each
+join field in your query.
Example:
@@ -226,9 +231,9 @@ query {
limit: 5
where: {
author: {
- equals: "66e3431a3f23e684075aaeb9"
- }
+ equals: "66e3431a3f23e684075aaeb9"
}
+ }
) {
docs {
title
diff --git a/packages/db-mongodb/src/utilities/buildJoinAggregation.ts b/packages/db-mongodb/src/utilities/buildJoinAggregation.ts
index 332e09d7c..2ff570a25 100644
--- a/packages/db-mongodb/src/utilities/buildJoinAggregation.ts
+++ b/packages/db-mongodb/src/utilities/buildJoinAggregation.ts
@@ -57,8 +57,8 @@ export const buildJoinAggregation = async ({
const joinModel = adapter.collections[join.field.collection]
const {
- limit: limitJoin = 10,
- sort: sortJoin,
+ limit: limitJoin = join.field.defaultLimit ?? 10,
+ sort: sortJoin = join.field.defaultSort || collectionConfig.defaultSort,
where: whereJoin,
} = joins?.[join.schemaPath] || {}
@@ -66,7 +66,7 @@ export const buildJoinAggregation = async ({
config: adapter.payload.config,
fields: adapter.payload.collections[slug].config.fields,
locale,
- sort: sortJoin || collectionConfig.defaultSort,
+ sort: sortJoin,
timestamps: true,
})
const sortProperty = Object.keys(sort)[0]
diff --git a/packages/drizzle/src/find/traverseFields.ts b/packages/drizzle/src/find/traverseFields.ts
index 8f6440957..558341179 100644
--- a/packages/drizzle/src/find/traverseFields.ts
+++ b/packages/drizzle/src/find/traverseFields.ts
@@ -238,8 +238,8 @@ export const traverseFields = ({
}
const {
- limit: limitArg = 10,
- sort,
+ limit: limitArg = field.defaultLimit ?? 10,
+ sort = field.defaultSort,
where,
} = joinQuery[`${path.replaceAll('_', '.')}${field.name}`] || {}
let limit = limitArg
@@ -285,7 +285,9 @@ export const traverseFields = ({
let columnReferenceToCurrentID: string
if (versions) {
- columnReferenceToCurrentID = `${topLevelTableName.replace('_', '').replace(new RegExp(`${adapter.versionsSuffix}$`), '')}_id`
+ columnReferenceToCurrentID = `${topLevelTableName
+ .replace('_', '')
+ .replace(new RegExp(`${adapter.versionsSuffix}$`), '')}_id`
} else {
columnReferenceToCurrentID = `${topLevelTableName}_id`
}
diff --git a/packages/drizzle/src/transform/read/traverseFields.ts b/packages/drizzle/src/transform/read/traverseFields.ts
index 864eeb82c..4f7feacda 100644
--- a/packages/drizzle/src/transform/read/traverseFields.ts
+++ b/packages/drizzle/src/transform/read/traverseFields.ts
@@ -420,7 +420,8 @@ export const traverseFields = >({
}
if (field.type === 'join') {
- const { limit = 10 } = joinQuery?.[`${fieldPrefix.replaceAll('_', '.')}${field.name}`] || {}
+ const { limit = field.defaultLimit ?? 10 } =
+ joinQuery?.[`${fieldPrefix.replaceAll('_', '.')}${field.name}`] || {}
// raw hasMany results from SQLite
if (typeof fieldData === 'string') {
diff --git a/packages/payload/src/fields/config/types.ts b/packages/payload/src/fields/config/types.ts
index ad71c526a..4e7dd6c57 100644
--- a/packages/payload/src/fields/config/types.ts
+++ b/packages/payload/src/fields/config/types.ts
@@ -121,6 +121,7 @@ import type {
JSONFieldValidation,
PointFieldValidation,
RadioFieldValidation,
+ Sort,
TextareaFieldValidation,
} from '../../index.js'
import type { DocumentPreferences } from '../../preferences/types.js'
@@ -1452,6 +1453,8 @@ export type JoinField = {
* The slug of the collection to relate with.
*/
collection: CollectionSlug
+ defaultLimit?: number
+ defaultSort?: Sort
defaultValue?: never
/**
* This does not need to be set and will be overridden by the relationship field's hasMany property.
diff --git a/test/joins/collections/Categories.ts b/test/joins/collections/Categories.ts
index 22bf85110..6604c13ed 100644
--- a/test/joins/collections/Categories.ts
+++ b/test/joins/collections/Categories.ts
@@ -47,7 +47,10 @@ export const Categories: CollectionConfig = {
label: 'Related Posts',
type: 'join',
collection: postsSlug,
+ defaultSort: '-title',
+ defaultLimit: 5,
on: 'category',
+ maxDepth: 1,
},
{
name: 'hasManyPosts',
diff --git a/test/joins/int.spec.ts b/test/joins/int.spec.ts
index d700379dd..4fea55828 100644
--- a/test/joins/int.spec.ts
+++ b/test/joins/int.spec.ts
@@ -105,15 +105,6 @@ describe('Joins Field', () => {
},
collection: 'categories',
})
- // const sortCategoryWithPosts = await payload.findByID({
- // id: category.id,
- // joins: {
- // 'group.relatedPosts': {
- // sort: 'title',
- // },
- // },
- // collection: 'categories',
- // })
expect(categoryWithPosts.group.relatedPosts.docs).toHaveLength(10)
expect(categoryWithPosts.group.relatedPosts.docs[0]).toHaveProperty('id')
@@ -125,11 +116,12 @@ describe('Joins Field', () => {
const { docs } = await payload.find({
limit: 1,
collection: 'posts',
+ depth: 2,
})
expect(docs[0].category.id).toBeDefined()
expect(docs[0].category.name).toBeDefined()
- expect(docs[0].category.relatedPosts.docs).toHaveLength(10)
+ expect(docs[0].category.relatedPosts.docs).toHaveLength(5) // uses defaultLimit
})
it('should populate relationships in joins with camelCase names', async () => {