mongodb/node-mongodb-native — pull request #1207 by watson (2014-09-14 11:10:42+00:00)
Title: Force the projection argument for find/findOne

This pull request requires that the optional options argument should always be the 3rd argument. This means that if you do not want to specify any fields (aka projection), you will have to provide an empty hash:

Before:

```javascript
collection.find(query, options, callback);
```

Now:

```javascript
collection.find(query, {}, options, callback);
```

Ok, I'll admit that this is an opinionated and breaking change. But first let me demonstrate the problem I encountered that let me to propose this change:

I had a collection with a set of documents that each looked roughly like so:

```javascript
{ _id: 1, name: "foo", max: 1000, min: 50, foo: "...", bar: "..." }
```

Now I did a query to get all `name`, `max` and `min` for each document:

```javascript
collection.find({}, { _id: 0, name: 1, max: 1, min: 1 }, callback);
```

Instead of getting an array of documents in the callback, the mongo driver (v1.4.10) instead threw an error:

```
MongoError: Can't canonicalize query: BadValue $max must be a BSONObj
at Object.toError (/tmp/node_modules/mongodb/lib/mongodb/utils.js:114:11)
at /tmp/node_modules/mongodb/lib/mongodb/cursor.js:703:54
at Cursor.close (/tmp/node_modules/mongodb/lib/mongodb/cursor.js:986:5)
at commandHandler (/tmp/node_modules/mongodb/lib/mongodb/cursor.js:703:21)
at /tmp/node_modules/mongodb/lib/mongodb/db.js:1847:9
at Server.Base._callHandler (/tmp/node_modules/mongodb/lib/mongodb/connection/base.js:445:41)
at /tmp/node_modules/mongodb/lib/mongodb/connection/server.js:478:18
at MongoReply.parseBody (/tmp/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js:68:5)
at null. (/tmp/node_modules/mongodb/lib/mongodb/connection/server.js:436:20)
at emit (events.js:95:17)
```

Researching it, I found that there exists [a secret list of magic property names](https://github.com/mongodb/node-mongodb-native/blob/927692e43c90d16832f8b1b3789c472b5df4ddfd/lib/mongodb/collection/query.js#L10-L12) that if used in a projection will treat that projection as an options argument instead. And of cause if you treat `{ _id: 0, name: 1, max: 1, min: 1 }` as options, weird things are bound to happen.

This behaviour is both undocumented (as far as I can see) and works differently than how the same command would work if entered into the mongo console.

A solution to this is easy, just change the above command to the following (notice the extra `{}`):

```javascript
collection.find({}, { _id: 0, name: 1, max: 1, min: 1 }, {}, callback);
```

By explicitly giving `find` an empty options hash you overwrite the magic behaviour - but this took quite some digging and I would expect this to be the source of a lot of frustration out there.

I know this might not make it into master, but hopefully we can at least have a discussion about the issue and how this confusion can be avoided in other ways. I apologies in advance if this have already been discussed elsewhere, but I couldn't find any mention of it.