Как отмечает @beartech, ваши опасения по поводу размера баз данных могут быть совершенно необоснованными, и это, скорее всего, всего лишь случай преждевременной оптимизации.
Но для ответа на реальный вопрос есть несколько способов хранения колеблется в Postgres. Первый «классический» способ полиглота заключается в использовании двух столбцов, а затем между:
Post.where("? BETWEEN posts.starting_chaper AND posts.ending_chapter", 99)
Так как это просто ваниль SQL, он будет работать с любой реляционной базой данных.
Postgres также имеет диапазон собственных типов диапазонов (каламбур предназначен):
- int4range - диапазон целых чисел
- int8range - диапазон bigint
- numrange - диапазон чисел c
- tsrange - диапазон отметки времени без часового пояса
- tstzrange - диапазон отметки времени с часовым поясом
- daterange - диапазон даты
И это только встроенные типы.
Собственные диапазоны на самом деле не поддерживаются в ActiveRecord, но вы можете использовать API атрибутов, представленный в Rails 5 для обработки типов.
class Chapter < ApplicationRecord
attribute :page_range, range: true
end
Одним из огромных преимуществ здесь является запрос, поскольку PG знает, что этот столбец на самом деле является диапазоном и может создать очень эффективный план запроса по сравнению с предыдущим решением.
Использование JSON или типа массива здесь довольно сомнительно, поскольку вы теряете все преимущества реляционной модели и не получаете ни одного из преимуществ столбца диапазона. Если модель имеет несколько диапазонов, я бы создал отдельную таблицу соединений.
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)