将mongoose流转换为数组

时间:2015-01-30 08:05:03

标签: node.js mongodb stream mongoose

我曾与mongodb合作,但对于猫鼬ORM来说却是一个新手。我试图从集合中获取数据,而explain()输出显示50ms。通过mongoose获取数据的总时间是9秒。这是查询:

Node.find({'dataset': datasetRef}, function (err, nodes){
   // handle error and data here
});

然后我在我查询的字段上应用了索引。 explain()输出现在显示4ms。但是通过mongoose检索数据的总时间没有变化。然后我搜索了一下,发现使用lean()可以帮助使用非常接近本地mongodb的mongoose读取查询的性能

所以我将查询更改为:

Node.find({'dataset': datasetRef})
.lean()
.stream({transform: JSON.stringify})
.pipe(res)

这完全解决了性能问题。但最终的结果是像这样的JSON文档流:

{var11: val11, var12: val12}{var21: val21, var22: val22} ...

如何解析这个以形成一系列文档?或者我应该不使用流?在我看来,如果我计划在后端形成数组,那么使用流是没有意义的,因为我将不得不等待所有文档被读入内存。但我也认为在前端解析和创建整个数组可能代价高昂。

在这种情况下如何在不堵塞网络的情况下实现最佳性能?

更新

我正在尝试使用直播来解决此问题。但是,我无法在JSON对象之间插入逗号。请参阅以下代码:

res.write("[");

var through = require('through');
var tr = through(
  function write(data){
    this.queue(data.replace(/\}\{/g,"},{"));
  }
);

var dbStream = db.node.find({'dataset': dataSetRef})
.lean()
.stream({'transform': JSON.stringify});

dbStream.on("end", function(){
    res.write("]");
});

dbStream
.pipe(tr)
.pipe(res);

有了这个,我可以在开头得到“[”,在结尾得到“]”。但是,仍然无法获得模式“} {”替换为“},{”。不确定我做错了什么

更新2

现在弄明白为什么替换不起作用。看来,由于我已将转换函数指定为JSON.stringify,因此它一次读取一个JSON对象,因此永远不会遇到模式}{,因为它一次不会选择多个JSON元素。

现在我修改了我的代码,并编写了一个自定义转换函数,它执行JSON.stringify,然后在末尾附加一个逗号。我在这里遇到的唯一问题是我不知道它何时是流中的最后一个JSON对象。因为我不想在这种情况下附加逗号。此刻,一旦遇到结束,我会附加一个空的JSON对象。但不知何故,这似乎并不是一个令人信服的想法。这是代码:

res.write("[");
function transform(data){
    return JSON.stringify(data) + ",";
}

var dbStream = db.node.find({'dataset': dataSetRef})
.lean()
.stream({'transform': transform});

dbStream.on("end", function(){
    res.write("{}]");
});

dbStream
.pipe(res);

4 个答案:

答案 0 :(得分:5)

  

我在这里遇到的唯一问题是我不知道它何时是流中的最后一个JSON对象。

但你确实知道哪一个是第一个。知道这一点,你可以将它添加到除第一个之外的每个对象,而不是附加逗号。为此,请在闭包内设置转换函数:

function transformFn(){

    var first = true;

    return function(data) {

        if (first) {

            first = false;
            return JSON.stringify(data);
        }
        return "," + JSON.stringify(data);
    }
}

现在你可以调用该函数并将其设置为实际转换。

var transform = transformFn();
res.write("[");
var dbStream = db.node.find({'dataset': dataSetRef})
.lean()
.stream({'transform': transform});

dbStream.on("end", function(){
    res.write("]");
});

dbStream
.pipe(res);

答案 1 :(得分:4)

@cbajorin和@rckd都给出了正确答案。

然而,一直重复这段代码似乎很痛苦。

因此我的解决方案使用额外的Transform流来实现同样的目的。

    import { Transform } from 'stream'

class ArrayTransform extends Transform {
    constructor(options) {
        super(options)
        this._index = 0
    }

    _transform(data, encoding, done) {
        if (!(this._index++)) {
            // first element, add opening bracket
            this.push('[')
        } else {
            // following element, prepend comma
            this.push(',')
        }
        this.push(data)
        done()
    }

    _flush(done) {
        if (!(this._index++)) {
            // empty
            this.push('[]')
        } else {
            // append closing bracket
            this.push(']')
        }
        done()
    }
}

反过来可以用作:

const toArray = new ArrayTransform();
Model.find(query).lean().stream({transform: JSON.stringify })
    .pipe(toArray)
    .pipe(res)

编辑:添加了空白支票

答案 2 :(得分:1)

我喜欢@ cdbajorin的解决方案,所以我创建了一个更易读的版本(ES6):

Products
    .find({})
    .lean()
    .stream({
        transform: () => {
            let index = 0;
            return (data) => {
                return (!(index++) ? '[' : ',') + JSON.stringify(data);
            };
        }() // invoke
    })
    .on('end', () => {
        res.write(']');
    })
    .pipe(res);

答案 3 :(得分:0)

    var mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost/shoppingdb');
    var Sports = mongoose.model('sports', {});

    var result = [];
    var prefix_out = "your info";

      Sports.find({"goods_category": "parts"}).
      cursor().
      on("data", function(doc){
        //stream ---> string
          var str = JSON.stringify(doc)
        //sring ---> JSON
          var json = JSON.parse(str);
        //handle Your Property
          json.handleYourProperty = prefix_out + json.imageURL;              
          result.push(result);
      }).
      on('error', function(err){
          console.log(err);
      }).
      on('close', function(){
          console.log(result);
      });