MongoDB aggregateion 簡単サンプルコマンド & Mongoose#Aggregate() by node.js

# ▪️今日のお悩み

- MongoDBで、groupbyとかdistinctとかどうするの？
- それをnode.jsで実装する場合はどうするの？

# ▪️解決

- MongoDBのaggregateを使う。
- node.jsでMongoDBを使う時は、monngooseを使う。


## > まずは参考となるリンク集

こちらを読んでいき、実際にデータをフィルタさせていくと、なんとなくコツがつかめてきます。


- [Aggregation — MongoDB Manual 3.4](https://docs.mongodb.com/manual/aggregation/#Aggregation-Distinct)

- [Aggregation Pipeline Operators — MongoDB Manual 3.4]( https://docs.mongodb.com/manual/reference/operator/aggregation/)

- [Aggregation with the Zip Code Data Set — MongoDB Manual 3.4]( https://docs.mongodb.com/manual/tutorial/aggregation-zip-code-data-set/)

- [MongoDB Aggregation]( https://www.tutorialspoint.com/mongodb/mongodb_aggregation.htm)

- MySQLに馴染んでいる人はこちらもどうでしょうか。MySQLのクエリをMongoのクエリに直してくれるというものです。
    - [Query Mongo: MySQL to MongoDB Query Translator ](http://www.querymongo.com/)

## > MongoDBでaggregationする簡単なサンプル

## > aggregationするデータの準備

- ちなみに、MongoDBでの日付の挿入は以下の通り

```
db.example.insert({"date":new Date(Date.now())});
```

- monngooseは、collectionを複数形で作成する必要があります。

後で出てきますが、以下のようにTopicモデルを作成すると、
MongoDB側のターゲットが「topic」かと思いきや「topics」になっているので、よくはまるので気をつけてください。

```
var TopicModel = mongoose.model('Topic', topicSchema);
```

- サンプルデータをinsertします

```

db.topics.insert(
[
   {
      posted_at: new Date(Date.now()),
      posted_by: 'tanaka',
      data: 'hogehoge',
      room:'room_1',
      topic_text:'Todays Weather',
   },
   {
      posted_at: new Date(Date.now()),
      posted_by: 'okamoto',
      data: 'hogehoge',
      room:'room_2',
      topic_text:'Todays Weather',
   },
   {
      posted_at: new Date(Date.now()),
      posted_by: 'tanaka',
      data: 'hogehoge',
      room:'room_2',
      topic_text:'Todays Weather',
   },
   {
      posted_at: new Date(Date.now()),
      posted_by: 'yamamoto',
      data: 'hogehoge',
      room:'room_1',
      topic_text:'New Year',
   },
   {
      posted_at: new Date(Date.now()),
      posted_by: 'tanaka',
      data: 'hogehoge',
      room:'room_1',
      topic_text:'Count Down',
   },
]
)
```


- データをselectします
    - prettyをつけてあげると、分かりやすいJSON形式で出力してくれます。
```
db.topics.find().pretty()
```

## > Aggregationの実行

Aggregationは、unixのpipe "|" operatorに似ています。

- count
    - posted_by毎の数
    
```
db.topics.aggregate([
{$group : {_id : "$posted_by", num : {$sum : 1}}}
])


{ "_id" : "yamamoto", "num" : 1 }
{ "_id" : "okamoto", "num" : 1 }
{ "_id" : "tanaka", "num" : 3 }
```

- group
    - room毎にgroupby

```

db.topics.aggregate( [ 
{ $group : { _id :  { room: "$room" },  profile: { $push: "$$ROOT" }} } 
] ).pretty()



{
	"_id" : {
		"room" : "room_2"
	},
	"profile" : [
		{
			"_id" : ObjectId("5861f90669e5e0b2eb21f16c"),
			"posted_at" : ISODate("2016-12-27T05:15:50.603Z"),
			"posted_by" : "okamoto",
			"data" : "hogehoge",
			"room" : "room_2",
			"topic_text" : "Todays Weather"
		},
		{
			"_id" : ObjectId("5861f90669e5e0b2eb21f16d"),
			"posted_at" : ISODate("2016-12-27T05:15:50.603Z"),
			"posted_by" : "tanaka",
			"data" : "hogehoge",
			"room" : "room_2",
			"topic_text" : "Todays Weather"
		}
	]
}
{
	"_id" : {
		"room" : "room_1"
	},
	"profile" : [
		{
			"_id" : ObjectId("5861f90669e5e0b2eb21f16b"),
			"posted_at" : ISODate("2016-12-27T05:15:50.603Z"),
			"posted_by" : "tanaka",
			"data" : "hogehoge",
			"room" : "room_1",
			"topic_text" : "Todays Weather"
		},
		{
			"_id" : ObjectId("5861f90669e5e0b2eb21f16e"),
			"posted_at" : ISODate("2016-12-27T05:15:50.603Z"),
			"posted_by" : "yamamoto",
			"data" : "hogehoge",
			"room" : "room_1",
			"topic_text" : "New Year"
		},
		{
			"_id" : ObjectId("5861f90669e5e0b2eb21f16f"),
			"posted_at" : ISODate("2016-12-27T05:15:50.603Z"),
			"posted_by" : "tanaka",
			"data" : "hogehoge",
			"room" : "room_1",
			"topic_text" : "Count Down"
		}
	]
}

```


- match+ group + sort
    - room = room_1
    - posted_atのソート
    - topic_text毎

```
db.topics.aggregate( [ 
{ $match: { room : "room_1"}},
{ $sort: {posted_at:1}},
{ $group : { _id :  { topic_text: "$topic_text" },  profile: { $push: "$$ROOT" }} } 
] ).pretty()

{
	"_id" : {
		"topic_text" : "Count Down"
	},
	"profile" : [
		{
			"_id" : ObjectId("5861f90669e5e0b2eb21f16f"),
			"posted_at" : ISODate("2016-12-27T05:15:50.603Z"),
			"posted_by" : "tanaka",
			"data" : "hogehoge",
			"room" : "room_1",
			"topic_text" : "Count Down"
		}
	]
}
{
	"_id" : {
		"topic_text" : "New Year"
	},
	"profile" : [
		{
			"_id" : ObjectId("5861f90669e5e0b2eb21f16e"),
			"posted_at" : ISODate("2016-12-27T05:15:50.603Z"),
			"posted_by" : "yamamoto",
			"data" : "hogehoge",
			"room" : "room_1",
			"topic_text" : "New Year"
		}
	]
}
{
	"_id" : {
		"topic_text" : "Todays Weather"
	},
	"profile" : [
		{
			"_id" : ObjectId("5861f90669e5e0b2eb21f16b"),
			"posted_at" : ISODate("2016-12-27T05:15:50.603Z"),
			"posted_by" : "tanaka",
			"data" : "hogehoge",
			"room" : "room_1",
			"topic_text" : "Todays Weather"
		}
	]
}
```


## > monngoose+ node.jsでの実装

さて、上記のaggregateで実行したものをnode.jsで書きます。
npm install　で　mongoose をインストールして使います。

- [Mongoose API v4.7.5]( http://mongoosejs.com/docs/api.html)

```js

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

//Database connection
var uristring = 'mongodb://localhost/jp';
var mongoOptions = { };

mongoose.connect(uristring, mongoOptions, function (err, res) {
    if (err) {
        console.log('Error when connecting to: ' + uristring + '. ' + err);
    }
    else {
        console.log('Successfully connected to: ' + uristring);
    }
});


//Schemas:

var topicSchema = new Schema({
    posted_by: {
        type: String,
        default: ''
    },
    posted_at: {
        type: Date,
        default: Date.now
    },
    data: {
        type: String,
        default: JSON.stringify({})
    },
    room: {
        type: String,
        default: ''
    },
    topic_text: {
        type: String,
        default: ''
    }
});


//Define Models
var TopicModel = mongoose.model('Topic', topicSchema);


getTopicProfile("room_1", function(err, data){
    if(!err){
        console.log(JSON.stringify(data));
    }
});


//Aggregation function
function getTopicProfile(room_name, callback) {
    TopicModel.aggregate([
        { $match: {
            room: room_name
        }},
        { $sort: {posted_at:1}},
        { $group: {
            _id :  {  topic_text: "$topic_text" },
            profile: { $push: "$$ROOT" }
            }
        }
    ], function (err, result) {
        if (err) {
            console.log(err);
            return;
        }
        console.log(JSON.stringify(result));
    });
}
```

- コードはこちらを参考にさせていただきました。
    - [Aggregate data from MongoDB with Node.js and mongoose ](https://gist.github.com/kdelemme/9659364#file-aggregate-js-L34)