0

如果我尝试在 MongoDB 中插入一个以某个键开头的文档,$则会收到一条错误消息:

> db.x.insert({"a": {"$b": "1"}})
2016-09-29T21:14:23.078+0200 E QUERY    [thread1] Error: field names cannot start with $ [$b] :
...

(我有一个使用 Node.js 驱动程序的类似行为的观察者)

但是,运行以下 C++ 程序:

#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;
}

我可以插入它,如图find()所示:

> db.x.find()
{ "_id" : ObjectId("57ed67fdbf3a716e16f6d102"), "a" : { "$b" : 1 } }

因此,C++ 驱动程序似乎能够“绕过” MongoDB 文档中描述的文档结构规则。这种行为有什么解释吗?它可能会以某种方式“破坏” MongoDB 数据库(我想这种限制是有充分理由的,并且数据库中的文档不尊重它可能是有问题的)

我观察到只有当密钥$不在第一级时才会发生这种情况。例如,如果我使用

mongo::BSONObj doc = BSON("$b" << 1);

然后我得到一个一致的错误

caught OperationException: { index: 0, code: 2, errmsg: "Document can't have $ prefixed field names: $b", op: { _id: ObjectId('57ed6a015365c193cbbb3231'), $b: 1 } }

以防万一,我使用的是 MongoDB 3.2.0 和旧版 C++ 驱动程序 1.0.7

4

1 回答 1

1

从 MongoDB 3.2 开始,服务器在文档的顶层执行一些键名的插入时验证(包括禁止它们以美元符号字符开头),但它不验证子文档中的任何键名。请参阅https://jira.mongodb.org/browse/SERVER-10987以获取跟踪请求以验证子文档中的密钥名称的票证。

一些驱动程序执行额外的键名验证(例如,shell 和 Node.js 驱动程序在文档中所有键的开头检查美元符号字符以进行插入),但验证规则集当前在驱动程序之间不一致。巧合的是,在https://jira.mongodb.org/browse/DRIVERS-308的 JIRA 中的 DRIVERS 项目(用于以一致的方式协调所有驱动程序的新功能和改进)中有一张相对较新的票,其中包括尝试指定在插入时应执行哪些客户端验证。如果该票最终继续前进,那么修复肯定会使其成为最新版本的 C++ 驱动程序(请注意,旧版驱动程序这些天只接收关键错误修复)。

就带有美元符号的键名而言:服务器将很好地存储和检索它们,但它们不会与其他数据库功能配合得很好。参见,例如:

> 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."
    }
})
于 2016-09-30T21:41:36.317 回答