9

我有一个 REST 方法,它应该返回一个 JSON 数组,其中包含从 mongodb 读取的某些元素(使用 mongoose)。

它应该非常简单(在实际情况下 find 方法有参数,但这不是问题):

OutDataModel.find().stream({transform: JSON.stringify}).pipe(res);

这种方法的问题是我没有得到有效的 JSON,因为结果是这样的:

{"id":"1","score":11}{"id":"2","score":12}{"id":"3","score":13}

我期待这个:

[{"id":"1","score":11},{"id":"2","score":12},{"id":"3","score":13}]

我一直无法找到解决方案,但我很确定会有一个简单的解决方案。

我试过的:

没有什么让我感到自豪的,但它就在这里。

  1. 在流式写入'['响应之前。
  2. 我提供了另一种调用 JSON.stringify 并','在末尾添加 a 的方法,而不是 JSON.stringify
  3. 在流的“结束”事件中,我写入']'响应。

仍然没有使用这个“解决方案”,因为我在结尾有一个逗号,如下所示:

 [{"id":"1","score":11},{"id":"2","score":12},{"id":"3","score":13},]

正如我所说,我很确定应该有一个干净的解决方案,因为它应该很常见。

这个方法会有很多并发调用,所以我不想把所有的东西都读到内存中,然后把所有的东西都写到响应中。每次调用都不会返回很多记录,但它们加在一起可能会很大。消费者是一个带有spring的java应用程序,使用jackson解析JSON。

请让我知道该怎么做。

回答

通过按照接受的答案中的建议创建一个转换流,我让它工作了。

我的流看起来像这样:

var arraystream = new stream.Transform({objectMode: true});
arraystream._hasWritten = false;


arraystream._transform = function (chunk, encoding, callback) {
    console.log('_transform:' + chunk);
    if (!this._hasWritten) {
        this._hasWritten = true;
        this.push('[' + JSON.stringify(chunk));

    } else {
        this.push(',' + JSON.stringify(chunk));
    }
    callback();
};

arraystream._flush = function (callback) {
    console.log('_flush:');
    this.push(']');
    callback();

};

以及使用它的代码:

OutDataModel.find().stream().pipe(arraystream).pipe(res);

谢谢。

4

2 回答 2

3

You're on the right track by implementing your own logic. You could also make use of the ArrayFormatter here which is doing something similar: https://gist.github.com/aheckmann/1403797

The transform function gets called on each document individually -- a Mongoose QueryStream emits a single document per 'data' event but QueryStream isn't treating them semantically as part of any larger array data structure; to get an array-formatted JSON, you really do have to do it yourself (as you have surmised).

于 2015-02-27T18:20:32.473 回答
2

I found a very simple and clean solution here: convert mongoose stream to array

That's my simplified version of the code:

Products
    .find({})
    .lean()
    .stream({
        transform: () => {
            let index = 0;
            return (data) => {
                return (!(index++) ? '[' : ',') + JSON.stringify(data);
            };
        }() // invoke
    })
    .on('end', () => {
        res.write(']');
    })
    .pipe(res);
于 2015-12-28T21:02:49.713 回答