如何在猫鼬/节点中获得平均评分

How to get a rating average in Mongoose / node

我在 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 } ]

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

完美!

如果您要做的是在您的输出中列出每个 "Bourbon" 的 "average" 评级,可能有几种方法。但更简洁的方法之一是在表示聚合结果结构的特殊对象模型上使用 mongoose 填充。

除了 "bourbon" 之外,您在 "Ratings" 中似乎没有任何其他 "types",因此按理说您只想汇总整个集合。

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

因此,您定义了一个架构和模型,该架构和模型将匹配return聚合结果的结构。这样做是为了稍后您可以调用 .populate()

return聚合的结果不是 mongoose 文档,而是普通对象。然后,通过 .map() 方法将所有结果传递给 BourbonResult 对象,以便 return 一个 BourbonResult.

数组

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

.populate() 的回调中,returned 结果合并了聚合的平均得分 return 和 [=18= 中的完整 Bourbon 对象] 场地。如果您真的愿意,您还可以 运行 在每个 Bourbon 对象上进一步 .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 } ]