9

I'm using Mongoid (v3) to access MongoDB, and want to perform this action:

db.sessionlogs.update( 
    {sessionid: '12345'}, /* selection criteria */
    {'$push':{rows: "new set of data"}},  /* modification */
    true /* upsert */
);

This works fine in the mongo shell. It's also exactly what I want since it's a single atomic operation which is important to me as I'm going to be calling it a lot. I don't want to have to do two operations -- a fetch and then an update. I've tried a bunch of things through mongoid, but can't get it to work.

How can I get MongoID out of the way and just send this command to MongoDB? I'm guessing there's some way to do this at the Moped level, but the documentation of that library is basically non-existent.

David J.
  • 31,569
  • 22
  • 122
  • 174
Leopd
  • 41,333
  • 31
  • 129
  • 167

3 Answers3

10

[Answer found while writing the question...]

criteria = Sessionlogs.collection.find(:sessionid => sessionid)
criteria.upsert("$push" => {"rows" => datarow})
Leopd
  • 41,333
  • 31
  • 129
  • 167
2

Here is one way to do it:

session_log = SessionLog.new(session_id: '12345')
session_log.upsert
session_log.push(:rows, "new set of data")

Or another:

SessionLog.find_or_create_by(session_id: '12345').
  push(:rows, "new set of data")

#push performs an atomic $push on the field. It is explained on the Atomic Persistence page.

(Note: the examples use UpperCamelCase and snake_case as is Ruby convention.)

David J.
  • 31,569
  • 22
  • 122
  • 174
0

Don't go down to moped just yet, you can use find and modify operation to achieve the same thing (with all the default scope and inheritance goodies)

Sample to save an edge in a graph if not existed

edge = {source_id: session[:user_id],dest_id:product._id, name: edge_name}
ProductEdge.where(edge).find_and_modify(ProductEdge.new(edge).as_document,{upsert:true})
Le Duc Duy
  • 1,881
  • 1
  • 19
  • 18