创建 link table 以连接同一模型的一对一关系
Creating link table to connect one to one relationship of same model
我想确保 table join 与 joining table 具有很强的一对一关系。以下是例子
运行 生成器
bundle exec rails g model Page name:string content:text
bundle exec rails g model PageAssociation left_page_id:integer right_page_id:integer
在 运行 这些生成器之后,我必须按照
更改我的 page
模型
class Page < ApplicationRecord
has_one :left_page_association, :foreign_key => :left_page_id,
:class_name => 'PageAssociation'
has_one :right_page_association, :foreign_key => :right_page_id,
:class_name => 'PageAssociation'
has_one :left_page, :through => :left_page_association,
:source => :right_page
has_one :right_page, :through => :right_page_association,
:source => :left_page
end
在我的协会table模特page_association
class PageAssociation < ApplicationRecord
belongs_to :left_page, class_name: 'Page'
belongs_to :right_page, class_name: 'Page'
end
现在使用 rails c
我可以执行以下操作
page_one = Page.create(name: 'first page', content: 'first page content')
page_two = Page.create(name: 'second page', content: 'second page content')
PageAssociation.create(left_page: page_one, right_page: page_two)
page_one.left_page
page_two.right_page
一切正常,正在返回页面。但我也可以这样做
page_three = Page.create(name: 'third page', content: 'third page content')
PageAssociation.create(left_page: page_one, right_page: page_three)
当然,由于 has_one
,它仍然显示在关系上,但它正在创建另一个关系。所以我想关注
- 只能创建一个关联,不能再创建一个
- 因为我们有数百万条记录,所以最好的方法是什么
- 是不是一个用page.left_page?或者是否有任何其他优化方法来做同样的事情。
- 我应该使用以下迁移行添加 indexing 吗?影响性能吗
add_foreign_key :page_associations, :pages, column: :left_page_id
add_foreign_key :page_associations, :pages, column: :right_page_id
我更改了迁移以使列值唯一,因此当我创建另一个具有相同 page.id 的 PageAssociate 时它现在会出错,但这是正确的方法吗?
t.integer :left_page_id, null: false, index: { unique: true }
t.integer :right_page_id, null: false, index: { unique: true }
我在解决什么问题
所以我有书 table 我有多本书,但有些书互相借页。所以假设我有 book-A 和 book-B,所以我想显示 book-a 与 book-b 的关系。如果它们是相关的,那么我将创建链接到 book-b 第 20 页的 book-a 第 10 页的另一个关系,因此当我单击 book-a 进行同步时,它将带来我书中 book-b 第 20 页的所有更改-一种。
以上是我连接两本书的第一步。我知道使用自连接和密钥解决它是最好的,但我不能这样做,因为我们有大量记录,所以这是不可能的。所以我用上面的方法来做到这一点。但是我确实在模型中添加了对数据库和验证的唯一约束来解决它。但我觉得这仍然不是一个好方法。
稍后我会做以下
main-book , main-book-page, secondary-book-page
这将允许我将后面的页面相互装订。书名和页数是虚构的,实际实体是不同的。
这里你想要的只是一个普通的多对多table设置:
class Book < ApplicationRecord
has_many :book_pages
has_many :pages, through: :book_pages
end
class Page < ApplicationRecord
has_many :book_pages
has_many :books, through: :book_pages
end
class BookPage < ApplicationRecord
belongs_to :book
belongs_to :page
validates_uniqueness_of :book_id, scope: :page_id
end
这种情况下的唯一性可以通过添加唯一索引来保证:
add_index :book_pages, [:book_id, :page_id]
为什么?
M2M join table 有两个任意分配的外键的设置不是一个很好的设计,并且不能很好地与 ActiveRecord 一起工作,因为您无法定义与 [=14= 的关联] 条款。
每个关联只能有一个外键。这意味着您不能将其视为同质集合,也不能急切加载它。
这意味着您需要像这样编写蹩脚的联接,而不是能够使用适当的关联:
Book.joins(
"LEFT JOIN pages_assocations pa ON pa.left_page_id = books.id OR pa.left_page_id = books.id"
)
而且创建间接关联的时候也要写蒸堆
虽然每个 book/page 组合只有一行的 table 设置似乎在开始时需要更多行,但它也更加灵活,因为您可以通过子查询映射书籍之间的关联,横向连接或分组并计算匹配数。
class Book < ApplicationRecord
has_many :book_pages
has_many :pages, through: :book_pages
def books_with_pages_in_common
Book.where(
id: BookPage.select(:book_id)
.where(page_id: pages)
)
end
end
我想确保 table join 与 joining table 具有很强的一对一关系。以下是例子 运行 生成器
bundle exec rails g model Page name:string content:text
bundle exec rails g model PageAssociation left_page_id:integer right_page_id:integer
在 运行 这些生成器之后,我必须按照
更改我的page
模型
class Page < ApplicationRecord
has_one :left_page_association, :foreign_key => :left_page_id,
:class_name => 'PageAssociation'
has_one :right_page_association, :foreign_key => :right_page_id,
:class_name => 'PageAssociation'
has_one :left_page, :through => :left_page_association,
:source => :right_page
has_one :right_page, :through => :right_page_association,
:source => :left_page
end
在我的协会table模特page_association
class PageAssociation < ApplicationRecord
belongs_to :left_page, class_name: 'Page'
belongs_to :right_page, class_name: 'Page'
end
现在使用 rails c
我可以执行以下操作
page_one = Page.create(name: 'first page', content: 'first page content')
page_two = Page.create(name: 'second page', content: 'second page content')
PageAssociation.create(left_page: page_one, right_page: page_two)
page_one.left_page
page_two.right_page
一切正常,正在返回页面。但我也可以这样做
page_three = Page.create(name: 'third page', content: 'third page content')
PageAssociation.create(left_page: page_one, right_page: page_three)
当然,由于 has_one
,它仍然显示在关系上,但它正在创建另一个关系。所以我想关注
- 只能创建一个关联,不能再创建一个
- 因为我们有数百万条记录,所以最好的方法是什么
- 是不是一个用page.left_page?或者是否有任何其他优化方法来做同样的事情。
- 我应该使用以下迁移行添加 indexing 吗?影响性能吗
add_foreign_key :page_associations, :pages, column: :left_page_id
add_foreign_key :page_associations, :pages, column: :right_page_id
我更改了迁移以使列值唯一,因此当我创建另一个具有相同 page.id 的 PageAssociate 时它现在会出错,但这是正确的方法吗?
t.integer :left_page_id, null: false, index: { unique: true }
t.integer :right_page_id, null: false, index: { unique: true }
我在解决什么问题
所以我有书 table 我有多本书,但有些书互相借页。所以假设我有 book-A 和 book-B,所以我想显示 book-a 与 book-b 的关系。如果它们是相关的,那么我将创建链接到 book-b 第 20 页的 book-a 第 10 页的另一个关系,因此当我单击 book-a 进行同步时,它将带来我书中 book-b 第 20 页的所有更改-一种。 以上是我连接两本书的第一步。我知道使用自连接和密钥解决它是最好的,但我不能这样做,因为我们有大量记录,所以这是不可能的。所以我用上面的方法来做到这一点。但是我确实在模型中添加了对数据库和验证的唯一约束来解决它。但我觉得这仍然不是一个好方法。 稍后我会做以下
main-book , main-book-page, secondary-book-page
这将允许我将后面的页面相互装订。书名和页数是虚构的,实际实体是不同的。
这里你想要的只是一个普通的多对多table设置:
class Book < ApplicationRecord
has_many :book_pages
has_many :pages, through: :book_pages
end
class Page < ApplicationRecord
has_many :book_pages
has_many :books, through: :book_pages
end
class BookPage < ApplicationRecord
belongs_to :book
belongs_to :page
validates_uniqueness_of :book_id, scope: :page_id
end
这种情况下的唯一性可以通过添加唯一索引来保证:
add_index :book_pages, [:book_id, :page_id]
为什么?
M2M join table 有两个任意分配的外键的设置不是一个很好的设计,并且不能很好地与 ActiveRecord 一起工作,因为您无法定义与 [=14= 的关联] 条款。
每个关联只能有一个外键。这意味着您不能将其视为同质集合,也不能急切加载它。
这意味着您需要像这样编写蹩脚的联接,而不是能够使用适当的关联:
Book.joins(
"LEFT JOIN pages_assocations pa ON pa.left_page_id = books.id OR pa.left_page_id = books.id"
)
而且创建间接关联的时候也要写蒸堆
虽然每个 book/page 组合只有一行的 table 设置似乎在开始时需要更多行,但它也更加灵活,因为您可以通过子查询映射书籍之间的关联,横向连接或分组并计算匹配数。
class Book < ApplicationRecord
has_many :book_pages
has_many :pages, through: :book_pages
def books_with_pages_in_common
Book.where(
id: BookPage.select(:book_id)
.where(page_id: pages)
)
end
end