【问题标题】:Modify Mongodb source code to disable auto-generating "_id" field for doc修改 Mongodb 源代码以禁用 doc 的自动生成“_id”字段
【发布时间】:2017-07-17 14:35:47
【问题描述】:

我想修改编译最新的mongodb源代码(截至目前,mongodb版本v3.5.10-34-g27d21e6 from github)使_id字段不自动生成如果插入文档时未提供。当前官方发布的 mongodb 需要存在这个字段(我已经在 mongodb-win32-x86_64-v3.4-latest from https://www.mongodb.org 上测试过这个事实)。

我发现了一个相关问题Mongodb inserting doc without _id field。但我认为我的问题与这个问题不重复,因为我的问题是要求修改源代码,而这个问题是关于参数设置(其答案也不再适用于当前版本的 mongodb)。

我知道官方的mongodb组有他们的理由和考虑要求这样一个_id字段,既然他们已经决定引入这样的要求,他们不太可能会在不久的将来删除它未来。而且我也知道禁用 _id 会影响 mongodb 的某些功能,例如上面提到的复制。但我仍然想知道如何禁用它,因为我只是将它用作个人数据库,在一台机器上只运行一个实例。我确信我不需要其他功能。存储是我考虑最多的,所以我不希望这个字段占用额外的磁盘空间。出于同样的原因,我需要具有压缩能力的 WiredTiger 引擎,所以我需要对相对较新的 mongodb 版本执行此操作。

到目前为止,我已经使用安装了VS2015的scons成功编译并链接了原始源代码v3.5.10-34-g27d21e6。所以我希望有人能告诉我应该修改哪些源代码文件/文件的哪些代码段/段以禁用自动生成 _id 字段;以及哪些文件也需要“修补”,以检查 _id 的存在,如果找不到,即使它们并不真正需要它也会拒绝正常工作。

我发现以下两个文件似乎相关:

1) mongo/src/mongo/db/ops/insert.cpp 行:153-177

我尝试如下修改这个文件,但之后 _id 仍然是自动创建的

来自:

if (firstElementIsId) {
        b.append(doc.firstElement());
        i.next();
    } else {
        BSONElement e = doc["_id"];
        if (e.type()) {
            b.append(e);
        } else {
            b.appendOID("_id", NULL, true);
        }
    }

    while (i.more()) {
        BSONElement e = i.next();
        if (hadId && e.fieldNameStringData() == "_id") {
            // no-op
        } else if (e.type() == bsonTimestamp && e.timestampValue() == 0) {
            auto nextTime = LogicalClock::get(service)->reserveTicks(1);
            b.append(e.fieldName(), nextTime.asTimestamp());
        } else {
            b.append(e);
        }
    }

到:

if (firstElementIsId) {
        //b.append(doc.firstElement());
        i.next();
    } else {
        BSONElement e = doc["_id"];
        if (e.type()) {
            //b.append(e);
        } else {
            //b.appendOID("_id", NULL, true);
        }
    }

    while (i.more()) {
        BSONElement e = i.next();
        if (hadId && e.fieldNameStringData() == "_id") {
            // no-op
        } else if (e.fieldNameStringData() == "_id") {
            // no-op
        } else if (e.type() == bsonTimestamp && e.timestampValue() == 0) {
            auto nextTime = LogicalClock::get(service)->reserveTicks(1);
            b.append(e.fieldName(), nextTime.asTimestamp());
        } else {
            b.append(e);
        }
    }

2) mongo/src/mongo/bson/bsonobj.cpp 行:281-315

我没有修改这个文件,因为我没有看到哪一行代码可以添加 _id 字段,只是感觉它可能与它在评论中提到的问题有关。

/* note: addFields always adds _id even if not specified
   returns n added not counting _id unless requested.
*/
int BSONObj::addFields(BSONObj& from, set<string>& fields) {
    verify(isEmpty() && !isOwned()); /* partial implementation for now... */

    BSONObjBuilder b;

    int N = fields.size();
    int n = 0;
    BSONObjIterator i(from);
    bool gotId = false;
    while (i.moreWithEOO()) {
        BSONElement e = i.next();
        const char* fname = e.fieldName();
        if (fields.count(fname)) {
            b.append(e);
            ++n;
            gotId = gotId || strcmp(fname, "_id") == 0;
            if (n == N && gotId)
                break;
        } else if (strcmp(fname, "_id") == 0) {
            b.append(e);
            gotId = true;
            if (n == N && gotId)
                break;
        }
    }

    if (n) {
        *this = b.obj();
    }

    return n;
}

所以,这就是我现在所做的。 我不熟悉 mongodb 源代码结构,这对我来说确实是一个巨大的项目。期待一份详细的待办事项列表,我可以按照它来修改代码,而无需太多关于 mongodb 工作原理的知识。但是,当然,欢迎提供任何其他类型的相关信息。提前致谢。

