将记录加入到范围内的最佳方法是什么?

问题描述 投票:0回答:1

我正在Ruby on Rails应用程序上工作(尽管这实际上更多是一个数据结构问题),其中我有PostsBooksChapters作为模型。假设希望能够引用Post中的多个章节,并且以后能够根据他们引用的章节和书籍来过滤帖子。将这些记录连接在一起的最佳方式是什么,以便以后可以轻松查询?

我首先想到的是典型的has_many :through关联。

class Post < ApplicationRecord
  has_many :post_chapters
  has_many :chapters, through: :post_chapters
end

class PostChapter < ApplicationRecord
  belongs_to :post
  belongs_to :chapter
end

class Chapter < ApplicationRecord
  belongs_to :book
  has_many :post_chapters
  has_many :posts, through: :post_chapters
end

class Book < ApplicationRecord
  has_many :chapters
end

如果我只需要存储对几章的引用,这将很好地工作。对于每篇章节的引用,我都会得到一个额外的PostChapter记录。但是,如果有人引用第1-1000章会怎样?然后,该应用程序将需要创建1000条记录,以便能够确定参考文献中是否包含第X章。

是否有一种方法可以将它存储为某种范围连接,而该范围仅存储第一章和最后一章,但是以后仍然很容易查询?

如果有帮助,我正在使用PostgreSQL。

ruby-on-rails join range data-modeling has-many-through
1个回答
1
投票

正如@beartech指出的那样,您对数据库大小的担心可能完全没有根据,这很可能只是过早优化的情况。

但是要回答实际的问题,有几种方法可以在Postgres中存储范围。第一种“经典的”多语言方式是使用两列,然后使用之间:

Post.where("? BETWEEN posts.starting_chaper AND posts.ending_chapter", 99)

由于这只是普通的SQL,它将在任何关系数据库上都可以使用。

Postgres也有native range types范围(双关语意:):>

  • int4range-整数范围
  • int8range — bigint的范围
  • numrange-数字范围
  • tsrange-不带时区的时间戳范围
  • tstzrange-带有时区的时间戳范围
  • daterange-日期范围
  • 而且这些只是内置类型。

ActiveRecord盒外并没有真正支持本地范围,但是您可以使用Rails 5中引入的属性API来处理类型转换。

class Chapter < ApplicationRecord
  attribute :page_range, range: true
end

这里的巨大优势之一是查询,因为PG知道此列实际上是一个范围,并且与以前的解决方案相比,可以创建非常有效的查询计划。

在这里使用JSON或数组类型非常可疑,因为您失去了关系模型的所有好处,而没有range列的好处。如果一个模型有多个范围,我将创建一个单独的联接表。

class Post < ApplicationRecord
  has_many :post_chapters
  has_many :chapter_ranges
  has_many :chapters, through: :post_chapters
end

class ChapterRange
  belongs_to :post
  attribute :chapters, range: true
end

# Check if one chapter is contained in range:
Post.joins(:chapter_ranges)
    .where("? @> chapter_ranges.chapters" 10) 

# range is contained by
Post.joins(:chapter_ranges)
    .where("int4range(?, ?) @> chapter_ranges.chapters" 2, 4) 

# overlap
Post.joins(:chapter_ranges)
    .where("int4range(?, ?) && chapter_ranges.chapters" 2, 4) 

-1
投票

如果您担心章节的范围,则可以通过在帖子中存储范围的开始和结束来进一步优化。

© www.soinside.com 2019 - 2024. All rights reserved.