Can findOne match first or last? - javascript

I'm specifically using mongoose, although I don't believe that matters that much. For example, say I have a collection called MongoQueue and I add a few people to that queue.
`MongoQueue.save {function(err, firstPerson) {
if (err) console.log(err);
MongoQueue.save {function(err, secondPerson) {
if (err) console.log(err);
MongoQueue.save {function(err, thirdPerson) {
if (err) console.log(err);
}}}`
How do I retrieve the person who was first saved to MongoQueue? Or....how does the findOne() method of mongoDB narrow down it's search? Can I specify behavior of findOne() to choose the oldest document in the collection? Or must I do a sort on the collection (this would be last resort), and if so how would I sort by timestamp?

Yes you can specify the behavior of .findOne() as is best shown in the native driver documentation. The only difference there is that in the mongoose implemetation the "options" document must be the "third" argument passed to the method.
So you can supply a "sort" specification to this as is shown in the available options:
Queue.findOne({ },null,{ "sort": { "_id": -1 } },function(err,doc) {
Just for additional information you can do this in the MongoDB shell with the following, using the $orderby query option:
db.collection.findOne({ "$query": { }, "$orderby": { "_id": -1 } })
Also the .findOne() method may only return one document, but it really is just a wrapper around .find() so all of the modifiers apply. The wrapping just calls .next() on the returned cursor, returns the document and discards the cursor.
This longer example shows different ways in which this can be applied:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/sequence');
var queueSchema = new Schema({
name: String,
same: { type: String, default: "same" }
});
var Queue = mongoose.model( "Queue", queueSchema );
var count = 0;
async.series(
[
// Remove any documents
function(callback) {
Queue.remove(function(err) {
if (err) throw err;
callback();
});
},
// Insert some new ones
function(callback) {
async.eachSeries(
["one","two","three"],
function(item,callback) {
var queue = new Queue({ name: item });
queue.save(function(err,doc) {
if (err) throw err;
console.dir(doc);
callback(err,doc);
});
},
function(err) {
callback(err);
}
);
},
function(callback) {
async.whilst(
function() { return count < 2 },
function(callback) {
count++
async.series(
[
// findOne is just the first one
function(callback) {
Queue.findOne({ "same": "same" },function(err,doc) {
if (err) throw err;
console.log( "FindOne:\n%s", doc );
callback();
});
},
// Or is sorted
function(callback) {
Queue.findOne(
{ "same": "same" },
null,
{ "sort": { "_id": -1 } },
function(err,doc) {
if (err) throw err;
console.log("FindOne last:\n%s", doc );
callback();
}
);
},
// find is ordered but not singular
function(callback) {
async.eachSeries(
["first","last"],
function(label,callback) {
var direction = ( label == "first" ) ? 1 : -1;
var query = Queue.find({ "same": "same" })
.sort({ "_id": direction })
.limit(1);
query.exec(function(err,docs) {
if (err) throw err;
console.log( ".find() %s:\n%s", label, docs[0] );
callback();
});
},
function(err) {
callback();
}
);
},
// findAndModify takes a sort
function(callback) {
Queue.findOneAndUpdate(
{ "same": "same" },
{ "$set": { "same": "different" } },
{ "sort": { "_id": -1 } },
function(err,doc) {
if (err) throw err;
console.log( "findOneAndUpdate:\n%s", doc );
callback();
}
);
}
],function(err) {
callback();
}
);
},
function(err) {
callback();
}
);
}
],function(err) {
console.log("done");1
mongoose.disconnect();
}
);

The findOne() function returns documents in the natural order, which is the order on disk. You cannot count on this returning the least recently inserted document. To return the least recently inserted document, you can use the find() function with an inverse sort on _id and a limit of 1, assuming _id is a default ObjectId generated for you, or on a timestamp (_id is built, in part, from a timestamp, which is why it works with _id). If you use _id (which is always indexed) or index the timestamp field, this will be very fast.

Related

Use Rename collection in mongoose

I have a problem. I renamed my collection responses into old. It works really well but now I need to retrieve my data and my impediment is that I only used the model to retrieve my data from a collection. But now I need to retrieve my data from my renamed collection but I have no model and schema. I tried to create a schema and a model but it didn't work. It returns no elements.
Here is a part of the code :
app.get("/Play", function(req, res) {
var urlTempBox = 'http://localhost:3000/Play';
///////////////////////////////////////////////////////////
request(urlTempBox, function(error, response, body) {
if (error) {
throw (error);
} else {
var jobj = JSON.parse(response.body);
persistRS(jobj);
setTimeout(function() {
ResponseDatabase.find()
.populate('unitCode')
.exec(function(err, finalData) {
if (err) throw (err);
mongoose.connection.db.listCollections({
name: 'old'
})
.next(function(err, collinfo) {
if (err) throw (err);
if (collinfo) {
console.log('lookinOld');
OldResponseDatabase.find()
.populate('unitCode')
.exec(function(err, oldData) {
if (err) throw (err);
console.log('itsOld');
console.log(oldData);
res.send(finalData);
});
} else {
console.log('fk');
res.send(finalData);
}
})
})
}, 5000);
}
});
Here is the part where it doesn't work: console.log(oldData) returns nothing. And I know that my data is in the database when I try to retrieve them.
if (collinfo) {
console.log('lookinOld');
OldResponseDatabase.find()
.populate('unitCode')
.exec(function(err, oldData) {
if (err) throw (err);
console.log('itsOld');
console.log(oldData);
res.send(finalData);
});
}
Finally I found how to do maybe it will be usefull for someone
You just need in your schema to precise the name of your collection like this
( collection : 'old' )
var nameSchemaOldRS = new mongoose.Schema({
MainClass: String,
BookingClass: String,
carID: String,
unitCode:{type: String, ref: 'Map' ,required: [true,'No post id found']},
Deck:Number,
Orientation:String,
Position:String,
}, {
versionKey: false,
collection : 'old'
},);

Mongo find() produces {} along with expected results

Hello I am trying to filter out for only video_trim in my query but the code still produces empty objects in the place of filtered out content
here is the code
const findTrims = (db, cb) => {
// Get the documents collection
const collection = db.collection(documentName);
// Find some documents
collection.find({}, {projection:{ _id: 0, name: 0, label: 0 }}).toArray((err, docs) => {
// An error occurred we need to return that to the given
// callback function
if (err) {
return cb(err);
}
assert.equal(err, null);
console.log("Found the following records");
console.log(docs)
return cb(null, docs);
});
}
module.exports = {
findTrims: cb => {
MongoClient.connect(url, (err, client) => {
if (err) {
return cb(err)
}
console.log('Connected successfully to server')
const db = client.db(dbName)
findTrims(db, (err, docs) => {
if (err) {
return cb(err)
}
// return your documents back to the given callback
return cb(null, docs)
})
})
}
}
the output of the find is this
[ {}, {}, {}, {}, { Video_trim: ' ' } ]
How do I get rid of the {}?
Pass some conditions to filter. f.e. collection.find({"Video_trim": { $exists: true }}, { _id: 0, name: 0, label: 0 })

Using async.js for deep populating sails.js

I have a big issue with my function in sails.js (v12). I'm trying to get all userDetail using async (v2.3) for deep populating my user info:
UserController.js:
userDetail: function (req, res) {
var currentUserID = authToken.getUserIDFromToken(req);
async.auto({
//Find the User
user: function (cb) {
User
.findOne({ id: req.params.id })
.populate('userFollowing')
.populate('userFollower')
.populate('trips', { sort: 'createdAt DESC' })
.exec(function (err, foundedUser) {
if (err) {
return res.negotiate(err);
}
if (!foundedUser) {
return res.badRequest();
}
// console.log('foundedUser :', foundedUser);
cb(null, foundedUser);
});
},
//Find me
me: function (cb) {
User
.findOne({ id: currentUserID })
.populate('myLikedTrips')
.populate('userFollowing')
.exec(function (err, user) {
var likedTripIDs = _.pluck(user.myLikedTrips, 'id');
var followingUserIDs = _.pluck(user.userFollowing, 'id');
cb(null, { likedTripIDs, followingUserIDs });
});
},
populatedTrip: ['user', function (results, cb) {
Trip.find({ id: _.pluck(results.user.trips, 'id') })
.populate('comments')
.populate('likes')
.exec(function (err, tripsResults) {
if (err) {
return res.negotiate(err);
}
if (!tripsResults) {
return res.badRequest();
}
cb(null, _.indexBy(tripsResults, 'id'));
});
}],
isLiked: ['populatedTrip', 'me', 'user', function (results, cb) {
var me = results.me;
async.map(results.user.trips, function (trip, callback) {
trip = results.populatedTrip[trip.id];
if (_.contains(me.likedTripIDs, trip.id)) {
trip.hasLiked = true;
} else {
trip.hasLiked = false;
}
callback(null, trip);
}, function (err, isLikedTrip) {
if (err) {
return res.negotiate(err);
}
cb(null, isLikedTrip);
});
}]
},
function finish(err, data) {
if (err) {
console.log('err = ', err);
return res.serverError(err);
}
var userFinal = data.user;
//userFinal.trips = data.isLiked;
userFinal.trips = "test";
return res.json(userFinal);
}
);
},
I tried almost everthing to get this fix but nothing is working...
I am able to get my array of trips(data.isLiked) but I couldn't get my userFInal trips.
I try to set string value on the userFinal.trips:
JSON response
{
"trips": [], // <-- my pb is here !!
"userFollower": [
{
"user": "5777fce1eeef472a1d69bafb",
"follower": "57e44a8997974abc646b29ca",
"id": "57efa5cf605b94666aca0f11"
}
],
"userFollowing": [
{
"user": "57e44a8997974abc646b29ca",
"follower": "5777fce1eeef472a1d69bafb",
"id": "5882099b9c0c9543706d74f6"
}
],
"email": "test2#test.com",
"userName": "dany",
"isPrivate": false,
"bio": "Hello",
"id": "5777fce1eeef472a1d69bafb"
}
Question
How should I do to get my array of trips (isLiked) paste to my user trips array?
Why my results is not what I'm expecting to have?
Thank you for your answers.
Use .toJSON() before overwriting any association in model.
Otherwise default toJSON implementation overrides any changes made to model associated data.
var userFinal = data.user.toJSON(); // Use of toJSON
userFinal.trips = data.isLiked;
return res.json(userFinal);
On another note, use JS .map or _.map in place of async.map as there is not asynchronous operation in inside function. Otherwise you may face RangeError: Maximum call stack size exceeded issue.
Also, it might be better to return any response from final callback only. (Remove res.negotiate, res.badRequest from async.auto's first argument). It allows to make response method terminal

mongoose remove does not remove all documents

I have 2 collections (User and Offer) and I use async to do a series of operations, but the last document is not removed.
async.series([
function (callback) {
User.findOne({_id: req.user._id}, function (err, result) {
if (err) {
return callback(err);
}
user = result;
callback();
});
},
function (callback) {
Offer.find({_owner: user._id}, function (err, offers) {
if (err) {
return callback(err);
}
var i = offers.length;
while (i--) {
var offer = offers[i];
Offer.remove(offer, function (err) {
return callback(err);
});
}
callback();
});
},
function (callback) {
User.remove(user, function (err) {
if (err) {
return callback(err);
}
});
callback();
}
], function (err) {
if (err) {
return res.status(503).json({message: err});
}
res.status(200).json({message: 'User and offers successfully deleted'});
});
I turned mongoose debugging on and I see that 8 documents for this user are removed (as expected). Example message:
Mongoose: users.remove({ active: true, role: 'user', favoredOffers: [],
salt: 'qIADYcMS3SiAuF3M007E9g==', hashedPassword: '**', emailVerified: true,
name: 'Test', email: 'test1#test.com', emailVerificationToken: '***',
updatedAt: new Date("Mon, 21 Sep 2015 00:52:58 GMT"),
createdAt: new Date("Mon, 21 Sep 2015 00:52:58 GMT"),
_id: ObjectId("55ff54ea1d6201ff42ea0045") }) {
safe: { w: 'majority', wtimeout: 10000 } }
When I run a test the result is, that this one document remains. At first I thought my test runs too fast (async.waterfall) but then I switched to async.series and the behaviour remains and when I look into the test-db the document is still there.
Test code:
it.only('should allow deleting my account and all my offers if my password was right', function (done) {
async.series([function (callback) {
login(server, server.delete('*url*'), 'test#test.com', 'testpwd1', function (server) {
server
.send({password: 'testpwd1'})
.expect(200)
.end(callback);
});
}, function (callback) {
User.findOne({email: 'test1#test.com'}, function (err, user) {
callback(user ? new Error('user was still found in mongo') : null); //misuse not found user as 'error'-object
});
}, function (callback) {
Offer.find({_owner: user1._id}, function (err, offers) {
expect(offers).to.have.length(0);
callback();
});
}], done);
});
Result: Uncaught AssertionError: expected [ Array(1) ] to have a length of 0 but got 1
Wether I am missing something or I don't understand the behaviour of the mongdb.
Whilst the operations are being called in series per those that are split in the async.series the problem here is the while loops are not asynchronously controlled, and therefore the callbacks to move to the next stage are being called before all removal is complete.
You could use async.whilst to control the callbacks from the .remove() operations, but that would be overkill since you really should just be issuing ther "query" directly to .remove() rather than trying to work with a list returned by .find():
async.series(
[
function (callback) {
Offer.remove({ "_owner": req.user._id}, callback);
},
function (callback) {
User.remove({ "_id": req.user._id },callback);
}
],
function (err,results) {
if (err) {
res.status(503).json({message: err});
} else {
if ( Array.sum(results) == 0 ) {
res.status(404).json({message: 'User not found'});
} else {
res.status(200).json({message: 'User and offers successfully deleted'});
}
}
}
);
Of course if the result of the remove operations did not remove anything, then you know the input did not match the user.
The async library allows callback passing so you don't need to control each stage. Any errors immediately go to the end block, along with the results of each stage. Also look at parallel instead for this case as one stage is not dependent on the other to complete first.

Is there a better way of dealing with concurrency in Mongo in Node?

I'm working on an API where we send the Last-Modified date header for individual resources returned from our various GETs.
When a client is making a PUT/PATCH on that resource, they can send an If-Unmodified-Since header to ensure that they are only updating the most current version of the resource.
Supporting this is kind of a pain because I want my app to respond to the following use-cases:
the resource you're trying to update doesn't exists (404)
the resource you're trying to update fails a precondition (412)
there was an error processing the request (500)
Is there a better way to do this with Mongo which doesn't involve making 3 separate Mongo db calls in order to capture the possible use cases and return the appropriate response?
I'd like to clean this up:
// the callback the handler is expecting from the model method below
callback(err, preconditionFailed, wasUpdatedBool)
// model method
var orders = this.db.client({ collection: 'orders' });
var query = { _id: this.db.toBSON(this.request.params.id) };
// does the order exist?
orders.findOne(query, function(err, doc) {
if(err) {
return callback(err);
}
if(!doc) {
return callback(null, null, false);
}
// are you updating the most recent version of the doc?
var ifUnModifiedSince = new Date(self.request.headers['if-unmodified-since']).getTime();
if(ifUnModifiedSince) {
query.lastModified = { $lte: ifUnModifiedSince };
orders.findOne(query, function(err, doc) {
if(err) {
return callback(err);
}
if(!doc) {
return callback(null, true);
}
//attempt to update
orders.update(query, payload, function(err, result) {
if(err) {
return callback(err);
}
if(!result) {
return callback(null, null, false);
}
return callback(null, null, result);
});
});
}
//attempt to update
orders.update(query, payload, function(err, result) {
if(err) {
return callback(err);
}
if(!result) {
return callback(null, null, false);
}
callback(null, null, result);
});
});
You have too many queries, as you probably know. The general flow of this should be:
Look for the document by ID - if not found, give a 404
Check the presence of the if-unmodified-since header and compare it with the document's modified date. Issue 412 if applicable
Update the document if allowed
So, your code should look something like this:
var orders = this.db.client({ collection: 'orders' });
var query = { _id: this.db.toBSON(this.request.params.id) };
orders.findOne(query, function(err, order) {
if(err) {
return callback(err); // Should throw a 500
}
if(!order) {
return callback(404);
}
if(self.request.headers['if-unmodified-since']) {
var ifUnModifiedSince = new Date(self.request.headers['if-unmodified-since']).getTime();
if(order.lastModified.getTime() > ifUnModifiedSince) {
return callback(412); // Should throw a 412
}
}
// Now do the update
orders.update(query, payload, function(err, result) {
if(err) {
return callback(err);
}
if(!result) {
return callback(null, null, false);
}
return callback(null, null, result);
});
});

Categories

Resources