如何获得Mongoose / node的平均评分

时间:2015-01-09 02:48:15

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

我有一个星级评级指令在angularjs的前端工作,我可以将评级保存到评级集合。 这是我的评级架构/型号:

var mongoose = require('mongoose');

module.exports = mongoose.model('Rating', {
    bourbonId:   {type: mongoose.Schema.ObjectId, ref: 'Bourbon'},
    userId:   {type: mongoose.Schema.ObjectId, ref: 'User'},
    rating:     {type: Number, required: true},
    ratingId : {type: mongoose.Schema.ObjectId}

});

以下是我需要平均评分的项目:

 'use strict';

var mongoose = require('mongoose'),
    BourbonSchema = null;

module.exports = mongoose.model('Bourbon', {
    BourbonId:    {type: mongoose.Schema.ObjectId},
    name:  {type: String, required: true},
    blog:  {type: String, required: true},
    photo: {type: String, required: true},
    ratings: {type: mongoose.Schema.ObjectId, ref: 'Rating'},
    rating: {type: Number}

});

var Bourbon = mongoose.model('Bourbon', BourbonSchema);
module.exports = Bourbon;

我需要找到一种方法来匹配波旁威士忌ID。从查看堆栈溢出,似乎使用聚合函数可能是要走的路。 stack overflow link

这是我控制器中当前损坏的代码。我知道它已经离开,以及我曾尝试使用async.map尝试解决此问题的失败尝试:

    'use strict';

var Bourbon = require('../../../models/bourbon'),
    Rating = require('../../../models/rating');
    //async = require('async');

module.exports = {
    description: 'Get Bourbons',
    notes: 'Get Bourbons',
    tags:['bourbons'],
    handler: function(request, reply){
        Bourbon.find(function(err, bourbons){
            Bourbon.findOne(id, 'Rating', function(err, bourbon){
                Rating.aggregate([
                    {$match: {bourbonId: {$in: bourbon.ratings}}},
                    {$group: {bourbonId: bourbon._id, average: {$avg: '$rating'}}}
                ], function(err, result){
                    bourbon.rating = result;
                    reply({bourbons:bourbons});
                    console.log('Bourbs', bourbons);
                });
            });
        });
    }
};

任何帮助将不胜感激。把头撞到砖墙上,现在只丢掉随机码。 ..


这是我实施的内容: 模型:

'use strict';

var mongoose = require('mongoose'),
    BourbonResultSchema = null;


module.exports = mongoose.model('BourbonResult', {
    _Id:    {type: mongoose.Schema.ObjectId, 'ref': 'Bourbon'},
    avgRating: {type: Number}

});


var BourbonResult = mongoose.model('BourbonResult', BourbonResultSchema, null);
module.exports = BourbonResult;

控制器:

  'use strict';

var Bourbon = require('../../../models/bourbon'),
    Rating = require('../../../models/rating'),
    BourbonResult = require('../../../models/bourbonResult');
    //async = require('async');

module.exports = {
    description: 'Get Bourbons',
    notes: 'Get Bourbons',
    tags:['bourbons'],
    handler: function(request, reply){

            Rating.aggregate(
                [
                    {'$group':{
                        '_id': '$bourbonId',
                        'avgRating': {'$avg': '$rating'}
                    }}
                ],
                function(err,bourbons){
                    // Map plain results to mongoose document objects
                    bourbons = bourbons.map(function(result){
                        return new BourbonResult(result);
                    });

                    Bourbon.populate(bourbons,{'path': '_id'},function(err,bourbons){
                        reply({bourbons:bourbons});
                        console.log('BourbsRESSSSSS', JSON.stringify(bourbons, undefined, 2));
                    });
                }
            );

    }
};

这是我从控制台日志中得到的信息:

BourbsRESSSSSS [ { _id: 
     { _id: 54acf382894ee2bcdebbc7f5,
       name: 'example2',
       photo: 'http://aries-wineny.com/wp-content/uploads/2014/09/woodford-reserve.jpg',
       blog: 'example2',
       __v: 0 },
    avgRating: 3.3333333333333335 },
  { _id: 
     { _id: 54a77e0fe63c850000f1269c,
       name: 'example',
       photo: 'http://aries-wineny.com/wp-content/uploads/2014/09/woodford-reserve.jpg',
       blog: 'example',
       __v: 0 },
    avgRating: 3 } ]

=============================================== =========================

完美!

1 个答案:

答案 0 :(得分:1)

如果您要做的是在输出中列出每个“Bourbon”的“平均”评级,则可能有几种方法。但更清洁的方法之一是在特殊对象模型上使用mongoose populate来表示聚合结果的结构。

除了“波本威士忌”之外,你似乎没有“评级”的任何其他“类型”,所以你只想集合整个集合。

// Set up a schema and model to match result structure
var bourbonResultSchema = new Schema({
    "_id": { "type": Schema.Types.ObjectId, "ref": "Bourbon" },
    "avgRating": Number
});

