【问题标题】:mongodb / mongoose mapreduce - concat all values to single arraymongodb / mongoose mapreduce - 将所有值连接到单个数组
【发布时间】:2013-12-15 04:32:48
【问题描述】:

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

{
    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 字段基本上是一个字符串,其中包含用“#”分隔的名称。朋友字段作为字符串而不是数组存在的一个重要原因。所以我们不能改变它的类型或结构。 这个“朋友列表”在真实数据库中实际上要长得多。如您所见,这些对象中的大多数都会有相交的朋友列表(steven 出现在多个文档中)。

目标: 我需要找出有效分割每个文档中的朋友字段的方法,将其转换为一个数组,并列出所有不同填充人子集的朋友。所以基本上我在询问“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
    });
});

这样我就部分地实现了我的目标:我能够结交“tony”和“mike”的所有不同朋友。但我希望填充这些朋友,但我找不到在 mapreduce 期间填充它们的好方法。 当然,我可以在 function(err, data) 中进行另一个 DB 调用,并在查询中使用名称获取 Persons

...
},
function(err, data) {
    Persons.find({name : data[0].allValues},
        function(err, friends){
            console.log(friends);
        }
    );
});

但在此过程中总共有 3 个 DB 调用: - 地图减少 - 聚合 - 搜索查询

最后一个 .find() 电话一直困扰着我。您是否看到任何在 mapreduce 或聚合期间/期间填充朋友的方法?如果您对我的问题有完全不同的解决方案,请分享。

【问题讨论】:

    标签: node.js mongodb mongoose mapreduce aggregation-framework


    【解决方案1】:

    Require 和 Import 之间的主要区别

    【讨论】:

      【解决方案2】:

      为什么不使用数组?如果你这样做了,你可以在 mongo 中使用各种巧妙的技巧来处理你的数据(例如,用“field”:“value”在数组中查找一个值。)如果你需要这种散列格式的数据,你可以只需加入它,使用virtual getter 将它们散列在一起,而不是相反,您的数据将更接近地反映它的模型。由于这一切都定义了一种关系,populate 也可能是合适的,但可能会使事情变得更加迟钝。这是一个示例,其中“朋友”是一种单向关系,例如“关注”。我正在使用async,所以所有的东西都以正确的顺序保存。

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

      【讨论】:

        猜你喜欢
        • 2014-04-08
        • 2022-08-08
        • 2019-03-14
        • 2018-07-10
        • 2017-01-18
        • 1970-01-01
        • 2019-10-23
        • 1970-01-01
        • 2015-05-07
        相关资源
        最近更新 更多