[SERVER-340] $setOnInsert modifier for upsert Created: 07/Oct/09  Updated: 07/Mar/14  Resolved: 12/Feb/13

Status: Closed
Project: Core Server
Component/s: Write Ops
Affects Version/s: 1.1.1
Fix Version/s: 2.4.0-rc1

Type: New Feature Priority: Major - P3
Reporter: gf Assignee: Alberto Lerner
Resolution: Done Votes: 65
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
depends on SERVER-6399 Refactor update() code Closed
is depended on by DOCS-875 Add new $setOnInsert documentation Closed
Duplicate
is duplicated by SERVER-453 $iset - set fields only on insert whe... Closed
is duplicated by SERVER-3440 Add $setOnInsert command Closed
is duplicated by SERVER-4900 $ifnotpresent Closed
Related
related to CSHARP-684 Add new features to Update builder fo... Closed
is related to SERVER-7894 A $setOnChange command would be very ... Closed
Participants:

 Description   

This will set the fields specified in $setOnInsert:

{<fields>}

if the update results in an insert. It does nothing if any existing documents are updated.



 Comments   
Comment by Nik Shornikov [ 27/Feb/13 ]

@Scott Nichol +1

Comment by auto [ 07/Feb/13 ]

Author:

{u'date': u'2013-02-07T21:53:24Z', u'email': u'alerner@10gen.com', u'name': u'Alberto Lerner'}

Message: SERVER-340 $setOnInsert should be a no-op in an update.
Branch: master
https://github.com/mongodb/mongo/commit/52858af695150e71ca17f06e10c07419d54af42b

Comment by Scott Hernandez (Inactive) [ 31/Jan/13 ]

The implementation in 2.3.2 is actually what "$setIfAbsent" should be and not the correct behavior of $setOnInsert.

Comment by Alberto Lerner [ 12/Dec/12 ]

Already discussed documentation needs with Sam.

Comment by auto [ 12/Dec/12 ]

Author:

{u'date': u'2012-12-12T16:46:47Z', u'email': u'alerner@10gen.com', u'name': u'Alberto Lerner'}

Message: SERVER-340 Created a $setOnInsert update operator.
Branch: master
https://github.com/mongodb/mongo/commit/ac3f20c6dcb8aeaa9ae7182816ab1dc4f15619f5

Comment by Stephen Cuppett [ 17/Sep/12 ]

That's why I believe setIfAbsent is a better solution.

Comment by Ben Polidore [ 17/Sep/12 ]

Wait, if you do an upsert and put the _id in the query, it will set the id on the "upserted" document to that which you searched.

Comment by Ben Polidore [ 17/Sep/12 ]

This feature is valuable. Any chance of getting implemented?

Comment by Stephen Cuppett [ 06/Sep/12 ]

I have effectively implemented a variant of this feature as $setIfAbsent in pull request #298 (https://github.com/mongodb/mongo/pull/298). It appears to satisfy the use case.

Comment by Scott Nichol [ 24/Apr/12 ]

I am one day into my first MongoDB project and already have a definite need for this capability. It is disconcerting that this has been open since 2009 when it is so useful.

Comment by Glenn Maynard [ 09/Apr/12 ]

Rather than "$setOnInsert", this should be "$onInsert". For example,

insert({name: 'Glenn'}, {
    $onInsert: {
        $set: {x: 1},
        $pushAll: {y: [1,2,3]}
    }
}, true);

This allows using the same code to generate change lists, eg.:

function update_access_counter()
{
    return {$set: {last_date: new Date()}, $inc: {uses: 1}}
}
coll.update({_id: 1}, update_access_counter());
coll.update({name: 'Glenn'}, {$onInsert: update_access_counter()}, true);

They can share code much more easily, since you don't need separate code to generate

{last_date: new Date(), uses: 1}

in the second case.

Comment by Bryan Absher [ 10/Feb/12 ]

I think that there is enough of a need that this should be done now, whether there is a patch or not.

Comment by Philipp Heinze [ 07/Feb/12 ]

If there's already some code ready for inclusion into trunk this should be added asap. I don't feel that this new operator would clutter the code more than it's already and if so I'm not sure if this added functionality isn't worth it.
For the naming I don't feel that $setDef expresses what the operator does maybe even add may be too unspecific, further I don't think that something like $ifnotset would be too long as operator name. But anyway as long as the documentation make clear what the operator is for it can be named whatever the devs like as long as this will make it into core soon

Additionally as no refactoring of the current implementation happened for over a year, i don't feel that a refactoring (which seems not to happen in the very near future) should block the implementation of new operators, which are quite much wanted from the community.

Comment by Remon van Vliet [ 26/Jan/12 ]

By the way, I'm not that fond of $setDef. Although having shorter operator names is generally better this isn't a very clear name. $addField for example is more clear perhaps although I'm sure other suggestions are viable as well.

Comment by Remon van Vliet [ 26/Jan/12 ]

I'll ask just to be sure; this addition would allow for upserts where the _id field is not of type ObjectID correct? e.g. update(

{a:1}

, {$setDef:{_id:"myid"}, $set:{b:1}}, true). In fact, it could be argued server generated _id values can be removed completely through this change. The driver can now do this just as it does for insert/save.

