Node.js mongodb/mongoose mapreduce-将所有值合并到单个数组
我正在开发一个运行在node.js上的小型应用程序,它通过MongooseORM连接到mongodb。其中一个模型是个人模型 模型架构:Node.js mongodb/mongoose mapreduce-将所有值合并到单个数组,node.js,mongodb,mongoose,mapreduce,aggregation-framework,Node.js,Mongodb,Mongoose,Mapreduce,Aggregation Framework,我正在开发一个运行在node.js上的小型应用程序,它通过MongooseORM连接到mongodb。其中一个模型是个人模型 模型架构: { id : Number, name : String concatVals : String } 例如: [ { id : 1, name : 'jerry' friends : 'adam#peter#robert#steven' }, {
{
id : Number,
name : String
concatVals : String
}
例如:
[
{
id : 1,
name : 'jerry'
friends : 'adam#peter#robert#steven'
},
{
id : 2,
name : 'tony'
friends : 'richard#robert#steven'
},
{
id : 3,
name : 'mike'
friends : 'henry#steven#jerry#adam#tony'
},
{
id : 4,
name : 'peter'
friends : 'jerry#bill#bobby#steven#mike#paul'
}
]
如您所见,friends字段基本上是一个字符串,包含由“#”分隔的名称。为什么friends字段作为字符串而不是数组存在,这是一个重要原因。所以我们不能改变它的类型或结构。
这个“好友列表”在真实数据库中实际上要长得多。如您所见,这些对象中的大多数都有相交的朋友列表(steven出现在多个文档中)
目标:
我需要找出有效地分割每个文档中的friends字段的方法,将其转换为一个数组,并将所有不同的朋友列表填充到人的子集中。因此,基本上,当我询问“tony”和“mike”的人时,我想要得到的结果是:
[
{
name : jerry,
id : 1,
friends : 'adam#peter#robert#steven'
},
{
name : tony,
id : 2,
friends : 'richard#robert#steven'
},
{
richard ...
},
{
henry ...
},
{
steven ...
},
{
robert ...
},
{
adam ...
}
] // POPULATED friends of tony and mike
问题是数据量巨大,所以我希望将尽可能多的计算转移到数据库端,在服务器端进行最少的数据处理。到目前为止,我的解决方案如下所示:
Person.mapReduce({
map: function() {
emit(this.name, this.friends.split('#'));
},
reduce: function(key, values) {
return values;
},
query: {
name: {
$in: ['tony', 'mike']
}
},
out: 'friends_output'
}, // at this point we have docs with friends String splitted into array
function(err, mapReduceObject) {
mapReducePipeline.aggregate(
{ $unwind: '$value'},
{
$group: {_id: '$value'} // distinct friend docs
},
{
// combining all distinct friends
$group: {
_id: null,
allValues: { $addToSet: '$_id'}
}
},
function(err, data) {
console.log(data[0].allValues)
// here I get the list of names, not populated docs
});
});
这样,我就部分实现了我的目标:我能够得到“托尼”和“迈克”的所有不同的朋友。但我希望填充这些朋友,但在mapreduce期间找不到填充他们的好方法。
当然,我可以在函数(err,data)中进行另一个DB调用,并在查询中使用姓名获取人员
...
},
function(err, data) {
Persons.find({name : data[0].allValues},
function(err, friends){
console.log(friends);
}
);
});
但在此过程中,总计3 DB的呼叫:
-地图还原
-聚集
-搜索查询
最后一个.find()电话一直困扰着我。在mapreduce或aggregate中,您是否发现了填充好友的方法?如果您对我的问题有完全不同的解决方案,请分享。为什么不使用阵列?如果您这样做了,您可以在mongo中使用各种简洁的技巧来处理数据(例如,在带有“field”:“value”的数组中查找一个值)。如果您需要这种散列格式的数据,您只需在get时将其连接起来,使用a将它们散列在一起,而不是相反,您的数据将更紧密地反映其模型。由于这一切都定义了一种关系,因此可能也适用,但可能会使事情变得更加迟钝。这里有一个例子,其中“朋友”是一种单向关系,如“跟随”。我正在使用,所以所有的东西都以正确的顺序保存
var async = require('async');
// return all unique valuesin an Array.filter
var filterUnique = function(value, index, self) { return self.indexOf(value) === index; };
var PersonSchema = new mongoose.Schema({
'name': String,
'_friends': [{ type: mongoose.Schema.Types.ObjectId, ref: 'Person' }]
});
PersonSchema.virtual('friends').get(function () {
return this['_friends'].map(function(f){ return f.name; }).join('#');
});
PersonSchema.methods.addFriend = function (friend) {
this['_friends'] = this['_friends'] || [];
this['_friends'].push(friend);
this['_friends'] = this['_friends'].filter(filterUnique);
}
var Person = mongoose.model('Person', PersonSchema);
function generatePeople(cb){
var generatePerson = function(name, cb){
Person({"name": name}).save(cb);
}
async.map(['Paul', 'Peter', 'Mary', 'Emily', 'David', 'Christy'], generatePerson, cb);
}
function addFriendsPaul(cb){
Person.findOne({"name":"Paul"}, function(err, Paul){
var addFriend = function(person, cb){
person.addFriend(Paul);
person.save(cb);
// paul adds them back
Paul.addFriend(person);
Paul.save();
}
Person.find({"name":{"$ne":"Paul"}}, function(err, people){
async.map(people, addFriend, cb);
});
});
}
function addFriendsDavid(cb){
Person.findOne({"name":"David"}, function(err, David){
var addFriend = function(person, cb){
person.addFriend(David);
person.save(cb);
}
Person.find({"name":{"$ne":"David"}}, function(err, people){
async.map(people, addFriend, cb);
});
});
}
async.series([
generatePeople,
addFriendsPaul,
addFriendsDavid,
function(){
Person.findOne({"name":"Paul"})
.populate('_friends')
.exec(function(err, Paul){
console.log('Paul:', Paul.friends);
})
}
]);
Require和Import之间的主要区别