mongodb, get the result line after update - node.js

I'm working with mongodb, node.js and socket.io and I'm trying to reduce the number off access to the database. I need to update a line ; and after to return the updated line this is how I do :
db.collection('users').update({_id:targetID}, {$set: { 'property': 'value' }}, {safe:true}, function(err, result) {
db.collection('users').find({_id:targetID}).toArray(function(error, results){
//a socket.io resend the content
});
});
It works, but I really fell like I'm having a useless step here. The callback of the update function seems to be a boolean.
BTW, is there a better documentation than this one : http://docs.mongodb.org/manual/applications/update/ ? I'd like to find a list of properties and methods. The {safe:true} for instance. It seems not working without it but I can't find it in the reference.
Maybe I'm completely wrong and this is not the way I should do it. If you have a better idea... :)

You can use findAndModify to do this efficiently:
db.collection('users').findAndModify(
{_id: targetID}, [],
{$set: { 'property': 'value' }},
{new: true}, // Return the updated doc rather than the original
function(err, result) {
// result contains the updated document
}
);

Related

"not contains" query in SailsJS Waterline

How can I create the not contains query that returns true if the item does not contain a certain value. I'm running mongoDB if it is relevant.
I can use the contains successfully however receive an error when I introduce the not. I've tried it with find and where but get the same regex error both times. I can add the exact error if its relevant.
Working version:
model.find({attribute: {'contains': value}})
.exec(function(err, users) {
// happy code
});
The following complains about some regex error:
model.find({attribute: {not: {'contains': value}}})
.exec(function(err, users) {
// sad code
});
There was an issue raised and closed in 2013 about this. Maybe something changed recently?
https://github.com/balderdashy/waterline/issues/22
Alexei, I don't think this is supported by waterline currently. There is a related issue which is marked as feature and that you can track:
https://github.com/balderdashy/waterline/issues/666
So far, you can only use native feature of Mongodb I think.
Model.native(function(err, collection) {
collection.find({
"attribute" : {
$ne : value, //Not Equal
//OR
$nin : [value1, value2]// Not In
}
}).toArray(function(err, docs) {
if (err) return callback(err, docs);
res.json(null, docs);
});
});

Saving subdocuments with mongoose

I have this:
exports.deleteSlide = function(data,callback){
customers.findOne(data.query,{'files.$':1},function(err,data2){
if(data2){
console.log(data2.files[0]);
data2.files[0].slides.splice((data.slide-1),1);
data2.files[0].markModified('slides');
data2.save(function(err,product,numberAffected){
if(numberAffected==1){
console.log("manifest saved");
var back={success:true};
console.log(product.files[0]);
callback(back);
return;
}
});
}
});
}
I get the "manifest saved" message and a callback with success being true.
When I do the console.log when I first find the data, and compare it with the console.log after I save the data, it looks like what I expect. I don't get any errors.
However, when I look at the database after running this code, it looks like nothing was ever changed. The element that I should have deleted, still appears?
What's wrong here?
EDIT:
For my query, I do {'name':'some string','files.name':'some string'}, and if the object is found, I get an array of files with one object in it.
I guess this is a subdoc.
I've looked around and it says the rules for saving subdocs are different than saving the entire collection, or rather, the subdocs are only applied when the root object is saved.
I've been going around this by grabbing the entire root object, then I do loops to find the actual subdoc I that I want, and after I manipulate that, I save the whole object.
Can I avoid doing this?
I'd probably just switch to using native drivers for this query as it is much simpler. (For that matter, I recently dropped mongoose on my primary project and am happy with the speed improvements.)
You can find documentation on getting access to the native collection elsewhere.
Following advice here:
https://stackoverflow.com/a/4588909/68567
customersNative.update(data.query, {$unset : {"slides.1" : 1 }}, function(err){
if(err) { return callback(err); }
customersNative.findAndModify(data.query, [],
{$pull: {'slides' : null } }, {safe: true, 'new' : true}, function(err, updated) {
//'updated' has new object
} );
});

Mongoose: Using addToSet with ObjectIds Results in Orphan Id

