Devahoy Logo
PublishedAt

Web Development

ทดลองเขียน Aggregation ใน MongoDB

ทดลองเขียน Aggregation ใน MongoDB

พอดีว่าได้ลองนั่งอ่านเรื่อง Aggregation ของ MongoDB แล้วรู้สึกว่ามันน่าสนใจดี ก็เลยทำเป็นบทความซะเลย

หากใครยังไม่รู้จัก MongoDB หรือว่ายังไ่ม่ได้ติดตั้งแนะนำบทความ MongoDB คืออะไร? + สอนวิธีใช้งานเบื้องต้น

สำหรับเวอร์ชันของผมที่ใช้ในการเขียนบทความคือ MongoDB version 3.2.6

Aggregation คืออะไร ?

Aggregation คือคำสั่งที่ใช้ในการประมวลผล data records เช่นการ group, การ sum, การค้นหาข้อมูลหรือแม้แต่การเปลี่ยนแปลงค่าของ output ใน MongoDB เราสามารถเรียกใช้ aggregate() ได้เลย เช่น

1
db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)

Aggregation ทำอะไรได้บ้าง ?

  • การ Group ข้อมูล
  • เลือกแสดง fields ที่ต้องการได้ (เหมือนกับ projection)
  • คำนวณข้อมูลก่อนแสดงผล เช่น sum, average
  • นับจำนวนคอมเม้น ในแต่ละโพส (เก็บ comment เป็น array ใน post document)

ตัวอย่าง Operators

  • $match : สำหรับ filter documents
  • $project : เอาไว้ปรับแต่ง documents เช่น จะเอา fields ไหนให้แสดง หรือเปลี่ยนชื่อ fields ได้
  • $group : group document
  • $sort : เอาไว้เรียงลำดับ document
  • $limit, $skip : สำหรับทำ pagination documents

ตัวอย่าง Data

ตัวอย่างจะใช้ Data Set ของทางเว็บ mongo สามารถดาวน์โหลดได้จาก zipcodes collection

ทำการ import ลง database ของเรา ด้วย mongoimport

1
mongoimport --db ahoy_aggregate_example --collection zipcodes --drop --file /path/to/file/zips.json
  • --db : กำหนดชื่อของ database
  • --collection : ทำการกำหนดชือ collection
  • --drop : เช็คว่า db ahoy_aggregate_example มี collection zipcodes หรือเปล่า ถ้ามีก็ drop ทิ้ง
  • --file : กำหนดไฟล์ที่เราต้องการ import

เมือรันคำสั่งจะได้ผลลัพธ์ลักษณะแบบนี้

1
2016-06-25T20:45:47.637+0700 connected to: localhost
2
2016-06-25T20:45:47.637+0700 dropping: ahoy_aggregate_example.zipcodes
3
2016-06-25T20:45:48.194+0700 imported 29353 documents

ทดลองดูว่ามีข้อมูลจริงมั้ย

Terminal window
mongo
> show dbs
ahoy_aggregate_example 0.078GB
> use ahoy_aggregate_example
switched to db ahoy_aggregate_example
> db.zipcodes.find().count()
29353
> db.zipcodes.findOne()
{
"_id" : "01001",
"city" : "AGAWAM",
"loc" : [
-72.622739,
42.070206
],
"pop" : 15338,
"state" : "MA"
}

ซึ่งถ้าดูจากข้อมูลแล้ว เราสามารถสร้าง Schema ด้วย Mongoose จะได้ดังนี้

1
'use strict'
2
3
const mongoose = require('mongoose')
4
const Schema = mongoose.Schema
5
6
let Zipcodes = new Schema({
7
_id: {
8
type: String,
9
unique: true,
10
index: true
11
},
12
city: String,
13
loc: {
14
type: [Number],
15
index: '2d'
16
},
17
pop: Number,
18
state: String
19
})
20
21
module.exports = mongoose.model('Zipcodes', Zipcodes)

$match

  • สำหรับ Filter Document

เช่น หา state ชื่อ NY

Terminal window
db.zipcodes.aggregate([
{
$match: { state: 'NY' }
}
])

จะได้ข้อมูลเฉพาะ state เท่ากับ NY เท่านั้น

หรือตัวอย่าง เช่น หาเมืองที่มีค่า pop มากกว่า 100000 จะได้

Terminal window
db.zipcodes.aggregate([
{
$match: { pop: {$gte: 100000} }
}
])

ผลลัพธ์

1
[
2
{
3
"_id": "10025",
4
"city": "NEW YORK",
5
"loc": [-73.9683119999999974, 40.797466],
6
"pop": 100027,
7
"state": "NY"
8
},
9
{
10
"_id": "10021",
11
"city": "NEW YORK",
12
"loc": [-73.9588049999999981, 40.7684759999999997],
13
"pop": 106564,
14
"state": "NY"
15
},
16
{
17
"_id": "11226",
18
"city": "BROOKLYN",
19
"loc": [-73.9569850000000031, 40.6466939999999965],
20
"pop": 111396,
21
"state": "NY"
22
},
23
{
24
"_id": "60623",
25
"city": "CHICAGO",
26
"loc": [-87.7156999999999982, 41.8490150000000014],
27
"pop": 112047,
28
"state": "IL"
29
}
30
]

$project

