Ruby on Rails 和 NoSQL,添加字段

发布于 2024-12-29 07:04:48 字数 760 浏览 0 评论 0原文

我刚刚开始研究 Mongodb 和 MongoID with 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

而且这可以完美地发挥作用。但我的问题是,(抱歉,如果这是微不足道的)现有条目是空白的,并且没有为新添加的字段定义值。例如,在示例博客应用程序中,在我发布了两篇文章后,我决定将摘录和位置字段添加到我的数据库中(请参阅上面的代码)。添加这些新字段后发布的任何博客文章都可以确保为摘录字段填写了值。但是在添加这两个新字段之前发布的帖子具有空值(这是可以理解的原因),我无法验证。有一个优雅的解决方案吗?

谢谢。

I'm just diving into Mongodb and MongoID with Rails and I find it awesome. One thing the NoSQL helps is when I can add extra fields to my model without any extra effort whenever I want:

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

And this works perfectly as it should. But my question is, (sorry if this is trivial) the existing entries are blank and have no defined value for the newly added field. For example, in a sample blog application, after I've published two posts, I decide to add an excerpt and a location field to my database (refer code above). Any blog post that is published after the addition of these new fields can be made sure to have a value filled in for the excerpt field. But the posts published prior to the addition of these two new fields have null values (which is understandable why) which I cannot validate. Is there an elegant solution for this?

Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

风透绣罗衣 2025-01-05 07:04:48

共有三个基本选项:

  1. 更新 MongoDB 内的所有内容以包含摘录。
  2. 当您将现有对象从 MongoDB 中拉出时,使用 after_initialize 挂钩将默认摘录添加到现有对象中。
  3. 整合您的验证逻辑,仅检查新对象上是否存在摘录。

(1) 当您进行更改时需要(可能很大)时间,但这只是一次性的事情,之后您不必担心。您可以从 MongoDB 中提取每个页面,执行 page.excerpt = 'some default excerpt',然后将其保存回 MongoDB。如果您有很多页面,您会希望一次处理 100 个页面。如果这样做,您将能够搜索摘录,而不必担心应该如何处理 null。您还可以通过将 JavaScript 片段发送到 MongoDB 来在 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

您可以如果您不介意使用 lambda,请将 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 选项,重新启动,并手动修补任何通过的杂散。

There are three basic options:

  1. Update everything inside MongoDB to include the excerpt.
  2. Use an after_initialize hook to add a default excerpt to existing objects when you pull them out of MongoDB.
  3. Kludge your validation logic to only check for the existence of excerpt on new objects.

(1) requires a (possible large) time hit when you make the change but it is just a one time thing and you don't have to worry about it after that. You'd pull every Page out of MongoDB, do page.excerpt = 'some default excerpt', and then save it back to MongoDB. If you have a lot of Pages you'll want to process them in chunks of, say, 100 at a time. If you do this, you'll be able to search on the excerpt without worrying about what you should do with nulls. You can also do this inside MongoDB by sending a JavaScript fragment into MongoDB:

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

(2) would go something like this:

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

You could move the unless self.excerpt up to the :unless if you didn't mind using a lambda:

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

This should be pretty quick and easy to set up but there are downsides. First of all, you'd have a bunch of nulls in your MongoDB that you might have to treat specially during searches. Also, you'd be carrying around a bunch of code and logic to deal with old data but this baggage will be used less and less over time. Furthermore, the after_initialize calls do not come for free.

(3) requires you to skip validating the presence of the excerpt for non-new Pages (:unless => :new_record?) or you'd have to find some way to differentiate new objects from old ones while also properly handling edits of both new and old Pages. You could also force people to supply an excerpt when they change a Page and leave your validation as-is; including a :default => '' on your field :excerpt would take care of any nil issues in views and such.


I'd go with (1) if possible. If the update would take too long and you wanted the site up and running while you were fixing up MongoDB, you could add a :default => '' while updating and then remove the :default option, restart, and manually patch up any strays that got through.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文