請問各位大大
我現在想要使用mongo的aggregation
來實作avg這項功能
我原本的code是用find的方式
router.get('/test', function(req, res,next){
data.findById(req.params.id,function(err, test){
res.render('test', {
test:test.score
});
我現在想要改成aggregation 請問該如何是好呢
查了滿多aggregation使用方法都是直接使用沒有看到這種的
我還有試一種方法就是前端拿到資料後用JS來算
不過好像沒辦法...
原始資料
/* 1 */
{
"_id" : ObjectId("5db1b9fabfe18dffc8f7fc68"),
"name" : "Jack",
"quizs" : [
10,
20,
30
]
}
/* 2 */
{
"_id" : ObjectId("5db1b9fabfe18dffc8f7fc6a"),
"name" : "Amy",
"quizs" : [
30,
40,
50
]
}
/* 3 */
{
"_id" : ObjectId("5db1b9fabfe18dffc8f7fc6c"),
"name" : "Ryan",
"quizs" : [
25,
35,
45
]
}
code
db.getCollection('ithelp').aggregate([
{
$addFields: {
avg: {
$avg: '$quizs'
}
}
},
{
$match: {
avg: {
$gte: 30
}
}
}
]);
result
/* 1 */
{
"_id" : ObjectId("5db1b9fabfe18dffc8f7fc6a"),
"name" : "Amy",
"quizs" : [
30,
40,
50
],
"avg" : 40.0
}
/* 2 */
{
"_id" : ObjectId("5db1b9fabfe18dffc8f7fc6c"),
"name" : "Ryan",
"quizs" : [
25,
35,
45
],
"avg" : 35.0
}
說明
透過 $avg
算出 quizs
的平均
並透過 $addFields
新增一個 avg
column
再透過 $match
找尋符合條件的資料 ({avg: { $gte: 30 } })
所以你原本的 query 可以寫在 $match
裡
我還有試一種方法就是前端拿到資料後用JS來算
不過好像沒辦法...
說 mongodb 沒辦法可能有些狀況還有可能
說 js 沒辦法應該不太可能
而且也應該是在後端算
不過這樣算完之後不是整個data都算嗎
所以我findById是要寫在 $match裡這樣嗎
不過這樣算完之後不是整個data都算嗎
是阿
aggregate 是會照順序執行的
所以你不想整個算
你可以調整裡面的順序
e.g.
db.getCollection('ithelp').aggregate([{ $match: { name: { $ne: 'Jack'}}}, { $addFields: { avg: { $avg: '$quizs'}}} ]);
上面的 code 會先找到 name 不等於 Jack
然後才算平均、新增一個 avg
column
所以我findById是要寫在 $match裡這樣嗎
對的
dragonH
請問一下 我現在是先update資料然後把資料存進array裡
再把aggregate寫在update成功後執行那邊
不過好像都沒反應
這樣的寫法是不行的嗎
貼個 code 吧
dragonH抱歉最近忙考試
name:String,
address:String,
comments:[{
content:String,
point:[Number],
}], 這是database
router.post('/comment', (req, res) => {
const { postid, content,points } = req.body;
const point=[points,point1,point2];
const obj={content:content,point:point};
Article.updateMany({ _id: postid }, { $push: { comments: obj } },function(err,house){
if(err){
throw err;
}else{
Article.aggregate([{ $addFields: { avg: { $avg: '$point'}}} ]);
res.redirect('/articles/house')
console.log();
}
});
大概是這樣
現在point裡面可以存數值了我想把它做平均
但都沒反應
dragonH
我是直接去資料庫看
內容
comments:Array
0:Object
point:[4,5,6]
_id:objectId("")
content:""
1:Object
point:[1,2,3]
_id:objectId("")
content:""
但都沒新增FIELD
Article.aggregate([{ $addFields: { avg: { $avg: '$point'}}} ],(err,result)=>{
Article.updateMany({_id: postid},{$push:{comments:result}});
console.log(result);
});
剛剛查一下才發現原來aggregate是查詢的語法
現在有點沒方向 我update應該是要把aggregate的結果update上去對吧
但我發現我這樣打的result會有每一筆資料
是的
不過你可以先看一下
是不是有必要存這平均值在 database
還是說這平均只有 query 時 會用到
只有 query 時 會用到
那就 query 時 用 aggregate
就好
不用去 update
如果你真要寫進 database 的話
其實也不用先用 aggregate 算平均
可以用一般的 query 把資料拿回來後
在後端算完平均在 update
然後你的寫法應該有點問題
這樣寫他應該會把整個 result
push 到 comments
裡吧
dragonH
因為我平均可能會做兩次
自從資料庫越來越多層後 思緒也就開始越來越亂了
就是把每個point都先平均 然後再平均一次
這樣有什麼比較簡單的方法嗎
還有就是 要怎麼單獨把aggregate後 想要的東西push到comments裡
剛剛有試過把 comments:avg這樣 不過他直接說undefined
aggregate 跟一般的 query 一樣
也是會回傳給你一個 data array
接下來就是一般的 array 的操作呀
可以用 Array.map
重新組一個含有平均值的 data array
再用迴圈根據每筆 data 的 objectID 來更新
const data =[{"post":{"point":[5],"_id":"5dbaa1d3125dcd4200c41133","content":"123"},"avg":5},{"post":{"point":[6,1,3],"_id":"5dbfdf105012092068f9b2af","content":"123"},"avg":3.3333333333333335}]
;
let sum = 0;
let itemsFound = 0;
const len = data.length;
let item = null;
for (let i = 0; i < len; i++) {
item = data[i];
if (item.found) {
sum = item.avg + sum;
itemsFound = itemsFound + 1;
}
}
const avg = sum / itemsFound;
console.log("avg:", avg);
請問一下大大
我現在想做一個總平均的
不過我算出來都是NaN
請問是哪邊算錯了嗎
我查出來的object是
Object {
avg: 5,
post: Object {
_id: "5dbaa1d3125dcd4200c41133",
content: "123",
point: [5]
}
}
把值印出來看
console.log(sum, itemsFound)
會發現兩個都是 0
js 0/0
本來就會等於 NaN (Not a Number)
兩個都等於 0 的原因是因為
我不知道你的 item.found
是什麼
估計電腦也不知道
所以就沒執行 if ()
裡面的 code
最後兩個都是預設值 0
另外如果是不同的問題
建議開另一個發問
不然別人很難爬文