快速计算BSON文档中对象的数量

4
我希望能够在不通过mongo restore 导入文件到数据库的情况下计算存储在MongoDB BSON文件中的文档数量。 目前我在Python中可以想到的最好方法是:
bson_doc = open('./archive.bson','rb')
it = bson.decode_file_iter(bson_doc)
total = sum(1 for _ in it)
print(total)

这在理论上是可行的,但在bson文档较大时实际操作速度很慢。有没有更快的方法来计算bson文档中文档数量而不需要完全解码?我目前正在使用Python 2.7和pymongo。 https://api.mongodb.com/python/current/api/bson/index.html

你在使用哪个BSON软件包?这是MongoDB的BSON还是其他的? - J'e
我已经更新了问题并提供了以下信息。我正在使用的bson包是https://api.mongodb.com/python/current/api/bson/index.html。 - user1438162
1个回答

5
我没有文件来尝试,但我相信有一种方法——如果您手动解析数据。

bson.decode_file_iter的源代码(去掉文档字符串)大致如下:

_UNPACK_INT = struct.Struct("<i").unpack

def decode_file_iter(file_obj, codec_options=DEFAULT_CODEC_OPTIONS):
    while True:
        # Read size of next object.
        size_data = file_obj.read(4)
        if len(size_data) == 0:
            break  # Finished with file normaly.
        elif len(size_data) != 4:
            raise InvalidBSON("cut off in middle of objsize")
        obj_size = _UNPACK_INT(size_data)[0] - 4
        elements = size_data + file_obj.read(obj_size)
        yield _bson_to_dict(elements, codec_options)

我猜测,耗时的操作是_bson_to_dict的调用 - 而您不需要它。
所以,您只需要读取文件 - 获取下一个文档大小的int32值并跳过它。然后计算在这样做时遇到了多少个文档。
因此,我相信,这个函数应该可以解决问题:
import struct
import os
from bson.errors import InvalidBSON

def count_file_documents(file_obj):
    """Counts how many documents provided BSON file contains"""
    cnt = 0
    while True:
        # Read size of next object.
        size_data = file_obj.read(4)
        if len(size_data) == 0:
            break  # Finished with file normaly.
        elif len(size_data) != 4:
            raise InvalidBSON("cut off in middle of objsize")
        obj_size = struct.Struct("<i").unpack(size_data)[0] - 4
        # Skip the next obj_size bytes
        file_obj.seek(obj_size, os.SEEK_CUR)
        cnt += 1
    return cnt

(我还没有测试过代码,手头没有MongoDB。)

你的函数只需添加一个导入语句 'import struct' - https://docs.python.org/2/library/struct.html# - user1438162
是的,再加上几个导入(os.SEEK_CURInvalidBSON 异常)。我已经编辑了答案。 - drdaeman
@user1438162 如果您用 bson._UNPACK_INT(...) 替换 struct.Struct("<i").unpack(...) 的话,可能会有一些额外的加速效果(或者将 _UNPACK_INT 复制到自己的代码中以不依赖于“私有”API)。不同之处在于 Struct 对象只会被创建一次,这可能会在包含许多小型文档的大型 BSON 文件上节省一些毫秒。尽管如此,与文件 I/O 成本相比,我相信差异会微不足道。 - drdaeman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接