What is a Cursor in MongoDB?

Here’s a comparison between toArray() and cursors after a find() in the Node.js MongoDB driver. Common code:

var MongoClient = require('mongodb').MongoClient,
assert = require('assert');

MongoClient.connect('mongodb://localhost:27017/crunchbase', function (err, db) {
    assert.equal(err, null);
    console.log('Successfully connected to MongoDB.');

    const query = { category_code: "biotech" };

    // toArray() vs. cursor code goes here
});

Here’s the toArray() code that goes in the section above.

    db.collection('companies').find(query).toArray(function (err, docs) {
        assert.equal(err, null);
        assert.notEqual(docs.length, 0);

        docs.forEach(doc => {
            console.log(`${doc.name} is a ${doc.category_code} company.`);
        });

        db.close();
    });

Per the documentation,

The caller is responsible for making sure that there
is enough memory to store the results.

Here’s the cursor-based approach, using the cursor.forEach() method:

    const cursor = db.collection('companies').find(query);

    cursor.forEach(
        function (doc) {
            console.log(`${doc.name} is a ${doc.category_code} company.`);
        },
        function (err) {
            assert.equal(err, null);
            return db.close();
        }
    );
});

With the forEach() approach, instead of fetching all data in memory, we’re streaming the data to our application. find() creates a cursor immediately because it doesn’t actually make a request to the database until we try to use some of the documents it will provide. The point of cursor is to describe our query. The second parameter to cursor.forEach shows what to do when an error occurs.

In the initial version of the above code, it was toArray() which forced the database call. It meant we needed ALL the documents and wanted them to be in an array.

Note that MongoDB returns data in batches. The image below shows requests from cursors (from application) to MongoDB:

MongoDB cursor graphic

forEach scales better than toArray because we can process documents as they come in until we reach the end. Contrast it with toArray – where we wait for ALL the documents to be retrieved and the entire array is built. This means we’re not getting any advantage from the fact that the driver and the database system are working together to batch results to your application. Batching is meant to provide efficiency in terms of memory overhead and the execution time. Take advantage of it in your application, if you can.

Leave a Comment