// The "null" for the collection is because there will not be any physical storage
var BourbonResult = mongoose.model( "BourbonResult", bourbonResultSchema, null );


// Aggregate an mapping code

Rating.aggregate(
    [
        { "$group": {
            "_id": "$bourbonId",
            "avgRating": { "$avg": { "$ifNull": ["$rating",0 ] } }    
        }}
    ],
    function(err,results) {
        if (err) throw err;

        // Map plain results to mongoose document objects
        results = results.map(function(result) {
            return new BourbonResult(result);
        });

        Bourbon.populate(results,{ "path": "_id" },function(err,results) {
            if (err) throw err;
            reply(results);
            console.log( JSON.stringify( results, undefined, 2 ) );
        })
    }
);

因此,您定义了一个与聚合返回的结果结构相匹配的模式和模型。这样做可以让您稍后致电.populate()

从聚合返回的结果不是mongoose文档,而只是普通对象。然后,通过传递BourbonResult方法将所有结果转换为.map()个对象,以便返回BourbonResult的数组。

由于这些不是mongoose文档,因此可以调用.populate()的模型方法,该方法将一组mongoose文档作为第一个参数。第二个“options”参数告诉方法哪个字段路径用于popluation,前面定义的_id用于引用Bourbon模型。

在回调.populate()时,返回的结果会合并聚合返回的平均分数和Bourbon字段中的完整_id对象。如果您真的希望,您还可以在每个.populate()对象上运行更多Bourbon语句,以便引入其中的任何引用。有点复杂但可能。

注意,“波本威士忌”模型中的“bourbonId”字段可能有点多余。除非另有说明,否则MongoDB始终具有唯一的_id字段,引用的对象链接使用的实际值是该字段。即使您需要像BourbonResult那样在那里定义引用,那么您也可以这样做。


包含修正架构示例的完整列表:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var userSchema = new Schema({
  "name": String
});

var ratingSchema = new Schema({
  "bourbonId": { "type": Schema.Types.ObjectId, "ref": "Bourbon" },
  "userId": { "type": Schema.Types.ObjectId, "ref": "User" },
  "rating": { "type": Number, "required": true }
});

var bourbonSchema = new Schema({
  "name": { "type": String, "required": true },
  "blog": { "type": String, "required": true },
  "photo": { "type": String, "required": true },
  "ratings": [{ "type": Schema.Types.ObjectId, "ref": "Rating" }],
  "rating": { "type": Number }
});

var bourbonResultSchema = new Schema({
  "_id": { "type": Schema.Types.ObjectId },
  "avgRating": Number
});

var User = mongoose.model( "User", userSchema ),
    Rating = mongoose.model( "Rating", ratingSchema ),
    Bourbon = mongoose.model( "Bourbon", bourbonSchema ),
    BourbonResult = mongoose.model(
      "BourbonResult", bourbonResultSchema, null );


mongoose.connect("mongodb://localhost/bourbon");

async.waterfall(
  [
    function(callback) {
      async.each([User,Rating,Bourbon],function(model,callback) {
        model.remove({},callback);
      },
      function(err) {
        callback(err);
      });
    },

    function(callback) {
      Bourbon.create({
        "name": 'test',
        "blog": 'test',
        "photo": 'test'
      },callback);
    },

    function(bourbon,callback) {
      User.create({ "name": 'ted' },function(err,user) {
        if (err) callback(err);
        Rating.create({
          "bourbonId": bourbon,
          "userId": user,
          "rating": 5
        },function(err,rating1) {
          callback(err,user,bourbon,rating1)
        });
      });
    },

    function(user,bourbon,rating1,callback) {
      Rating.create({
        "bourbonId": bourbon,
        "userId": user,
        "rating": 7
      },function(err,rating2) {
        callback(err,bourbon,rating1,rating2);
      });
    },

    function(bourbon,rating1,rating2,callback) {
      Bourbon.findById(bourbon.id,function(err,bourbon) {
        bourbon.ratings.push(rating1,rating2);
        bourbon.save(function(err,bourbon) {
          callback(err)
        });
      });
    },

    function(callback) {
      Rating.aggregate(
        [
          { "$group": {
            "_id": "$bourbonId",
            "avgRating": { "$avg": { "$ifNull": ["$rating", 0 ] } }
          }},
        ],
        function(err,results) {
          console.log(results);

          results = results.map(function(result) {
            return new BourbonResult(result);
          });

          Bourbon.populate(
            results,
            { "path": "_id" },
            function(err,results) {
              console.log(results);
              callback(err);
            }
          )

        }
      );
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
)

提供输出:

[ { _id: 54af7581efc755470845005c, avgRating: 6 } ]
[ { _id:
     { _id: 54af7581efc755470845005c,
       name: 'test',
       blog: 'test',
       photo: 'test',
       __v: 1,
       ratings: [ 54af7581efc755470845005e, 54af7581efc755470845005f ] },
    avgRating: 6 } ]