如何使用JavaScript中的任意多个文档反序列化转储的BSON?

时间:2019-06-13 16:50:18

标签: javascript node.js mongodb bson

我有一个来自数据库BSON的{​​{1}}文件。假设数据库为mongoexport,集合为todo。现在,我想将数据脱机加载到RN应用程序中。由于该集合可能包含任意多个文档(当前说2个),因此我想使用一种方法来解析该文件,无论它包含多少文档。

我尝试了以下方法:

  1. 使用外部items可执行文件。

我们可以使用外部命令将文件转换为bsondump

JSON

但是我正在开发移动应用程序,因此在shell命令中调用第三方可执行文件不是理想的选择。另外,输出包含几行一线JSON对象,因此从技术上讲输出不是是正确的JSON文件。因此,事后解析并不优雅。

  1. bsondump --outFile items.json items.bson 库中使用deserialize

根据js-bson documentation,我们可以做到

js-bson

但这会引发错误

const bson = require('bson')
const fs = require('fs')
bson.deserialize(fs.readFileSync(PATH_HERE))

并添加此选项,

Error: buffer length 173 must === bson size 94

错误已解决,但仅返回第一个文档。因为文档中没有提到此功能只能解析1-document集合,所以我想知道是否有某些选项可以读取多个文档。

  1. bson.deserialize(fs.readFileSync(PATH_HERE), { allowObjectSmallerThanBufferSize: true }) 中使用deserializeStream
js-bson

但是此方法需要一个文档计数参数(此处为2)。

  1. 使用let docs = [] bson.deserializeStream(fs.readFileSync(PATH_HERE), 0, 2, docs, 0)

我实际上是在使用bson-stream而不是react-native-fetch-blob,并且根据他们的文档,流对象没有fs方法,这是演示的唯一方法在pipe文档中。因此,尽管此方法不需要大量文档,但是我却对如何使用感到困惑。

bson-stream

我也不确定上面的// fs const BSONStream = require('bson-stream'); fs.createReadStream(PATH_HERE).pipe(new BSONStream()).on('data', callback); // RNFetchBlob const RNFetchBlob = require('react-native-fetch-blob'); RNFetchBlob.fs.readStream(PATH_HERE, ENCODING) .then(stream => { stream.open(); stream.can_we_pipe_here(new BSONStream()) stream.onData(callback) });

1 个答案:

答案 0 :(得分:0)

我已经阅读了js-bson的源代码,并找到了解决该问题的方法。我认为最好在这里保留详细的记录:

BSON内部格式

假设我们的.json的{​​{1}}转储为

todo/items.bson

明显破坏 JSON语法。内部BSON具有相似的形状,但似乎BSON 允许将这种多对象填充到一个文件中。

然后对于每个文档,前四个字节指示该文档的长度,包括此前缀本身和后缀。后缀只是一个0字节。

最终的BSON文件类似于

{_id: "someid#1", content: "Launch a manned rocket to the sun"}
{_id: "someid#2", content: "Wash my underwear"}

其中LLLLDDDDDDD0LLLLDDD0LLLLDDDDDDDDDDDDDDDDDDDDDD0... 是长度,L是二进制数据,D实际上是0。

算法

因此,我们可以开发一种简单的算法来获取文档长度,将0bson.deserialize进行比较,这将从缓冲区开始处获取第一个文档,然后将该文档切成薄片并重复。

关于编码

我提到的另一件事是在React Native上下文中进行编码。处理React Native持久性的库似乎都缺乏从文件读取原始缓冲区的支持。我们最接近的选择是allowObjectSmallerThanBufferSize,它是 any 二进制文件的字符串表示形式。然后,我们使用base64Buffer字符串转换为缓冲区并馈入上述算法。

代码

deserialize.js

base64

App.js

const BSON = require('bson');

function _getNextObjectSize(buffer) {
    // this is how BSON 
    return buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
}

function deserialize(buffer, options) {
    let _buffer = buffer;
    let _result = [];

    while (_buffer.length > 0) {
        let nextSize = _getNextObjectSize(_buffer);
        if (_buffer.length < nextSize) {
            throw new Error("Corrupted BSON file: the last object is incomplete.");
        }
        else if (_buffer[nextSize - 1] !== 0) {
            throw new Error(`Corrupted BSON file: the ${_result.length + 1}-th object does not end with 0.`);
        }

        let obj = BSON.deserialize(_buffer, {
            ...options,
            allowObjectSmallerThanBufferSize: true,
            promoteBuffers: true // Since BSON support raw buffer as data type, this config allows
            // these buffers as is, which is valid in JS object but not in JSON
        });
        _result.push(obj);
        _buffer = _buffer.slice(nextSize);
    }

    return _result;
}

module.exports = deserialize;