Comment by Bryan Absher [ 10/Jan/12 ]

Is this feature going to be able to make it into the next release?

Comment by Sergey Schetinin [ 03/Nov/11 ]

$setDef is probably best, but I'll throw in some more options: $addField, $defVal.

Comment by Scott Hernandez (Inactive) [ 03/Nov/11 ]

$defaults/initial/onInsert maybe? It would good to allow all operators listed in there.

Comment by Seth LaForge [ 03/Nov/11 ]

The code seems to prefer shorter names. How about $setDef?

Comment by Sergey Schetinin [ 03/Nov/11 ]

WRT name. In Python such an method on a dict is called setdefault, maybe mongo could use the same name. It's not perfect but much better than $iset IMO.

Comment by Seth LaForge [ 03/Nov/11 ]

I needed this functionality, so I implemented it myself, ready for merging for 2.1:
   https://github.com/mongodb/mongo/pull/138

This supports $iset of _id, as well as other fields.

I'm not super keen on the name - "$iset" isn't very clear to me - but I can't think of anything I like better offhand. $new? $setOpt? Yuck.

Can this go in for 2.1? I realize from SERVER-1534, the claim:

Eliot Horowitz added a comment - Jan 30 2011 05:56:58 AM UTC
We're probably not going to add any more operators until we clean some of that code up.

...but I think that's short-sighted. I agree (after working with it) that this code desperately needs clean-up. However, adding new modifiers doesn't make the code much more complex, those new modifiers would need to be supported after a clean-up anyway.

Comment by Mongo User [ 30/Oct/11 ]

The $iset should support the definition of the _id value, so that custom ids can be used when an insert is made on an upsert (https://jira.mongodb.org/browse/SERVER-4175).

Comment by Remon van Vliet [ 07/Dec/10 ]

Voted, assuming Torsten's suggestion is taken into account so that the update is applied on the created document (and specifically the $add-ed fields).

Comment by Rithish [ 03/Nov/10 ]

This issue is still marked as open. Any timeline for this to come into production?

Comment by Eliot Horowitz (Inactive) [ 26/Jun/10 ]

Yes - something like that.

You can fork and commit on github.
This with a bunch of tests should do it.

Might be another case, but tests would show quickly

Comment by Torsten Curdt [ 26/Jun/10 ]

Shouldn't this be something along the lines of...

diff --git a/db/update.cpp b/db/update.cpp
index a943752..4527875 100644
— a/db/update.cpp
+++ b/db/update.cpp
@@ -29,7 +29,7 @@

namespace mongo {

  • const char* Mod::modNames[] = { "$inc", "$set", "$push", "$pushAll", "$pull", "$pullAll" , "$pop", "$unset" ,
    + const char* Mod::modNames[] = { "$inc", "$set", "$add", "$push", "$pushAll", "$pull", "$pullAll" , "$pop", "$unset" , "$bitand" , "$bitor" , "$bit" , "$addToSet" }

    ;
    unsigned Mod::modNamesNum = sizeof(Mod::modNames)/sizeof(char*);

@@ -94,6 +94,14 @@ namespace mongo

{ break; }

+ case ADD: {
+ if (in.type() == Undefined)

{ + _checkForAppending( elt ); + b.appendAs( elt , shortFieldName ); + }

+ break;
+ }
+
case UNSET: {
appendUnset( b );
break;
@@ -342,6 +350,10 @@ namespace mongo {
mss->amIInPlacePossible( m.elt.type() == e.type() &&
m.elt.valuesize() == e.valuesize() );
break;
+
+ case Mod::ADD:
+ mss->amIInPlacePossible( m.elt.type() != Undefined );
+ break;

case Mod::PUSH:
case Mod::PUSH_ALL:
@@ -454,6 +466,7 @@ namespace mongo {
case Mod::PULL:
case Mod::PULL_ALL:
case Mod::ADDTOSET:
+ case Mod::ADD:
// this should have been handled by prepare
break;

(Sorry, untested as it doesn't build for me yet)

Comment by Torsten Curdt [ 17/Jun/10 ]

Would be great to be able to combine these two operations into a single upsert

[code]
@totals.insert(

{'_id' => track_id}, {
'first_at' => time,
'last_at' => time,
"#{year}" => 1,
})

@totals.update({'_id' => track_id}

, {
'$set' =>

{ 'last_at' => time },
'$inc' => { "#{year}" => 1 },
})
[code]

so "first_at" only gets set on the first insert

[code]
@totals.update({'_id' => track_id}, {
'$add' => { 'first_at' => time },
'$set' => { 'last_at' => time }

,
'$inc' => { "#

{year}

" => 1 },
},

{upsert:true}

)
[code]

Comment by gf [ 19/Oct/09 ]

Good thought! $add - to add indendantly and $addbunch - to add if all of properties doesn't exist.

Comment by Jason Sachs [ 12/Oct/09 ]

suggest clarifying the description... "adds new properties only if it doesn't exist yet." Does this mean that each property is added only if that property does not exist, and that this decision is made independently for each key in $add's value? (that's what I think you mean) Or that they all get added only if all of them do not exist?

Generated at Thu Feb 08 02:53:46 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.