[SERVER-2642] Allow Modifier Operations During Insert Created: 28/Feb/11  Updated: 10/Dec/14  Resolved: 02/Apr/13

Status: Closed
Project: Core Server
Component/s: Write Ops
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Major - P3
Reporter: Tom Wardrop Assignee: Unassigned
Resolution: Won't Fix Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Participants:

 Description   

A large convenience to developers, would be the ability to use modifier operations during an insert. I propose the addition of a second argument for "insert", the purpose of which would be to accept a hash of modifier operations . E.g. db.test.insert({}, {$set :

{name: 'bob'}

, $inc: {age: 3}})

The purpose of the previous example would be to insert a new document (with an auto-generated ID), and then apply the given modifiers (or what one may call the "update" clause) to that new document. Basically, there needs to be some way to apply modifier operations during an insert, as this is a relatively common requirement for object mappers and the like. In an object mapper I'm writing at the moment, the only work-around I've found is to issue a blank insert (for the sole purpose of inserting a new document and generating an ID), followed by an update (or a findAndModify in my specific circumstance).



 Comments   
Comment by Eliot Horowitz (Inactive) [ 02/Apr/13 ]

Tom - _id generated on the client are unique (part of it is machine/pid)

Comment by Tom Wardrop [ 01/Mar/11 ]

But that's the thing, unless I have a document _id, I don't want to update, I want to insert. So basically, the logic in my code is this:

if this._id is null then insert a new document,
otherwise, update existing document.

Whether I'm inserting a new document or updating an existing one, the operations I have queued in my program are in the form of modifiers, e.g. {$set :

{...}

, $inc : {...}}. Insert doesn't allow me to use modifiers like $set, so I have to use update(). What I'm doing currently is, when the document doesn't already exist (i.e. when I don't have an _id), I insert an empty document (for the sole purpose of getting an _id), and then do an update on it.

If however you're correct in that the _id is generated by the client, and not the server, then yes, all my problems could be solved by just generating an ID in my code (using the Ruby driver's BSON::ObjectId.new). I was under the impression though that _id's are generated on the server to ensure uniqueness? Is there no risk of _id clashes if they're generated on the client?

Comment by Scott Hernandez (Inactive) [ 01/Mar/11 ]

So what should the _id be? This makes no sense, either you want to match on some set of criteria or not.

You do know that the _id field is usually set automatically by the client (driver), not the server? If you want your mapper to create an _id automatically, just create a new ObjectId().

Comment by Tom Wardrop [ 01/Mar/11 ]

I think I may have found a solution to this using upsert. It's not pretty, and I would still like to see this suggestion implemented, but this work-around seems to get the job done in the mean time:

db.test.update(

{insert: "a unique value of some sort"}

, {$set :

{name: 'bob'}

, $inc:

{age: 3}

, $unset: {insert: 1}}, true)

The idea here is to put condition in the query that should never match an existing document. Obviously however, upsert inserts the query data if an insert is performed, thus we need to unset the field used in the query.

It's nasty, but it gets the job done. At the very least, it demonstrates the intended outcome I'm looking for.

Comment by Tom Wardrop [ 01/Mar/11 ]

I'm glad you asked. Assuming you want an auto-generated ID, and want to allow identical documents (i.e. documents with the same data, but a different ID), what would you put in the query portion of the update? Leave it blank, e.g.

db.test.insert(

{name: 'bob'}

)
db.test.update({}, {$set :

{name: 'bob'}

, $inc: {age: 3}}, true)

..and the empty query will match all documents, and then select only the first of those to perform the update on. There's simply no way you can achieve the intended result using an upsert.

Comment by Eliot Horowitz (Inactive) [ 01/Mar/11 ]

Why wouldn't you just do an upsert?

Generated at Thu Feb 08 03:00:45 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.