Ruby on Rails и NoSQL, добавление полей - PullRequest
4 голосов
/ 26 января 2012

Я просто погружаюсь в Mongodb и MongoID с помощью Rails и нахожу это потрясающим.Одна вещь, которая помогает NoSQL, - это когда я могу добавить дополнительные поля в мою модель без каких-либо дополнительных усилий, когда захочу:

class Page
  include Mongoid::Document
  include Mongoid::MultiParameterAttributes
  field :title, :type => String
  field :body, :type => String
  field :excerpt, :type => String #Added later
  field :location, :type => String #Added later
  field :published_at, :type => Time

  validates :title, :presence => true
  validates :body, :presence => true
  validates :excerpt, :presence => true
end

И это работает отлично, как и должно.Но мой вопрос (извините, если это тривиально), существующие записи пустые и не имеют определенного значения для вновь добавленного поля.Например, в примере приложения для блога, после того, как я опубликовал два сообщения, я решил добавить выдержку и поле местоположения в свою базу данных (см. Код выше).Любое сообщение в блоге, опубликованное после добавления этих новых полей, может быть обязательно заполнено значением для поля выдержки.Но сообщения, опубликованные до добавления этих двух новых полей, имеют нулевые значения (что понятно, почему), которые я не могу проверить.Есть ли для этого элегантное решение?

Спасибо.

1 Ответ

4 голосов
/ 26 января 2012

Существует три основных варианта:

  1. Обновите все внутри MongoDB, чтобы включить выдержку.
  2. Используйте хук after_initialize для добавления выдержки по умолчанию к существующим объектам, когда вы извлекаете их из MongoDB.
  3. Запутайте вашу логику проверки, чтобы проверять только наличие выдержки на новых объектах.

(1) требуется (возможно большой) удар по времени, когда вы вносите изменение, но это всего лишь разовая вещь, и вам не нужно беспокоиться об этом после этого. Вы извлекаете каждую страницу из MongoDB, делаете page.excerpt = 'some default excerpt', а затем сохраняете ее обратно в MongoDB. Если у вас есть много страниц, вы хотите обрабатывать их, скажем, по 100 штук за раз. Если вы сделаете это, вы сможете искать отрывок, не беспокоясь о том, что вам следует делать с null s. Вы также можете сделать это внутри MongoDB, отправив фрагмент JavaScript в MongoDB :

connection.eval(%q{
    db.pages.find({}, { _id: true }).forEach(function(p) {
        db.pages.update(
            { _id: p._id },
            { $set: { excerpt: 'some default excerpt' } }
        );
    });
})

(2) будет выглядеть примерно так:

after_initialize :add_default_excerpt, :unless => :new_record?
#...
private
def add_default_excerpt
  self.excerpt = 'some default excerpt' unless self.excerpt.present?
end

Вы можете переместить unless self.excerpt до :unless, если не возражаете против использования лямбды:

after_initialize :add_default_excerpt, :unless => ->{ |o| o.new_record? || o.excerpt.present? }
#...
private
def add_default_excerpt
  self.excerpt = 'some default excerpt'
end

Это должно быть довольно быстро и легко настроить, но есть и недостатки. Во-первых, у вас в MongoDB будет куча null, которые вам, возможно, придется обрабатывать специально во время поисков. Кроме того, вы будете иметь при себе кучу кода и логики для работы со старыми данными, но этот багаж будет использоваться все реже и реже. Кроме того, after_initialize звонки не приходят бесплатно.

(3) требует, чтобы вы пропустили проверку наличия выдержки для не новых страниц (:unless => :new_record?) или вам нужно было бы найти какой-то способ отличить новые объекты от старых, а также правильно обрабатывать изменения обоих новых и старые страницы. Вы также можете заставить людей предоставить отрывок, когда они изменяют страницу и оставляют вашу проверку как есть; включая :default => '' на вашем field :excerpt, мы позаботимся о любых nil проблемах в представлениях и тому подобное.


Я бы пошел с (1), если это возможно. Если обновление заняло бы слишком много времени и вы хотели, чтобы сайт работал и работал во время исправления MongoDB, вы можете добавить :default => '' при обновлении, а затем удалить опцию :default, перезапустить и вручную исправить все отклонения, которые были получены. через.

...