Sailsjs find where field is not set - mongodb

I have a project in SailsJs, using waterline with a mongodb database.
I have some fields defined, which are not required fields.
I need to search records where that field is not defined. For example:
{id: 10, name: "jj"}
{id: 11, name: "kk", surname: "sr"}
I want to search where surname is not defined. Anybody has a clue how to do this?
I have tried
{surname: null}
{surname: ''}
Nothing produces the desired result.

I think what you want is Model.find( {where: {surname: null } }) Check out this link to Waterline Query language documentation

I think what you want is $exists: false. It is currently not supported by waterline. You can use Model.native() to use it.
Waterline native()
It returns MongoDB collection and you can use there native MongoDB operations:
MongoDB $exists
Complete code will look more like this
Model.native(function(err, collection) {
if (err) throw err;
collection.find({
surname: { $exists: false}
}).toArray(yourCallback);
});

Related

MongoInvalidArgumentError: Update document requires atomic operators

I am going through a course on MongoDB. Below is my list of documents in a collection called 'flightData'. Below is result of find query:
flights> db.flightData.find()
[
{
_id: ObjectId("611aaa1c4a0269583c8d81b0"),
aircraft: 'AirBus A308',
departingFrom: 'Mumbai',
arrivalStation: 'Moscow',
departureDate: '24/09/2021',
departureTime: '01:44',
arrivingAt: '12:00',
isOneWay: false,
status: {
description: 'on time',
lastUpdated: 'One hour ago',
details: { contact: 'John Doe' }
}
},
{
_id: ObjectId("611aaa554a0269583c8d81b1"),
aircraft: 'AirBus A308',
departingFrom: 'Kolkata',
arrivalStation: 'Stockholm',
departureDate: '24/09/2021',
departureTime: '01:44',
arrivingAt: '12:00',
isOneWay: false,
status: {
description: 'on time',
lastUpdated: 'One hour ago',
details: { contact: 'Cool User' }
}
}
]
When they show the difference between update and updateMany through an example similar to below one:
flights> db.flightData.update({_id:ObjectId("611aaa554a0269583c8d81b1")},{"delayed":false})
In the lecture it works. However, in my case it throws below error:
MongoInvalidArgumentError: Update document requires atomic operators
Can someone please explain this behavior? Is it not supported for my version or something else?
I am using MongoDB 5.0.2, mongosh 1.0.5
If you want to add the "delayed" field to the document you will want to use the $set operator
db.flightData.update({_id:ObjectId("611aaa554a0269583c8d81b1")},{$set:{"delayed":false}})
If you would like to replace the document you should use replaceOne
This command was added in mongodb 3.2 to avoid accidentality replacing the entire document when you mean to update a field
This may help someone so writing as answer.
In my case, I'm doing bulk write operation and missed a field that I'm trying to update in Mongoose Schema caused this issue. After correcting the Mongoose Schema, issue is resolved.
So, validate the fields that are used in update are configured in Schema if you are using Mongoose.

MongoDB - how to only update field if field does not exist

How can I update a mongo document with the following requirements:
Find a document by email property:
If the document exists:
If both retrieved and new document have property A, keep property A (the retrieved one).
If retrieved document property A is null or undefined or doesn't exist, update using property A of the new object.
If the document doesn't exist
Insert the new document.
The findOneAndUpdate seems not to convey the both 3 of the requirements. Thanks.
My recommendation is to go the following path:
db.getCollection('<some-collection>').update(
{ email: 'someguy#email.com' },
{
$set: {
name: "some guy",
username: someguy,
tel: '1234'
}
},
{ upsert: true }
);
Check upsert documentation:
https://docs.mongodb.com/manual/reference/method/db.collection.update/#upsert-option
Lets go through your requirements now:
3. If the document doesn't exist, insert the new document.
Yes, it will insert new document to collection if it doesnt find the document by email. Resulting document will be combination of find condition + $set + autogenerated _id, so it will look something like this:
{
_id: ObjectId(...)
email: 'someguy#email.com'
name: "some guy",
username: someguy,
tel: '1234'
}
2. If retrieved document property A is null or undefined or doesn't exist, update using property A of the new object.
All properties provided in $set will unconditionally be persisted in the database, which also covers your requirement of updating null/undefined values
3. If both retrieved and new document have property A, keep property A (the retrieved one).
If both newly provided A and database A are the same, we dont have a problem.
If As are different, dont you want to store the new A value?
If you are afraid of nulls/undefined values, you can omit them before providing object to $set.
What is the use-case for you not wanting to update database property with newly provided value?
One use-case i can see is that you want to pass createdAt in case you are creating new record, but dont want to update that value for existing records.
If thats the case, and you know those properties in advance, you can use $setOnInsert update operator. https://docs.mongodb.com/manual/reference/operator/update/#id1
So your update query can look like this:
db.getCollection('<some-collection>').update(
{ email: 'someguy#email.com' },
{
$set: {
name: "some guy",
username: someguy,
tel: '1234'
},
$setOnInsert: {
createdAt: new Date(),
updatedAt: new Date()
}
},
{ upsert: true }
);
I hope this helps!
You need not retrieve the document for updating the property A. You can use the update API of mongo to do so. Please find the psuedo code below:
db.<collection>.update({
"$or": [
{ "PropertyA": { "$exists": false } },
{ "PropertyA": null }
]
}, {$set: {"PropertyA": "NewValue"}});
The above code is for one property, but I think you can figure out how to scale it up.
Hope this helps !!

