2

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?

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
ckjavi70
  • 168
  • 3
  • 10

2 Answers2

1

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();
  }
);
Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
0

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.

wdberkeley
  • 11,531
  • 1
  • 28
  • 23
  • The command does not return in "natural" order unless you actually tell it to do otherwise. The `_id` primary index is used by default and you can supply a sort specification. `finOne()` in fact is really only just a wrapper for `.find()` that just returns one document. Just as with `.find()` there are other ways to provide sort and other query modifiers. – Neil Lunn Jul 18 '14 at 05:41
  • @NeilLunn: if there are no query or sort options specified, a `find()` (or `findOne()`) will use natural order (not the `_id` index). See also: [What does Mongo sort on when no sort order is specified?](http://stackoverflow.com/questions/11599069) .. or try a `.find.explain()` to confirm a `BasicCursor` is being used. – Stennie Jul 30 '14 at 23:45