将猫鼬流转换为数组
convert mongoose stream to array
我曾使用 mongodb 但对 mongoose ORM 还很陌生。我试图从集合中获取数据,explain() 输出显示 50 毫秒。通过猫鼬获取数据的总时间为 9 秒。这是查询:
Node.find({'dataset': datasetRef}, function (err, nodes){
// handle error and data here
});
然后我在查询的字段上应用了索引。 explain() 输出现在显示 4 毫秒。但是通过 mongoose 检索数据的总时间没有改变。然后我搜索了一下,发现使用 lean() 可以帮助使猫鼬中读取查询的性能非常接近原生 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);
有了这个,我就可以得到开头的“[”和结尾的“]”。但是,仍然无法将 patten "}{" 替换为 "},{"。不确定我做错了什么
更新 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);
The only problem I am facing here is that I don't know when it is the last JSON object in the stream.
但你知道哪个是第一个。知道这一点,而不是附加逗号,您可以将它添加到除第一个对象之外的每个对象之前。为此,请在闭包内设置转换函数:
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);
我喜欢@cdbajorin 的解决方案,所以我创建了一个更具可读性的版本 (ES6):
Products
.find({})
.lean()
.stream({
transform: () => {
let index = 0;
return (data) => {
return (!(index++) ? '[' : ',') + JSON.stringify(data);
};
}() // invoke
})
.on('end', () => {
res.write(']');
})
.pipe(res);
@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)
编辑:添加了空检查
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);
});
我曾使用 mongodb 但对 mongoose ORM 还很陌生。我试图从集合中获取数据,explain() 输出显示 50 毫秒。通过猫鼬获取数据的总时间为 9 秒。这是查询:
Node.find({'dataset': datasetRef}, function (err, nodes){
// handle error and data here
});
然后我在查询的字段上应用了索引。 explain() 输出现在显示 4 毫秒。但是通过 mongoose 检索数据的总时间没有改变。然后我搜索了一下,发现使用 lean() 可以帮助使猫鼬中读取查询的性能非常接近原生 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);
有了这个,我就可以得到开头的“[”和结尾的“]”。但是,仍然无法将 patten "}{" 替换为 "},{"。不确定我做错了什么
更新 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);
The only problem I am facing here is that I don't know when it is the last JSON object in the stream.
但你知道哪个是第一个。知道这一点,而不是附加逗号,您可以将它添加到除第一个对象之外的每个对象之前。为此,请在闭包内设置转换函数:
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);
我喜欢@cdbajorin 的解决方案,所以我创建了一个更具可读性的版本 (ES6):
Products
.find({})
.lean()
.stream({
transform: () => {
let index = 0;
return (data) => {
return (!(index++) ? '[' : ',') + JSON.stringify(data);
};
}() // invoke
})
.on('end', () => {
res.write(']');
})
.pipe(res);
@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)
编辑:添加了空检查
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);
});