Nodejs的简单$ avg查询

时间:2017-01-03 03:37:11

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

这是我必须制作应用程序的所有代码。所以我能够从csv文件导入数据/记录,或者从localhost:3000处的应用程序手动插入,它将被插入到数据库中。从那里我将能够从这里查询。错误在于底部的server.js。批量和平均值似乎都没有执行。

server.js,包括批量(1000data以上)和平均查询(air_temperature)

var express = require('express');
var app = express();
var mongojs = require('mongojs');
var db = mongojs('meibanlist', ['meibanlist']);
var bodyParser = require('body-parser');

app.use(express.static(__dirname + '/public'));
app.use(bodyParser.json());

app.get('/meibanlist', function (req, res) {
  console.log('I received a GET request');

  db.meibanlist.find(function (err, docs) {
    console.log(docs);
    res.json(docs);
  });
});

app.post('/meibanlist', function (req, res) {
  console.log(req.body);
  db.meibanlist.insert(req.body, function(err, doc) {
    res.json(doc);
  });
});

app.delete('/meibanlist/:id', function (req, res) {
  var id = req.params.id;
  console.log(id);
  db.meibanlist.remove({_id: mongojs.ObjectId(id)}, function (err, doc) {
    res.json(doc);
  });
});

app.get('/meibanlist/:id', function (req, res) {
  var id = req.params.id;
  console.log(id);
  db.meibanlist.findOne({_id: mongojs.ObjectId(id)}, function (err, doc) {
    res.json(doc);
  });
});

app.put('/meibanlist/:id', function (req, res) {
  var id = req.params.id;
  console.log(req.body.machine_unit);
  db.meibanlist.findAndModify({
    query: {_id: mongojs.ObjectId(id)},
    update: {$set: {machine_unit: req.body.machine_unit, air_temperature: req.body.air_temperature, water_temperature: req.body.water_temperature, heat_temperature: req.body.heat_temperature, room_temperature: req.body.room_temperature, date: req.body.date, time: req.body.time}},
    new: true}, function (err, doc) {
      res.json(doc);
    }
  );
});

var cursor = db.meibanlist.find({"air_temperature": { "$exists": true, "$type": 2 }}),
    bulkUpdateOps = [];

cursor.forEach(function(doc){ 
    var newTemp = parseInt(doc.air_temperature, 10);
    bulkUpdateOps.push({ 
        "updateOne": {
            "filter": { "_id": doc._id },
            "update": { "$set": { "air_temperature": newTemp } }
         }
    });

    if (bulkUpdateOps.length === 500) {
        db.meibanlist.bulkWrite(bulkUpdateOps);
        bulkUpdateOps = [];
    }
});         

if (bulkUpdateOps.length > 0) { db.meibanlist.bulkWrite(bulkUpdateOps); }

db.meibanlist.aggregate([
    {
        "$group": {
            "_id": null,
            "averageAirTemperature": { "$avg": "$air_temperature" } 
        }
    }
], function(err, results){
    if (err || !results) console.log ("record not found");
    else console.log(results[0]["averageAirTemperature"]);        
}); 

app.listen(3000);
console.log("Server running on port 3000");

的index.html

<!DOCTYPE>
<html ng-app="myApp">
<head>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">

<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css">

  <title>Meiban App</title>
</head>
<body>
  <div class="container" ng-controller="AppCtrl">
    <h1>Meiban App</h1>

    <table class="table">
      <thead>
        <tr>
          <th>Machine unit</th>         
          <th>Air Temperature</th>
          <th>Water Temperature</th>
          <th>Heat Temperature</th>       
          <th>Room Temperature</th>
          <th>Date</th>
          <th>Time</th>
          <th>Action</th>

          <th>&nbsp;</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td><input class="form-control" ng-model="contact.machine_unit"></td>
          <td><input class="form-control" ng-model="contact.air_temperature"></td>
          <td><input class="form-control" ng-model="contact.water_temperature"></td>
          <td><input class="form-control" ng-model="contact.heat_temperature"></td>
          <td><input class="form-control" ng-model="contact.room_temperature"></td>
          <td><input class="form-control" ng-model="contact.date"></td>
          <td><input class="form-control" ng-model="contact.time"></td>
          <td><button class="btn btn-primary" ng-click="addCollection()">Add Collection</button></td>
          <td><button class="btn btn-info" ng-click="update()">Update</button>&nbsp;&nbsp;<button class="btn btn-info" ng-click="deselect()">Clear</button></td>
        </tr>
        <tr ng-repeat="contact in collection">
          <td>{{contact.machine_unit}}</td>
          <td>{{contact.air_temperature}}</td>
          <td>{{contact.water_temperature}}</td>
          <td>{{contact.heat_temperature}}</td>
          <td>{{contact.room_temperature}}</td>
          <td>{{contact.date}}</td>
          <td>{{contact.time}}</td>
          <td><button class="btn btn-danger" ng-click="remove(contact._id)">Remove</button></td>
          <td><button class="btn btn-warning" ng-click="edit(contact._id)">Edit</button></td>
        </tr>
      </tbody>
    </table>

  </div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.12/angular.min.js"></script>
