mongoid更新多个对象中的所有嵌入对象

时间:2014-09-18 04:50:12

标签: ruby-on-rails ruby mongodb mongoid

我有一个以上的对象。我需要更新嵌入对象中的字段。在这里我的对象。

{
    "_id" : ObjectId("50dc134bb4a6de8e6b000001"),
    "emails" : [
        {
            "_id" : ObjectId("50dc134bb4a6de8e6b000004"),
            "_type" : "Email",
            "name" : "personal",
            "email" : "",
            "current" : false
        },
        {
            "_id" : ObjectId("51a855763183a6301e009461"),
            "_type" : "Email",
            "name" : "work",
            "email" : "xxx@gmail.com",
            "current" : true
        },
    ],
}
{
    "_id" : ObjectId("50dc134bb4a6de8e6b000002"),
    "emails" : [
        {
            "_id" : ObjectId("50dc134bb4a6de8e6b000067"),
            "_type" : "Email",
            "name" : "personal",
            "email" : "",
            "current" : false
        },
        {
            "_id" : ObjectId("51a855763183a6301e004795"),
            "_type" : "Email",
            "name" : "work",
            "email" : "xxx@gmail.com",
            "current" : true
        },
    ],
}

这是一个Contact集合。在这里,我需要设置current : true,其中namepersonal。我尝试了循环功能。它工作正常。

contacts = Contact.where(:emails.elem_match => {"name" => "personal"})
  contacts.each do |contact|
  contact.email.where("name" => "personal").update_all(:current => true)
end

在mongodb中,我们可以编写单行查询来更新多个对象,如

db.contacts.update({"emails": {$elemMatch : {"name" : "personal"}},{ $set: {"emails.$.current":true}})

所以我想知道,是否有可能编写一行mongoid查询来更新mongoid上的多个对象。

1 个答案:

答案 0 :(得分:1)

使用Mongoid的等效单行Ruby语句是:

Contact.where(:emails.elem_match => {"name" => "personal"}).update_all("$set" => {"emails.$.current" => true})

以下是它有效的证明。

应用程序/模型/ contact.rb

class Contact
  include Mongoid::Document
  embeds_many :emails
end

应用程序/模型/ email.rb

class Email
  include Mongoid::Document
  field :name, type: String
  field :email, type: String
  field :current, type: Boolean
  embedded_in :contact
end

测试/单元/ contact_test.rb

require 'test_helper'
require 'pp'

class ContactTest < ActiveSupport::TestCase
  def setup
    Contact.delete_all
  end
  test '0. mongoid version' do
    puts "\nMongoid::VERSION:#{Mongoid::VERSION}\nMoped::VERSION:#{Moped::VERSION}\nRails.version:#{Rails.version}"
  end
  test 'update all embedded objects' do
    docs = [
        {
            "_id" => Moped::BSON::ObjectId("50dc134bb4a6de8e6b000001"),
            "emails" => [
                {
                    "_id" => Moped::BSON::ObjectId("50dc134bb4a6de8e6b000004"),
                    "_type" => "Email",
                    "name" => "personal",
                    "email" => "",
                    "current" => false
                },
                {
                    "_id" => Moped::BSON::ObjectId("51a855763183a6301e009461"),
                    "_type" => "Email",
                    "name" => "work",
                    "email" => "xxx@gmail.com",
                    "current" => true
                },
            ],
        },
        {
            "_id" => Moped::BSON::ObjectId("50dc134bb4a6de8e6b000002"),
            "emails" => [
                {
                    "_id" => Moped::BSON::ObjectId("50dc134bb4a6de8e6b000067"),
                    "_type" => "Email",
                    "name" => "personal",
                    "email" => "",
                    "current" => false
                },
                {
                    "_id" => Moped::BSON::ObjectId("51a855763183a6301e004795"),
                    "_type" => "Email",
                    "name" => "work",
                    "email" => "xxx@gmail.com",
                    "current" => true
                },
            ],
        }
    ]
    Contact.collection.insert(docs)
    # contacts = Contact.where(:emails.elem_match => {"name" => "personal"})
    # contacts.each do |contact|
    #   contact.emails.where("name" => "personal").update_all(:current => true)
    # end
    #db.contacts.update({"emails": {$elemMatch : {"name" : "personal"}},{ $set: {"emails.$.current":true}})
    Contact.where(:emails.elem_match => {"name" => "personal"}).update_all("$set" => {"emails.$.current" => true})
    puts
    contacts = Contact.all.to_a
    contacts.each do |contact|
      emails = contact["emails"].select{|email| email["name"] == "personal"}
      assert_equal(true, emails.first["current"])
    end
    pp contacts.collect{|contact| contact.serializable_hash}
  end
end

rake test

Run options:

# Running tests:

[1/2] ContactTest#test_0._mongoid_version
Mongoid::VERSION:3.1.6
Moped::VERSION:1.5.2
Rails.version:3.2.17
[2/2] ContactTest#test_update_all_embedded_objects
[{"_id"=>"50dc134bb4a6de8e6b000001",
  "emails"=>
   [{"_id"=>"50dc134bb4a6de8e6b000004",
     "current"=>true,
     "email"=>"",
     "name"=>"personal"},
    {"_id"=>"51a855763183a6301e009461",
     "current"=>true,
     "email"=>"xxx@gmail.com",
     "name"=>"work"}]},
 {"_id"=>"50dc134bb4a6de8e6b000002",
  "emails"=>
   [{"_id"=>"50dc134bb4a6de8e6b000067",
     "current"=>true,
     "email"=>"",
     "name"=>"personal"},
    {"_id"=>"51a855763183a6301e004795",
     "current"=>true,
     "email"=>"xxx@gmail.com",
     "name"=>"work"}]}]
Finished tests in 0.083133s, 24.0578 tests/s, 24.0578 assertions/s.
2 tests, 2 assertions, 0 failures, 0 errors, 0 skips