连接字段
Join 字段用于在相反方向展示 Relationship 和 Upload 字段的关系。通过 Join 字段,你可以:
- 编辑和查看引用了特定集合文档的其他集合
- 该字段本身是一个虚拟字段,不会在集合中存储新数据
- 在 Admin UI 中展示相关文档以提供更好的编辑体验
- 通过 Payload 的 API 暴露这些关系
Join 字段在以下场景中非常有用:
- 展示某个
Product
的所有Orders
- 查看和编辑属于某个
Category
的Posts
- 处理任何双向关系数据
- 显示文档或上传文件在其他文档中的使用情况


要使 Join 字段正常工作,你必须在要关联的集合中已存在 relationship 或 upload 字段。这将引用相关文档的集合和字段路径。
要添加 Join 字段,在你的 Field Config 中将 type
设置为 join
:
import type { Field } from 'payload'
export const MyJoinField: Field = {
// highlight-start
name: 'relatedPosts',
type: 'join',
collection: 'posts',
on: 'category',
// highlight-end
}
// 另一个集合中的 relationship 字段:
export const MyRelationshipField: Field = {
name: 'category',
type: 'relationship',
relationTo: 'categories',
}
在这个例子中,该字段被定义为当添加到 category
集合时显示相关的 posts
。on
属性用于指定与集合文档关联的字段名称。
使用这个例子,如果你在 Admin UI 或 API 响应中导航到一个 Category,现在你会看到与该 Category 相关的 Posts 被自动填充。这非常强大,可以用来以简单的方式定义各种关系类型。
Join 字段性能极高,在添加深度为 1 或更高之前不会给 API 响应增加额外的查询开销。它适用于所有数据库适配器。在 MongoDB 中,我们使用 聚合 来自动关联相关文档,而在关系型数据库中,我们使用 join 操作。
Join 字段在 DocumentDB 和 Azure Cosmos DB 中不受支持,因为我们内部使用 MongoDB 聚合来查询该字段的数据,而这些数据库对此有限制。这一点未来可能会改变。
数据库建模建议
在设计数据库结构时,你可能会遇到许多需要建立双向关联的场景。但这里有一个重要原则——通常你只需要在一个地方存储关联关系信息。
以文章(Posts)和分类(Categories)为例,在编辑文章时定义它所属的分类是合理的做法。
但通常没有必要同时在分类中存储文章ID列表,原因如下:
- 你希望关联关系有"单一数据源",而不需要担心保持两个数据源的同步
- 如果文章数量达到数百、数千甚至数百万条,你不会希望在一个分类中存储所有这些文章的ID
- 等等
这正是 join
字段的强大之处。通过它,你只需要在 post
上存储 category_id
,当查询分类时,Payload 会自动为你关联相关文章。关联的分类信息仅存储在文章本身,而不会在两边重复存储。然而,join
字段为你提供了双向API和UI的能力。
使用 Join 字段完全掌控数据库架构
对于典型的多态/多对多关系,如果你使用 Postgres 或 SQLite,Payload 会自动创建一个 posts_rels
表作为连接表来存储文档的所有关系。
但如果你希望对数据库架构有更多控制权,这种默认方式可能并不适合你的使用场景。你可能不希望使用 _rels
表,而是更倾向于维护和控制自己的连接表设计。
通过 Join 字段,你可以控制自己的连接表设计,避免 Payload 自动创建 _rels 表。
join
字段可以与_任何_ collection 结合使用。如果你想定义自己的"连接" collection(例如名为 categories_posts
,包含 post_id
和 category_id
列),你可以完全控制该连接表的结构。
你还可以更进一步,利用 categories_posts
collection 的 admin.hidden
属性,将该 collection 从 Admin UI 导航中隐藏。
在关系字段上指定额外字段
join
字段另一个非常强大的用途是能够在关系上定义"上下文"字段。假设你有 Posts(文章)和 Categories(分类),并且在 Posts 和 Categories 集合中都使用了 join 字段来关联一个新的伪连接集合 categories_posts
。现在,这些关系存储在这个第三方连接集合中,并且可以同时在 Posts 和 Categories 上展示。但更重要的是,你可以为这个共享的连接集合添加额外的"上下文"字段。
例如,在这个 categories_posts
集合中,除了拥有 category
和 post
字段外,我们还可以添加自定义的"上下文"字段,如 featured
(精选)或 spotlight
(焦点),这允许你直接在关系上存储额外信息。join
字段让你能够完全控制 Payload 中的任何类型的关系架构,所有这些都封装在一个强大的 Admin UI 中。
配置选项
选项 | 描述 |
---|---|
name * | 作为从数据库检索时使用的属性名称。了解更多 |
collection * | 包含关系字段的集合 slug 或集合 slug 数组。 |
on * | 与集合文档相关联的关系或上传字段名称。对于嵌套路径使用点表示法,如 'myGroup.relationName'。如果 collection 是数组,则此字段必须存在于所有指定集合中 |
orderable | 如果为 true,启用自定义排序,可以通过拖放重新排序关联文档。使用分数索引实现高效重新排序。 |
where | 用于隐藏相关文档的 Where 查询条件。将与请求中指定的任何 where 条件合并。 |
maxDepth | 默认为 1,设置此字段的最大填充深度,无论到达此字段时剩余的深度如何。最大深度。 |
label | 在管理面板中用作字段标签的文本,或包含每种语言键的对象。 |
hooks | 提供字段钩子来控制此字段的逻辑。更多详情。 |
access | 提供字段访问控制,指定用户可以对此字段数据执行的操作。更多详情。 |
defaultLimit | 返回的文档数量。设置为 0 可返回所有相关文档。 |
defaultSort | 用于指定返回关联文档顺序的字段名称。 |
admin | 管理界面特定配置。更多详情。 |
custom | 用于添加自定义数据(例如插件)的扩展点。 |
typescriptSchema | 通过提供 JSON schema 来覆盖字段类型生成。 |
graphQL | 字段的自定义 graphQL 配置。更多详情 |
* 星号表示该属性为必填项。
管理配置选项
你可以使用 admin
配置属性来控制 join 字段的用户体验。支持以下选项:
选项 | 描述 |
---|---|
defaultColumns | 字段名称数组,对应关系表中要显示的列。默认为 collection 配置。 |
allowCreate | 设置为 false 可移除从此字段创建新相关文档的控件。 |
components.Label | 覆盖 Field 组件的默认 Label。更多详情 |
Join 字段数据
当返回包含 join 字段且已填充相关文档的文档时,返回的结构是一个包含以下属性的对象:
docs
相关文档数组,如果达到深度限制则仅包含 IDhasNextPage
布尔值,表示是否有更多文档totalDocs
文档总数,仅在 join 查询中传递count: true
时存在
{
"id": "66e3431a3f23e684075aae9c",
"relatedPosts": {
"docs": [
{
"id": "66e3431a3f23e684075aaeb9",
// 其他字段...
"category": "66e3431a3f23e684075aae9c"
}
// { ... }
],
"hasNextPage": false,
"totalDocs": 10 // 如果传递了 count: true
}
// 其他字段...
}
Join 字段数据(多态)
当一个文档返回时,对于多态 Join 字段(collection
为数组的情况),会填充相关文档。返回的结构是一个包含以下属性的对象:
docs
一个数组,包含relationTo
(文档所属的 collection slug)和value
(文档本身,如果达到深度限制则返回 ID)hasNextPage
布尔值,表示是否有更多文档totalDocs
文档总数,仅在 join 查询中传递了count: true
时存在
{
"id": "66e3431a3f23e684075aae9c",
"relatedPosts": {
"docs": [
{
"relationTo": "posts",
"value": {
"id": "66e3431a3f23e684075aaeb9",
// 其他字段...
"category": "66e3431a3f23e684075aae9c"
}
}
// { ... }
],
"hasNextPage": false,
"totalDocs": 10 // 如果传递了 count: true
}
// 其他字段...
}
查询选项
Join Field 支持自定义查询来筛选、排序和限制返回的相关文档。除了每个 Join Field 特有的查询选项外,你还可以传递 joins: false
来禁用所有 Join Field 的返回。这在不需要相关文档时有助于提升性能。
支持以下查询选项:
Property | Description |
---|---|
limit | 返回的相关文档最大数量,默认为 10。 |
where | 可选的 Where 查询,用于筛选关联文档。将与字段的 where 对象合并。 |
sort | 用于排序相关结果的字符串 |
count | 是否包含相关文档的计数。默认不包含 |
这些选项可应用于 Local API、GraphQL 和 REST API。
Local API
通过在 Local API 中添加 joins
,你可以按字段的 name
为每个 join field 自定义请求。
const result = await payload.find({
collection: 'categories',
where: {
title: {
equals: 'My Category',
},
},
joins: {
relatedPosts: {
limit: 5,
where: {
title: {
equals: 'My Post',
},
},
sort: 'title',
},
},
})
目前,对于包含 collection
数组的 join fields,其关联文档的 Where
查询支持有限,且不支持数组和 blocks 内部的字段。
REST API
REST API 支持与 Local API 相同的查询选项。你可以使用 joins
查询参数,通过字段的 name
为每个关联字段自定义请求。例如,一个获取文档并限制关联文章为5条且按标题排序的 API 调用:
/api/categories/${id}?joins[relatedPosts][limit]=5&joins[relatedPosts][sort]=title
你可以在单个请求中为相同或不同的关联字段指定任意数量的 joins
参数。
GraphQL
GraphQL API 支持与本地 API 和 REST API 相同的查询选项。你可以在查询中为每个关联字段指定查询选项。
示例:
query {
Categories {
docs {
relatedPosts(
sort: "createdAt"
limit: 5
where: { author: { equals: "66e3431a3f23e684075aaeb9" } }
) {
docs {
title
}
hasNextPage
}
}
}
}