<script src="controller/controller.js"></script>
</body>
</html>

controller.js

var myApp = angular.module('myApp', []);
myApp.controller('AppCtrl', ['$scope', '$http', function($scope, $http) {
    console.log("Hello World from controller");


var refresh = function() {
  $http.get('/meibanlist').success(function(response) {
    console.log("I got the data I requested");
    $scope.meibanlist = response;
    $scope.contact = "";
  });
};

refresh();

$scope.addCollection = function() {
  console.log($scope.contact);
  $http.post('/meibanlist', $scope.contact).success(function(response) {
    console.log(response);
    refresh();
  });
};

$scope.remove = function(id) {
  console.log(id);
  $http.delete('/meibanlist/' + id).success(function(response) {
    refresh();
  });
};

$scope.edit = function(id) {
  console.log(id);
  $http.get('/meibanlist/' + id).success(function(response) {
    $scope.contact = response;
  });
};  

$scope.update = function() {
  console.log($scope.contact._id);
  $http.put('/meibanlist/' + $scope.contact._id, $scope.contact).success(function(response) {
    refresh();
  })
};

$scope.deselect = function() {
  $scope.contact = "";
}

}]);

1 个答案:

答案 0 :(得分:1)

正如文档所指出的, $avg 运算符可以在两个管道步骤中运行, $project {{3} } 阶段。我相信您需要在 $group 管道中使用 $avg 累加器,因为您需要所有价格的平均值。

$group 阶段使用时, $group 会返回将指定表达式应用到的所有数值的集体平均值按_id字段标注的密钥共享同一组的一组文档中的每个文档。

_id字段是必填字段;但是,由于您要计算所有输入文档的累计平均值,因此您可以指定_id null的值:

db.database.aggregate([
    {
        "$group": {
            "_id": null,
            "Average": { "$avg": "$price" } 
        }
    }
], function(err, results){
    if (err !results) console.log ("record not found");
    else console.log(results[0]["Average"]);        
});

整理评论中的信息以及您问题的后续更新,您遇到了麻烦,因为您的值不是数字,因此 $avg 无法正常工作。

您需要使用 $avg 方法将字符串值转换为数字值。这里的概念是update(),对于游标中的每个文档,使用 loop through your collection with a cursor 使用 $set

假设您的集合不是那么庞大,上面的直觉可以使用游标的 parseInt() 方法来实现,以迭代它并更新集合中符合特定条件的每个文档。

以下mongo shell演示强调了针对小型数据集的这种方法:

mongo shell

db.meibanlist.find({"air_temperature": { "$exists": true, "$type": 2 }})
    .snapshot()
    .forEach(function(doc){ 
        var newTemp = parseInt(doc.air_temperature, 10);
        db.meibanlist.updateOne(
            { "_id": doc._id },
            { "$set": { "air_temperature": newTemp } }
        );
    });

现在,为了提高性能,特别是在处理大型集合时,请利用 forEach() API批量更新集合。

与上述操作相比,这是非常有效的,因为使用批量API,您将批量发送操作到服务器(例如,批量大小为500),这会让您更好 性能,因为您不会将每个请求发送到服务器,而是每500个请求中只发送一次,从而使您的更新更有效,更快捷。

以下示例演示如何使用MongoDB版本>= 2.6< 3.2中提供的 Bulk() API。

mongo shell

var bulkUpdateOps = db.meibanlist.initializeUnOrderedBulkOp(),   
    counter = 0;

db.meibanlist.find({"air_temperature": { "$exists": true, "$type": 2 }})
    .snapshot()
    .forEach(function(doc){ 
        var newTemp = parseInt(doc.air_temperature, 10);
        bulkUpdateOps.find({ "_id": doc._id })
            .update({ "$set": { "air_temperature": newTemp } })

        counter++;  // increment counter for batch limit
        if (counter % 500 === 0) { 
            // execute the bulk update operation in batches of 1000
            bulkUpdateOps.execute(); 
            // Re-initialize the bulk update operations object
            bulkUpdateOps = db.meibanlist.initializeUnOrderedBulkOp();
        } 
})

