If I try to insert a document in MongoDB with some key starting with $
I get an error message:
> db.x.insert({"a": {"$b": "1"}})
2016-09-29T21:14:23.078+0200 E QUERY [thread1] Error: field names cannot start with $ [$b] :
...
(I have observer a similar behaviour using Node.js driver)
However, running the following C++ program:
#include <cstdlib>
#include <iostream>
#include "mongo/client/dbclient.h" // for the driver
// compile with: g++ test.cpp -pthread -lmongoclient -lboost_thread -lboost_system -lboost_regex -o test
void run() {
mongo::DBClientConnection c;
c.connect("localhost");
mongo::BSONObj doc = BSON("a" << BSON("$b" << 1));
c.insert("test.x", doc);
}
int main() {
mongo::client::initialize();
try {
run();
} catch( const mongo::DBException &e ) {
std::cout << "caught " << e.what() << std::endl;
}
return EXIT_SUCCESS;
}
I'm able to insert it, as find()
shows:
> db.x.find()
{ "_id" : ObjectId("57ed67fdbf3a716e16f6d102"), "a" : { "$b" : 1 } }
Thus, it seems that C++ driver is able to "bypass" the document structure rules described in MongoDB documentation. Is there any explanation for this behaviour? It could "break" MongoDB database in some way (I guess that that limitation is for a good reason and having documents in the DB not honoring it could be problematic)
I have observed that this happens only when the key with $
is not at first level. For example, if I use
mongo::BSONObj doc = BSON("$b" << 1);
then I get a consistent error
caught OperationException: { index: 0, code: 2, errmsg: "Document can't have $ prefixed field names: $b", op: { _id: ObjectId('57ed6a015365c193cbbb3231'), $b: 1 } }
Just in case it is needed, I'm using MongoDB 3.2.0 and legacy C++ driver 1.0.7
As of MongoDB 3.2, the server performs some insertion-time validation of key names (including forbidding them from starting with the dollar sign character) at the top level of the document, but it doesn't validate any key names in subdocuments. See https://jira.mongodb.org/browse/SERVER-10987 for the ticket tracking the request to validate key names in subdocuments.
Some drivers perform additional key name validation (the shell and the Node.js driver check for dollar sign characters at the beginning of all keys in documents for insertion, for example), but the set of validation rules is currently not consistent between drivers. Coincidentally, there's a relatively recent ticket filed in the DRIVERS project (which is used for coordinating new features and improvements in a consistent fashion across all drivers) in JIRA at https://jira.mongodb.org/browse/DRIVERS-308, which includes an attempt to specify what client-side validation should be performed at insertion time. If that ticket ends up moving forward, then a fix will certainly make it into the latest version of the C++ driver (note that the legacy driver is only receiving critical bug fixes these days).
As far as key names with dollar signs go: the server will store and retrieve them just fine, but they won't play nice with other database features. See, for example:
> db.version()
3.2.10
> db.collection.find()
{ "_id" : 1, "a" : { "$b" : 1 } }
> db.collection.update({_id: 1}, {$set: {"a.$b": 2}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 52,
"errmsg" : "The dollar ($) prefixed field '$b' in 'a.$b' is not valid for storage."
}
})