ไว้สำหรับปรับแต่ง output คล้ายๆกับ query แบบกำหนด projection

ตัวอย่าง เข่น ต้องการแสดงแค่ชื่อ city และ pop เท่านั้น ไม่แสดงอย่างอื่น จะได้

Terminal window
db.zipcodes.aggregate([
{
$project: { _id: 0, city: 1, pop: 1 }
}
])

ผลลัพธ์

1
[
2
{
3
"city" : "AGAWAM",
4
"pop" : 15338
5
},
6
{
7
"city" : "BLANDFORD",
8
"pop" : 1240
9
},
10
{
11
"city" : "BELCHERTOWN",
12
"pop" : 10579
13
},
14
{
15
"city" : "CUSHMAN",
16
"pop" : 36963
17
},
18
...
19
]

Note: 0 คือไม่แสดง field นั้น และ 1 คือให้แสดง field นั้น

อีกตัวอย่าง เช่น sub-document fields เอา state และ city ไปไว้ใน info เช่น

Terminal window
db.zipcodes.aggregate([
{
$project: {
info: {
state: "$state",
city: "$city"
}
}
}
])

ผลลัพธ์

1
[
2
{
3
"_id": "01001",
4
"info": {
5
"state": "MA",
6
"city": "AGAWAM"
7
}
8
},
9
{
10
"_id": "01008",
11
"info": {
12
"state": "MA",
13
"city": "BLANDFORD"
14
}
15
},
16
{
17
"_id": "01007",
18
"info": {
19
"state": "MA",
20
"city": "BELCHERTOWN"
21
}
22
}
23
]

$group

เหมือนกับ GROUP BY ใน SQL เช่น การรวม city, state และ sum ค่า pop จะได้ดังนี้

Terminal window
db.zipcodes.aggregate([
{
$group: {
_id: "$state",
totalPop: { $sum: "$pop"}
}
}
])

ผลลัพธ์

1
[
2
{
3
"_id" : "NV",
4
"totalPop" : 1.20183e+06
5
},
6
{
7
"_id" : "ID",
8
"totalPop" : 1.00675e+06
9
},
10
{
11
"_id" : "CO",
12
"totalPop" : 3.29376e+06
13
},
14
...
15
]

อีกตัวอย่าง เช่น นับจำนวน city ในแต่ละ states ว่ามีเท่าไหร่

Note! : บาง city สามารถมีได้หลาย zipcode ฉะนั้นข้อมูลอาจจะคลาดเคลื่อนไปบ้าง และในข้อมูล data sheet บาง fields มี city และ state ซ้ำกัน

Terminal window
db.zipcodes.aggregate([
{
$group: {
_id: "$state",
totalCities: { $sum: 1}
}
}
])

ผลลัพธ์

1
[
2
{
3
"_id" : "NV",
4
"totalCities" : 104.0000000000000000
5
},
6
{
7
"_id" : "ID",
8
"totalCities" : 244.0000000000000000
9
},
10
{
11
"_id" : "CO",
12
"totalCities" : 414.0000000000000000
13
},
14
...
15
]
  • อีกตัวอย่าง เช่น การ distinct value รวมกลุ่ม city ทั้งหมดใน state ไว้ใน field cities
Terminal window
db.zipcodes.aggregate([
{
$group: {
_id: "$state",
cities: { $addToSet: "$city"}
}
}
])

ผลลัพธ์

1
[
2
{
3
"_id" : "NV",
4
"cities" : [
5
"BEOWAWE",
6
"OASIS",
7
"MOUNTAIN CITY",
8
...
9
]
10
},
11
{...},
12
{...}
13
]

$sort

การ Sorting document เรียงลำดับก่อนหลัง เช่น หาจำนวน city ในแต่ละ state แล้วก็เรียงข้อมูลจากน้อยไปมาก จะเขียนได้

Terminal window
db.zipcodes.aggregate([
{
$group: {
_id: "$state",
totalCities: { $sum: 1}
}
},
{
$sort: {
totalCities: 1
}
}
])

ผลลัพธ์

1
[
2
{
3
"_id" : "DC",
4
"totalCities" : 24.0000000000000000
5
},
6
{
7
"_id" : "DE",
8
"totalCities" : 53.0000000000000000
9
},
10
{
11
"_id" : "RI",
12
"totalCities" : 69.0000000000000000
13
},
14
{
15
"_id" : "HI",
16
"totalCities" : 80.0000000000000000
17
},
18
{
19
"_id" : "NV",
20
"totalCities" : 104.0000000000000000
21
},
22
...
23
]

ทั้งหมดนี้ก็เป็นแค่ตัวอย่างคร่าวๆในการใช้งาน Aggregation ใน MongoDB ที่ผมได้ลองศึกษา ยังไม่รวมหลายๆอย่าง เช่น $geoNear หรือ $unwind อีกทั้งตัว Aggregation นั้นยังทำอะไรได้อีกเยอะแยะ ที่ผมยังไม่ได้ลองโดยเฉพาะตัว $geoNear ได้ลองแค่เบื้องต้น แต่จริงๆมันทำอะไรได้เยอะแยะมาก เช่น ค้นหา nearby หรือหาระยะทางระหว่าง location เป็นต้น

References

Authors
avatar

Chai Phonbopit

เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust

Related Posts