// Clean up remaining operation in the queue
if (counter % 500 !== 0) { bulkUpdateOps.execute(); }

下一个示例适用于自Bulk() deprecated API以来的MongoDB版本3.2及更高版本,并使用 {{3}提供了一套更新的api }

它使用与上面相同的游标,但使用相同的 Bulk() 游标方法创建具有批量操作的数组,以将每个批量写入文档推送到数组。因为写命令可以接受不超过1000次的操作, 您需要将操作分组以进行最多1000次操作,并在循环达到1000次迭代时重新初始化该数组。如果您的收藏很大,上面的计数器变量可以有效地管理您的批量更新。它允许您批量更新操作,并以500的批量将写入发送到服务器,这样可以提供更好的性能,因为您不会向服务器发送每个请求,每500个请求中只发送一次。

对于批量操作,MongoDB对每个批处理施加了1000个操作的默认内部限制,因此在对批量大小有一定控制而不是让MongoDB强加默认值的情况下,选择500个文档是好的,即对于大型操作在>的大小> 1000份文件。

var cursor = db.meibanlist.find({"air_temperature": { "$exists": true, "$type": 2 }}),
    bulkUpdateOps = [];

cursor.forEach(function(doc){ 
    var newTemp = parseInt(doc.air_temperature, 10);
    bulkUpdateOps.push({ 
        "updateOne": {
            "filter": { "_id": doc._id },
            "update": { "$set": { "air_temperature": newTemp } }
         }
    });

    if (bulkUpdateOps.length === 500) {
        db.meibanlist.bulkWrite(bulkUpdateOps);
        bulkUpdateOps = [];
    }
});         

if (bulkUpdateOps.length > 0) { db.meibanlist.bulkWrite(bulkUpdateOps); }

更新集合后,您可以在aggregate()函数中愉快地应用 bulkWrite() 运算符。以下内容将返回所有合并文件的平均值air_temperature

db.meibanlist.aggregate([
    {
        "$group": {
            "_id": null,
            "averageAirTemperature": { "$avg": "$air_temperature" } 
        }
    }
], function(err, results){
    if (err !results) console.log ("record not found");
    else console.log(results[0]["averageAirTemperature"]);        
}); 

<强>更新

以上代码适用于mongo shell。对于mongojs版本,将操作封装在可以在需要时调用的函数中。例如,您可以将批量更新逻辑放在其自己的函数bulkUpdate中:

function bulkUpdate(collection, callback)
    var ops = [];

    collection.find({
        "air_temperature": { "$exists": true, "$type": 2 }
    }, function (err, docs) {
        docs.forEach(function(doc){ 
            ops.push({ 
                "updateOne": {
                    "filter": { "_id": doc._id },
                    "update": { 
                        "$set": { 
                            "air_temperature": parseInt(doc.air_temperature, 10),
                            "water_temperature": parseInt(doc.water_temperature, 10),
                            "heat_temperature": parseInt(doc.heat_temperature, 10),
                            "room_temperature": parseInt(doc.room_temperature, 10)
                        } 
                    }
                 }
            });

            if (ops.length === 500) {
                collection.bulkWrite(bulkUpdateOps, function(err, result){
                    if (err) callback(err);
                    callback(null, result);
                });
                ops = [];
            }
        });         

        if (ops.length > 0) { 
            collection.bulkWrite(ops, function(err, result){
                callback(null, result);
            }); 
        }
    });
}

以及获得平均温度的逻辑:

function getAverageTemps(collection, callback) {
    collection.aggregate([
        {
            "$group": {
                "_id": null,
                "averageAirTemperature": { "$avg": "$air_temperature" },
                "averageWaterTemperature": { "$avg": "$water_temperature" },
                "averageHeatTemperature": { "$avg": "$heat_temperature" },
                "averageRoomTemperature": { "$avg": "$room_temperature" }
            }
        }
    ], function(err, results){
        if (err || !results) callback(err);
        else callback(null, results);        
    }); 
}

然后您可以按如下方式调用,例如,您可以在API中放置一个端点来检索平均临时值:

app.get('/meibanlist/averagetemps', function (req, res) {
    getAverageTemps(db.meibanlist, function (err, temps) {
        if (err || !temps) console.log ("record not found");
        else res.json(temps);
    });
});