【问题标题】:convert mongoose stream to array将猫鼬流转换为数组
【发布时间】:2015-03-29 16:15:11
【问题描述】:

我曾使用过 mongodb,但对 mongoose ORM 还是很陌生。我试图从集合中获取数据,并且 explain() 输出显示为 50 毫秒。通过 mongoose 获取数据的总时间为 9 秒。这是查询:

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

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

所以我将查询更改为:

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);

【问题讨论】:

  • 您是否尝试过在第一个查询中传递精益选项? Node.find({'dataset': datasetRef}).lean().exec(function (err, nodes) {...});
  • 是的。问题是文档之前作为单个数组存储在文档中并存储在 gridfs 中。因此,在请求文档的早些时候,响应在几毫秒内开始流式传输。现在,如果我从那个模型转移到这个规范化模型,消除 gridfs,我需要使用流,否则响应会从后端延迟。
  • 是否有可能使用直通流将 }{ 等模式替换为 },{ ?我正在尝试但无法这样做

标签: node.js mongodb stream mongoose


【解决方案1】:

我在这里面临的唯一问题是我不知道它何时是流中的最后一个 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);

【讨论】:

    【解决方案2】:

    我喜欢@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】:

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

      但是,一直重复这段代码似乎很痛苦。

      因此我的解决方案使用额外的转换流来实现相同的目的。

          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)
      

      编辑:添加了空检查

      【讨论】:

        【解决方案4】:
            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);
              });
        

        【讨论】:

          猜你喜欢
          • 2014-04-21
          • 2014-06-20
          • 1970-01-01
          • 2021-07-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-06
          • 1970-01-01
          相关资源
          最近更新 更多