Mike Sukmanowsky
Mike Sukmanowsky

Reputation: 4477

MongoDB/MongoMapper Modifiers on Embedded Documents

Need some help with how to use atomic modifiers on an embedded document.

To illustrate, let's assume I've got a collection that looks like this.

Posts Collection

{
  "_id" : ObjectId("blah"),
  "title" : "Some title",
  "comments" : [
    {
      "_id" : ObjectId("bleh"),
      "text" : "Some comment text",
      "score" : 0,
      "voters" : []
    }
  ]
}

What I'm looking to do with MongoMapper/MongoDB is perform an atomic update on a specific comment within a post document.

Something like:

class Comment
  include MongoMapper::EmbeddedDocument

  # Other stuff...

  # For the current comment that doesn't have the current user voting, increment the vote score and add that user to the voters array so they can't vote again
  def upvote!(user_id)
    collection.update({"comments._id" => post_id, "comments.voters" => {"$ne" => user_id}}, 
      {"$inc" => {"comments.score" => 1}, "$push" => {"comments.voters" => user_id}})
  end
end

That's basically what I have now and it isn't working at all (nothing gets updated). Ideally, I'd also want to reload the document / embedded document but it seems as though there may not be a way to do this using MongoMapper's embedded document. Any ideas as to what I'm doing wrong?

Upvotes: 1

Views: 981

Answers (1)

Mike Sukmanowsky
Mike Sukmanowsky

Reputation: 4477

Got this working for anyone that's interested. Two things I was missing

  1. Using $elemMatch to search objects within an array that need to satisfy two conditions (such as _id = "" AND voters DOES NOT contain the user_id)
  2. Using the $ operator on the $inc and $push operations to ensure I'm modifying the specific object that's referenced by my query.

def upvote!(user_id)
  # Use the Ruby Mongo driver to make a direct call to collection.update
  collection.update(
    {
      'meanings' => {
        '$elemMatch' => {
          '_id' => self.id,
          'voters' => {'$ne' => user_id}
        }
      }
    },
    {
      '$inc' => { 'meanings.$.votes' => 1 },
      '$push' => { 'meanings.$.voters' => user_id }
    })
end

Upvotes: 2

Related Questions