Reputation: 6049
This Meteor server side (Mongo 1.1.18) tries to upsert a document according to the selector as shown but gives the following error:
myCol.upsert({name: 'sam', job: {$exists: false}}, {$set: {parents: ['jack', 'jacky']}});
MongoError: The dollar ($) prefixed field '$exists' in 'job.$exists' is not valid for storage.
How can I upsert to this selected document? or create it if it does not exist? thx
Upvotes: 0
Views: 3787
Reputation: 151200
The reason is because with an "upsert"
MongoDB is trying to assign any "query" arguments supplied as a "value" in the newly created object. Since you cannot name a property with a $
the error is thrown because it is trying to create the field "job" as { "job": { "$exists": true } }
, just like you supplied in the query arguments.
To avoid this, tell MongoDB which fields to actually use on creation by specifying $setOnInsert
. At least that's the case for a normal MongoDB collection. Thus for "meteor" you should access the .rawCollection()
instead"
myCol.rawCollection().update(
{name: 'sam', job: {$exists: false}},
{
$setOnInsert: { name: 'sam' },
$set: {parents: ['jack', 'jacky']}
},
{ upsert: true }
)
Note that when you do this this does not use various other meteor defaults, such as the value of _id
, so you might need to use the alternate approach.
The alternate approach here is to instead use $where
which can evaluate a JavaScript condition against the document to see that the "job"
field is not present. This would actually not be carried over to to "upsert" even from the meteor collection implementation:
myCol.upsert(
{name: 'sam', '$where': "!this.hasOwnProperty('job')" },
{
$set: {parents: ['jack', 'jacky']}
}
)
To prove the point, I started up a bare meteor project with no other changes than just adding a collection and executing the the code on server startup. So basically on a new project changing server/main.js
as follows:
import { Meteor } from 'meteor/meteor';
import { People } from '../imports/people.js';
Meteor.startup(() => {
// code to run on server at startup
People.upsert(
{ name: 'john', '$where': "!this.hasOwnProperty('job')" },
{
'$set': { parents: [ 'jack', 'jacky' ] }
}
);
People.rawCollection().update(
{ name: 'bill', job: { '$exists': false } },
{
'$setOnInsert': { name: 'bill' },
'$set': { parents: [ 'jack', 'jacky' ] }
},
{ 'upsert': true }
)
});
And the imports/people.js
:
import { Mongo } from 'meteor/mongo';
export const People = new Mongo.Collection('people');
And the documents are created in the collection without error as expected:
{
"_id" : "irDWFLdACjNKYGEcN",
"name" : "john",
"parents" : [
"jack",
"jacky"
]
}
{
"_id" : ObjectId("596489d0902edee769372ac6"),
"name" : "bill",
"parents" : [
"jack",
"jacky"
]
}
Upvotes: 1