Mongoose find elements with value in subdocuments

My models
ActivityLanguage
language_id: ID
name: String
Activity
price: Int
languages: [ActivityLanguage]
what i want to do is to find the Activities that have an ActivityLanguage with certain language_id.
This works like i need (in mongo):
db.activities.find({"languages.language_id": "59640de1675c1481820a543e" }, {"languages.$": 1})
How can i achieve it in mongoose?
I already tried using:
Activity.find({'languages.language_id': language_id})
and
Activity.find({"languages": {$elemMatch: { language_id: language_id }}})
both of them return an empty array (i already triple checked my database that there are Activities with that condition). I also tried all the answers i found here in StackOverflow without success
How can i achieve it in mongoose?
You can use Mongoose model.populate
From your example:
Activity.
find({}).
populate({
path: 'languages',
model: 'ActivityLanguage',
match: { language_id: ID } //here you define your language_id criteria
}).
exec()

Conditional update of multiple entries in mongodb?

I am a relative newbie in mongodb but have lots of experience in MySQL.
I want to do a simple query that would appear in MySQL as follows:
UPDATE {database}.{table} SET {FIELD1}='{VALUE1}' WHERE {FIELD2} = {VALUE2};
e.g.
UPDATE test.user SET email='test#acc.ie' WHERE ref='12';
I don't want to destroy the database or collection on my first attempt.
I have only ever ran SELECT type queries on mongo, edited individual json entries or dropped an entire database. A select in mongodb looks like the following
db.getCollection('user').find({email : "test#acc.ie"})
Is the following correct based on the MySQL example?
db.user.update({ref : "12"}, {$set: {email: test#ac.ie}}, { multi: true })
Because this is the response I am getting:
Updated 0 record(s) in 141ms
The syntax looks ok, you will get an error with how you are accessing the collection currently, change it to
db.user.update({ref: 12}, {$set: {email: 'test#ac.ie'}}, { multi: true })
or
db.getCollection('user').update({ref: 12}, {$set: {email: 'test#ac.ie'}}, { multi: true })
Note: the value of the field being set should be quoted if it's a string and unquote if a number. Best read the manual for better referencing.

Upserts in mongodb when using custom _id values

I need to insert a document if it doesn't exist. I know that the "upsert" option can do that, but I have some particular needs.
First I need to create the document with its _id field only, but only if it doesn't exist already. My _id field is a number generated by me (not an ObjectId). If I use the "upsert" option then I get "Mod on _id not allowed"
db.mycollection.update({ _id: id }, { _id: id }, { upsert: true });
I know that we can't use the _id in a $set.
So, my question is: If there any way to a "create if doesn't exists" atomically in mongodb?
EDIT:
As proposed by #Barrie this works (using nodejs and mongoose):
var newUser = new User({ _id: id });
newUser.save(function (err) {
if (err && err.code === 11000) {
console.log('If duplicate key the user already exists', newTwitterUser);
return;
}
console.log('New user or err', newTwitterUser);
});
But I still wonder if it is the best way to do it.
I had the same problem, but found a better solution for my needs. You can use that same query style if you simply remove the _id attribute from the update object. So if at first you get an error with this:
db.mycollection.update({ _id: id }, {$set: { _id: id, name: 'name' }}, { upsert: true });
instead use this:
db.mycollection.update({ _id: id }, {$set: { name: 'name' }}, { upsert: true });
This is better because it works for both insert and update.
UPDATE: Upsert with _id can be done without $setOnInsert, as explaind by #Barrie above.
The trick is to use $setOnInsert:{_id:1} with upsert, that way the _id is only written to if it's an insert, and never for updates.
Only, there was a bug preventing this from working until v2.6 - I just tried it on 2.4 and it's not working.
The workaround I use is having another ID field with a unique index. Eg. $setOnInsert:{myId:1}.
You can just use insert(). If the document with the _id you specify already exists, the insert() will fail, nothing will be modified - so "create if it doesn't exist" is what it's already doing by default when you use insert() with a user-created _id.
Please note that $setOnInsert don't work easily when you upsert a simple key => value object (not $set or other).
I need to use that (in PHP):
public function update($criteria , $new_object, array $options = array()){
// In 2.6, $setOnInsert with upsert == true work with _id field
if(isset($options['upsert']) && $options['upsert']){
$firstKey = array_keys($new_object)[0];
if(strpos($firstKey, '$')===0){
$new_object['$setOnInsert']['_id'] = $this->getStringId();
}
//Even, we need to check if the object exists
else if($this->findOne($criteria, ['_id'])===null){
//In this case, we need to set the _id
$new_object['_id'] = $this->getStringId();
}
}
return parent::update($criteria, $new_object, $options);
}