Reading with find and findOne

Both readers take a filter saying which documents to match and a projection saying which fields to return; an omitted filter matches everything. The difference is what comes back — findOne returns one document or null, find returns a cursor (Cursors and Sorting) that has not yet asked the server for anything.

Filter, projection, and the null that catches people outJavaScript
console.log(db.books.findOne({ year: 2018 }, { _id: 0, title: 1, copies: 1 }));
console.log(db.books.findOne({ year: 1999 }));
console.log(db.books.find({ year: { $gte: 2018 } }).map(d => d.title));
Output
{ title: 'Refactoring', copies: 2 }
null
[ 'Eloquent JavaScript', 'Refactoring', 'The Pragmatic Programmer' ]

A projection is inclusive ({ title: 1 }) or exclusive ({ tags: 0 }), never both — mixing them is an error. _id is the exception: it comes back unless you switch it off with _id: 0. Projecting is not cosmetic, since omitted fields are never serialized into BSON, sent across the socket or parsed by the driver: on fat documents it is the cheapest performance win available.

findOne is find(...).limit(1) with the cursor unwrapped, and its null return is the value that breaks Express 24,430 routes: doc.title on a missing book throws a TypeError in the handler and, without an async error wrapper, becomes a 500 instead of the 404 you meant (Validation and Error Handling).