Node.js mongodb/mongoose mapreduce-将所有值合并到单个数组

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' }, {

我正在开发一个运行在node.js上的小型应用程序,它通过MongooseORM连接到mongodb。其中一个模型是个人模型 模型架构:

{
    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之间的主要区别