に公開
今回はMongoDBで集計する際に使用するaggregateに関して、基礎的なものをまとめておこうと思います。
※ Aggregate Piplineのみ
今回は以下のようなMLBの選手成績のデータを用意しました。
obpは"出塁率"、slgは"長打率"で
leagueに関してはALは"アメリカンリーグ"、NLは”ナショナルリーグ”になります。
[
{
"_id": 1,
"name": "Ohtani",
"obp": 412,
"slg": 654,
"league": "AL",
},
{
"_id": 2,
"name": "Yoshida",
"obp": 338,
"slg": 445,
"league": "AL",
},
{
"_id": 3,
"name": "Betts",
"obp": 408,
"slg": 579,
"league": "NL",
},
{
"_id": 4,
"name": "AcunaJr",
"obp": 416,
"slg": 596,
"league": "NL",
}
]
$addFields
addFieldsを使用することで、集計時にフィールドを追加することができます。
obp(出塁率)とslg(長打率)を足した値を新しく加えたopsというフィールドに出力したい場合は以下のようになります。
($addで配列の中の要素を足し合わせることができます)
db.collection.aggregate([
{
"$addFields": {
"ops": {
$add: [
"$obp",
"$slg"
]
}
}
}
])
出力結果:
[
{
"_id": 1,
"league": "AL",
"name": "Ohtani",
"obp": 412,
"ops": 1066,
"slg": 654
},
{
"_id": 2,
"league": "AL",
"name": "Yoshida",
"obp": 338,
"ops": 783,
"slg": 445
},
{
"_id": 3,
"league": "NL",
"name": "Betts",
"obp": 408,
"ops": 987,
"slg": 579
},
{
"_id": 4,
"league": "NL",
"name": "AcunaJr",
"obp": 416,
"ops": 1012,
"slg": 596
}
]
上記のようにopsが追加できました。
$match
$matchを使用することで、条件に合うものに絞り込むことができます。
今回は追加した先ほど追加したopsが.800を超える選手に絞り込むように$matchを使ってみます。
db.collection.aggregate([
{
"$addFields": {
"ops": {
$add: [
"$obp",
"$slg"
]
}
}
},
// 以下を追加
{
$match: {
"ops": {
$gt: 800
}
}
}
])
出力結果:
[
{
"_id": 1,
"league": "AL",
"name": "Ohtani",
"obp": 412,
"ops": 1066,
"slg": 654
},
{
"_id": 3,
"league": "NL",
"name": "Betts",
"obp": 408,
"ops": 987,
"slg": 579
},
{
"_id": 4,
"league": "NL",
"name": "AcunaJr",
"obp": 416,
"ops": 1012,
"slg": 596
}
]
$matchを使用することで3選手に絞ることができました。
$project
$projectを使用することで残すフィールドや取り除くフィールドを設定することができます。
今回は先ほどの集計結果からnameとopsだけを残したものにして見ようと思います。
残したいフィールドには"1"を
取り除きたいフィールドには"0"を指定します。
何も設定しなければ取り除かれますが、"_id"に関しては削除したい場合は明示的に"0"を指定する必要があります。
db.collection.aggregate([
{
"$addFields": {
"ops": {
$add: [
"$obp",
"$slg"
]
}
}
},
{
$match: {
"ops": {
$gt: 800
}
}
},
// 以下を追加
{
$project: {
_id: 0,
name: 1,
ops: 1
}
}
])
出力結果:
[
{
"name": "Ohtani",
"ops": 1066
},
{
"name": "Betts",
"ops": 987
},
{
"name": "AcunaJr",
"ops": 1012
}
]
かなりスッキリした形になりました。
そして$projectは$addFieldsのように新しいフィールドの指定をすることで可能です。
以下のように指定します。
db.collection.aggregate([
{
$project: {
_id: 0,
name: 1,
ops: {
$add: [
"$obp",
"$slg"
]
}
}
}
])
出力結果:
[
{
"name": "Ohtani",
"ops": 1066
},
{
"name": "Yoshida",
"ops": 783
},
{
"name": "Betts",
"ops": 987
},
{
"name": "AcunaJr",
"ops": 1012
}
]
今回は $matchを使用していないのでyoshidaも残っています
最後にリーグ別にopsの高い順に並び替えたいと思います。
$facet
$facetを使用することで複数の集計結果を出すことができます。
db.collection.aggregate([
{
$project: {
_id: 0,
name: 1,
league: 1,
ops: {
$add: [
"$obp",
"$slg"
]
}
}
},
// 以下を追加
{
"$facet": {
"NL": [
{
$match: {
"league": "NL"
}
}
],
"AL": [
{
$match: {
"league": "AL"
}
}
],
}
}
])
出力結果:
[
{
"AL": [
{
"league": "AL",
"name": "Ohtani",
"ops": 1066
},
{
"league": "AL",
"name": "Yoshida",
"ops": 783
}
],
"NL": [
{
"league": "NL",
"name": "Betts",
"ops": 987
},
{
"league": "NL",
"name": "AcunaJr",
"ops": 1012
}
]
}
]
4選手をNLとALで分けることができました。
$sort
次に$sortを使用してopsの高い順に並び替えたいと思います。
$sortに1を指定することで昇順
-1を指定することで降順並び替えることができます。
db.collection.aggregate([
{
$project: {
_id: 0,
name: 1,
league: 1,
ops: {
$add: [
"$obp",
"$slg"
]
}
}
},
{
"$facet": {
"NL": [
{
$match: {
"league": "NL"
}
},
// $sortを追加
{
"$sort": {
"ops": -1
}
}
],
"AL": [
{
$match: {
"league": "AL"
}
},
// $sortを追加
{
"$sort": {
"ops": -1
}
}
]
}
}
])
出力結果:
[
{
"AL": [
{
"league": "AL",
"name": "Ohtani",
"ops": 1066
},
{
"league": "AL",
"name": "Yoshida",
"ops": 783
}
],
"NL": [
{
"league": "NL",
"name": "AcunaJr",
"ops": 1012
},
{
"league": "NL",
"name": "Betts",
"ops": 987
}
]
}
]
上記のようにALとNLのリーグごとのOPSの高い順に情報を集計することができました。