【问题讨论】:

  • 根据您提到的相关问题,插入文档时可以覆盖默认的_id 值。你破解服务器代码的目的是什么?这似乎是一条很长的路,可能会节省一些字节,并且除非您彻底测试,否则可能会导致细微的损坏。如果您要进行更改,我建议使用稳定分支 (v3.4) 而不是开发分支 (v3.5 / master)。如果您喜欢 WiredTiger 但不想要分布式数据库或查询计划器的好处/开销,您也可以考虑直接使用WiredTiger library
  • @Stennie 关于节省的空间: 是的,对于每个文档,可能只能节省几个字节。但在我的情况下,每个文档本身通常都很短,_id 字段仍然占据不可忽略的容量比例,并且会随着添加大量文档而累积。由于这是供个人使用,我不能像公司那样添加更多磁盘,而且我的 PC 上的磁盘空间已经不足。所以我只想通过压缩来压缩磁盘空间,并尽可能地删除不必要的字段。
  • @Stennie 关于难度: 我对mongodb的源码不熟悉,对这个hacking的难度没有明确的认识。我希望源代码设计得如此周到,以至于会有一些神奇的开关等着我去发现,它们可以很容易地做到这一点,只有我不熟悉代码才能轻松找到它/它们.我仍然希望是这样。
  • @Stennie 关于版本: 是的,我同意,v3.4 会更好,我会试试的,谢谢你的提示。 关于WiredTiger,我认为你的推荐对我很有吸引力,因为它最能满足我的要求。但是考虑到学习如何使用 WiredTiger 库进行编程所需的难度和时间投入,除了它支持压缩和行级锁定之外,我目前对此一无所知,我认为修补 Mongodb 的一些代码对我来说似乎相对更可取和实用.但我会开门的,谢谢你的信息。
  • 为什么不重命名您已经使用“_id”的现有字段之一(然后在创建集合时确保_id 上不存在唯一索引)?

标签: c++ mongodb


【解决方案1】:

在您发布的第三个代码 sn-p 中,将 bool gotId = false 替换为 bool gotId = true 可能会使其行为如您所愿 - 但也可能会破坏。

您提到您开始这样做的原因是为了提高存储性能 - 删除一个字段真的会产生相当大的影响吗?如果确实如此,我会感到惊讶。更有可能的是,您确实想要的某些功能会中断(也许现在不会,也许在很长一段时间内,在你最不期望的地方)。

除了警告,你说你想要:

一个详细的待办事项列表,我可以按照它来修改代码

您不太可能找到对 MongoDB 源足够熟悉的人,他们可以告诉您它是否安全/不安全以及如何在 StackOverflow 上删除此字段 - 您更有可能通过尝试联系开发团队,或者至少联系向open source effort 添加代码的人。不过,作为最后的警告,即使在那里,您也不太可能找到愿意花时间列出这些步骤的人。像这样的修改往往需要花费大量时间来了解源 - 没有简单的解决方法。

【讨论】:

  • 我刚刚尝试将bool gotId = false 替换为bool gotId = true,但没有成功,仍然会生成_id。我认为可以达到的存储性能取决于插入的文档的长度。如果每一个都很短,比如我的情况,对我来说还是值得一试的。我知道在 stackoverflow 上一定很难找到 To-Do 列表,但是由于我想要的功能是 mongodb 官方花了时间考虑不再支持的功能,所以我认为这个问题更适合这里。但我仍然会尝试从开发团队中找人来帮助我。谢谢你的回答。
【解决方案2】:

你现在尝试做的事情是行不通的。 _id 本质上是主键,并且在代码中的许多地方都假设存在 _id 字段(当然是唯一的)。如果您对主键的概念不是很熟悉,可以将其视为数据库中的唯一记录标识。没有身份证明 == 没有记录。

您寻求的具体好处是什么?您应该改写您的问题并询问人们如何实现这一目标。

祝你好运!

【讨论】:

  • _id 在 mongodb 中默认是唯一的。当插入没有指定 _id 的文档时,mongodb 将自动创建一个 ObjectId 类型并通过在 _id上应用唯一索引来确保其唯一性>。但是,如果在创建集合时指定了 {autoIndexID : false},则不会在 _id 上创建唯一索引,因此不会执行重复检查;现在如果带有插入了重复的 _id,则 _id 字段在集合中将是非唯一的。 autoIndexID 声称自 v3.2 起已弃用,但在我对 v3.5.10-34-g27d21e6 的测试中,它仍然有效。
  • 我正在寻求的是通过禁用自动生成 _id 字段及其相应索引来减少一些额外的存储空间。我知道有一些解决方法,例如在创建集合时将 autoIndexID 设置为 false 以禁用索引并为 _id 分配一个具有最小长度值的伪值,以抑制系统自动生成一个长值。但是我觉得结果集合对于那些我知道我永远不会使用的 _id 伪值并不优雅。另外,如果可以完全删除 _id 文件,我可以获得额外的存储空间减少。所以我想修改代码。
  • 你为什么不把最小的 _id 字段传给他们呢?说空。
  • @AsyaKamsky 如果我最终无法找到修改源代码的正确方法,我想我会通过尽可能小的 _id 作为解决方法。但我仍然希望可以修改代码以完全禁用 _id 字段,以使存储在 db 中的数据看起来更整洁。顺便说一句,正如您所建议的, null 看起来是一个不错的候选人。那么你有关于 null 需要多少位的信息吗?谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-13
  • 1970-01-01
  • 2018-09-04
相关资源
最近更新 更多