Ecto 中的双向关系和多态关联
Bidirectional relationships and polymorphic associations in Ecto
我有一个名为 :issues
的 table,我需要建立一个方法来 link 两个问题。我想创建一个名为 :links
的新 table,但由于我必须遵循的规则,我在设计 Ecto 模式时遇到了一些问题:
1. Links should be two-way and be any of these types: ["blocked-by" | "blocking" | "relates-to"]
2. If Issue-A is blocked-by Issue-B, Issue-B should be blocking Issue-A
3. If Issue-A relates-to Issue-B, Issue-B should relates-to Issue-A
4. Creating a link on one issue should create a link on the linked issue, following rules 1 and 2.
更复杂的是其他 table 的存在 :stories
、:notes
、:milestones
因为我应该能够 link 记录来自 table 的记录来自另一个 table 或来自相同 table 且具有上述相同规则。
我还考虑过为每种 link 创建一个 table。例如,Issue
的更新架构将变成这样:
defmodule MyApplication.Issue do
use Ecto.Schema
@primary_key false
schema "issues" do
field(:issue_id, Ecto.UUID, primary_key: true)
has_many(:linked_issues, LinkedIssue)
has_many(:linked_stories, LinkedStory)
has_many(:linked_notes, LinkedNote)
has_many(:linked_milestones, LinkedMilestone)
end
end
我必须为 Story
、Note
和 Milestone
做同样的事情。我只是对如何设置迁移和引用感到困惑。我也不确定连接 tables 如何知道哪个 table 正在 linking 它们,以及我将如何按照上面的第四条规则建立双向关系。
双向链接可以像 2 个单向链接一样简单地建模,例如。为了在这种情况下强制执行数据完整性,您将不得不依赖应用程序级逻辑或数据库触发器(后者更可靠但维护成本更高)。
你在这里需要的多态性是一个有点棘手的问题。 Ecto 不像 Rails' ActiveRecord(和类似框架)那样支持多态关联。故意的,因为它是有代价的:破坏 FK 约束(结果是数据完整性较弱)和一些性能损失(乍一看可以忽略不计,但随着数据库的增长而受到伤害)。
Ecto "suggests" 至少有 3 个解决方案:
- 单个
links
table,多个外键
- 多个
<object>_links
table 具有所谓的 Link
本身的抽象模式
- 单个
links
table 与多个多对多连接 tables (issues_links
, milestones_links
, ...)
每个都有很多优点和缺点,一些查询具体等
此处进行了简要说明https://hexdocs.pm/ecto/Ecto.Schema.html#belongs_to/3-polymorphic-associations but I would also suggest referring to https://pragprog.com/book/wmecto/programming-ecto - 本书包含整个章节 (14),对问题进行了详细说明,并提供了上述所有可能的解决方案。假装是我自己的知识,只是复制粘贴到这里是不公平的:)
我有一个名为 :issues
的 table,我需要建立一个方法来 link 两个问题。我想创建一个名为 :links
的新 table,但由于我必须遵循的规则,我在设计 Ecto 模式时遇到了一些问题:
1. Links should be two-way and be any of these types: ["blocked-by" | "blocking" | "relates-to"]
2. If Issue-A is blocked-by Issue-B, Issue-B should be blocking Issue-A
3. If Issue-A relates-to Issue-B, Issue-B should relates-to Issue-A
4. Creating a link on one issue should create a link on the linked issue, following rules 1 and 2.
更复杂的是其他 table 的存在 :stories
、:notes
、:milestones
因为我应该能够 link 记录来自 table 的记录来自另一个 table 或来自相同 table 且具有上述相同规则。
我还考虑过为每种 link 创建一个 table。例如,Issue
的更新架构将变成这样:
defmodule MyApplication.Issue do
use Ecto.Schema
@primary_key false
schema "issues" do
field(:issue_id, Ecto.UUID, primary_key: true)
has_many(:linked_issues, LinkedIssue)
has_many(:linked_stories, LinkedStory)
has_many(:linked_notes, LinkedNote)
has_many(:linked_milestones, LinkedMilestone)
end
end
我必须为 Story
、Note
和 Milestone
做同样的事情。我只是对如何设置迁移和引用感到困惑。我也不确定连接 tables 如何知道哪个 table 正在 linking 它们,以及我将如何按照上面的第四条规则建立双向关系。
双向链接可以像 2 个单向链接一样简单地建模,例如。为了在这种情况下强制执行数据完整性,您将不得不依赖应用程序级逻辑或数据库触发器(后者更可靠但维护成本更高)。
你在这里需要的多态性是一个有点棘手的问题。 Ecto 不像 Rails' ActiveRecord(和类似框架)那样支持多态关联。故意的,因为它是有代价的:破坏 FK 约束(结果是数据完整性较弱)和一些性能损失(乍一看可以忽略不计,但随着数据库的增长而受到伤害)。
Ecto "suggests" 至少有 3 个解决方案:
- 单个
links
table,多个外键 - 多个
<object>_links
table 具有所谓的Link
本身的抽象模式 - 单个
links
table 与多个多对多连接 tables (issues_links
,milestones_links
, ...)
每个都有很多优点和缺点,一些查询具体等
此处进行了简要说明https://hexdocs.pm/ecto/Ecto.Schema.html#belongs_to/3-polymorphic-associations but I would also suggest referring to https://pragprog.com/book/wmecto/programming-ecto - 本书包含整个章节 (14),对问题进行了详细说明,并提供了上述所有可能的解决方案。假装是我自己的知识,只是复制粘贴到这里是不公平的:)