I am having a rather interesting problem using mongoDB's $addToSet to an array full of ObjectIds.
In my mongoose schema ("Happening"), I declare an array of ObjecIds called "expected", to be used by .populate().
expected: [{type: Schema.Types.ObjectId, ref: "User" }]
... which works nicely everywhere I use it. So far so good.
I then attempt to update the Happening.expected array using $addToSet as outlined here:
http://docs.mongodb.org/manual/reference/operator/addToSet/
like so:
app.get("/happening/yamobethere/:id", ensureLoggedIn("/login"),
function (req, res) {
// userId is the mongo ObjectId of the user record
var userId = req.session.user.id,
eventId = req.params.id;
models.Happening.update(
{_id: eventId}, {
$addToSet: {expected: userId}
},
function(err, updated){
if (err) {
res.json({"error": err});
}
res.json({"updated": updated});
});
});
... which always yields:
{updated: 1}
Now the docs lead me to expect the actual userId that I passed in, so the "1" is a bit odd. I expected it to be a fail, and in light of the weirdness that happens next, it appears to be a mongodb error of some sort percolating it's way back to me as results.
The weirdness is, when I check my database, I see that indeed a new ObjectId has been added: just not the one I passed in.
"expected" : [
ObjectId("51cb18623ade2b9f1e000004"),
ObjectId("51cdb7c12f0e58bdb3000001")
],
becomes
"expected" : [
ObjectId("51cb18623ade2b9f1e000004"),
ObjectId("51cdb7c12f0e58bdb3000001"),
ObjectId("51cdb80e09612bfab3000002")
],
The new ObjectId does not appear in any of my collections. It appears to be an orphan, but I'm a mongo noob, so I may be full of compost on this.
I did attempt to cast the userId as an ObjectId:
$addToSet: {expected: mongoose.Types.ObjectId.fromString(userId)}
but that changed nothing, and really should not be necessary, since the schema should handle it.
I'd really rather not resort to downloading the entire object, appending the value to the "expected" array, then sending the whole schmear back for an update.
Any help appreciated, folks. Thanks!
Update:
A colleague suggested the following technique:
var addMe = {$addToSet: {expected: userId}};
models.Happening.findByIdAndUpdate(eventId, addMe, function(err, me) {
if (err) {
return json(err);
}
res.json(200, me);
});
... which is a bit of an improvement, since it actually returns an object for me to inspect. Unfortunately, it also results in orphaned ObjecIds appearing in the array, rather than the existing userId value I specified.
Thanks again!
It appears that my passport strategy is returning the ObjectID of the rejected attempted creation of a new user in the db via data from oauth. So, the code is fine, my data is garbage.
Never trust anything, and be prepared to look like a boob. :-)
Thanks for the clarification on my return values JohnnyHK.

Node.js MongoDB Upsert update

I'm writing a little application which scores keywords. So if "beirut" and "education" get entered in, if they haven't been seen before, I want to create a mongo entry, and give them a score of 1. If they have, I want to increment their score by one. I'm trying to do this with one update command, but I think I might be doing it wrong.
Ranking is the object representing the database
"key" is the keyword
rankingdb.update(
{keyword:key},
{keyword:key, {$inc:{score:1}}},
{upsert:true, safe:false},
function(err, data) {
if (err) {
console.log(err);
}
else {
console.log("score succeeded");
}
}
);
SyntaxError: Unexpected token {
Can you not create a brand new document with an increment?
Your general approach is right, but as the error message suggests, you've got a syntax problem in your code.
Try this instead:
rankingdb.update(
{keyword: key},
{$inc: {score: 1}},
{upsert: true, safe: false},
function(err,data){
if (err){
console.log(err);
}else{
console.log("score succeded");
}
}
);
When an upsert needs to create a new object it combines the fields from the selector (first parameter) and the update object (second parameter) when creating the object so you don't need to include the keyword field in both.
Note that update() is deprecated in the 2.0 driver, so you should now use either updateOne() or updateMany().

How to sort a collection by date in MongoDB?

I am using MongoDB with Node.JS. I have a collection which contains a date and other rows. The date is a JavaScript Date object.
How can I sort this collection by date?
Just a slight modification to #JohnnyHK answer
collection.find().sort({datefield: -1}, function(err, cursor){...});
In many use cases we wish to have latest records to be returned (like for latest updates / inserts).
db.getCollection('').find({}).sort({_id:-1})
This will sort your collection in descending order based on the date of insertion
Sorting by date doesn't require anything special. Just sort by the desired date field of the collection.
Updated for the 1.4.28 node.js native driver, you can sort ascending on datefield using any of the following ways:
collection.find().sort({datefield: 1}).toArray(function(err, docs) {...});
collection.find().sort('datefield', 1).toArray(function(err, docs) {...});
collection.find().sort([['datefield', 1]]).toArray(function(err, docs) {...});
collection.find({}, {sort: {datefield: 1}}).toArray(function(err, docs) {...});
collection.find({}, {sort: [['datefield', 1]]}).toArray(function(err, docs) {...});
'asc' or 'ascending' can also be used in place of the 1.
To sort descending, use 'desc', 'descending', or -1 in place of the 1.
Sushant Gupta's answers are a tad bit outdated and don't work anymore.
The following snippet should be like this now :
collection.find({}, {"sort" : ['datefield', 'asc']} ).toArray(function(err,docs) {});
This worked for me:
collection.find({}, {"sort" : [['datefield', 'asc']]}, function (err, docs) { ... });
Using Node.js, Express.js, and Monk
collection.find().sort('date':1).exec(function(err, doc) {});
this worked for me
referred https://docs.mongodb.org/getting-started/node/query/
With mongoose it's as simple as:
collection.find().sort('-date').exec(function(err, collectionItems) {
// here's your code
})
Additional Square [ ] Bracket is required for sorting parameter to work.
collection.find({}, {"sort" : [['datefield', 'asc']]} ).toArray(function(err,docs) {});
if your date format is like this : 14/02/1989 ----> you may find some problems
you need to use ISOdate like this :
var start_date = new Date(2012, 07, x, x, x);
-----> the result ------>ISODate("2012-07-14T08:14:00.201Z")
now just use the query like this :
collection.find( { query : query ,$orderby :{start_date : -1}} ,function (err, cursor) {...}
that's it :)
With mongoose I was not able to use 'toArray', and was getting the error: TypeError: Collection.find(...).sort(...).toArray is not a function.
The toArray function exists on the Cursor class from the Native MongoDB NodeJS driver (reference).
Also sort accepts only one parameter, so you can't pass your function inside it.
This worked for me (as answered by Emil):
collection.find().sort('-date').exec(function(error, result) {
// Your code
})

Resources