3

I have more then one objects. I need to update a filed in embeded objects. Here my objects.

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

It is a Contact collection. Here I need to set current : true where name is personal. I tried the loop function. It works fine.

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

In mongodb we can write single line of query to update multiple objects like

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

So I would like to know, Is there any possibility to write a single line of mongoid query to update multiple objects on mongoid.

1 Answer 1

2

An equivalent single-line Ruby statement using Mongoid is:

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

Here's the proof that it works.

app/models/contact.rb

class Contact
  include Mongoid::Document
  embeds_many :emails
end

app/models/email.rb

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

test/unit/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" => "[email protected]",
                    "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" => "[email protected]",
                    "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"=>"[email protected]",
     "name"=>"work"}]},
 {"_id"=>"50dc134bb4a6de8e6b000002",
  "emails"=>
   [{"_id"=>"50dc134bb4a6de8e6b000067",
     "current"=>true,
     "email"=>"",
     "name"=>"personal"},
    {"_id"=>"51a855763183a6301e004795",
     "current"=>true,
     "email"=>"[email protected]",
     "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
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks you, Gary Murakami, I have already tried this. It was not working.
I've updated the answer with full proof. You can copy it an assure yourself that it works equivalent to the mongo shell line.
@gary thanks for providing so many quality answers. Do you have suggestions on this: stackoverflow.com/questions/29719471/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.