MongoDB(35) - 一括処理(Bulk)④一括登録/一括更新処理(upsert)

今回は、一括登録/一括更新処理(upsert)を行います。

一括登録/一括更新処理(upsert)

順次処理で一括登録/一括更新処理(upsert)を行います。

upsert処理では該当するデータが存在する場合は更新を行い、該当するデータがない場合は登録を行います。

(並列処理で実行したい場合は、initializeOrderedBulkOp()の代わりにinitializeUnorderedBulkOp()を使います。)

処理の詳細は以下の通りです。

  1. Bulk実行タイプを設定。(1行目)
  2. upsertクエリーを設定。(3~6行目)
    前半の3件が更新処理で、最後の1件が登録処理になります。
  3. 一括実行(Bulk実行)。(8行目)

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
> var bulk = db.member.initializeOrderedBulkOp()

> bulk.find({LastName:'武元'}).upsert().updateOne({$set:{age:12}}) // 更新
> bulk.find({LastName:'松田'}).upsert().updateOne({$set:{age:23}}) // 更新
> bulk.find({LastName:'加藤'}).upsert().updateOne({$set:{age:33}}) // 更新
> bulk.find({LastName:'伊藤'}).upsert().updateOne({$set:{age:43}}) // 追加

> bulk.execute()
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 0,
"nUpserted" : 1,
"nMatched" : 3,
"nModified" : 3,
"nRemoved" : 0,
"upserted" : [
{
"index" : 3,
"_id" : ObjectId("613e8ff0d1340480894d324d")
}
]
})

> db.member.find()
{ "_id" : ObjectId("613e8e9a0fb0f3e6cf9e2b87"), "LastName" : "武元", "FirstName" : "ゆい", "age" : 12 }
{ "_id" : ObjectId("613e8e9a0fb0f3e6cf9e2b88"), "LastName" : "松田", "FirstName" : "りな", "age" : 23 }
{ "_id" : ObjectId("613e8e9a0fb0f3e6cf9e2b89"), "LastName" : "加藤", "FirstName" : "しほ", "age" : 33 }
{ "_id" : ObjectId("613e8ff0d1340480894d324d"), "LastName" : "伊藤", "age" : 43 }

前半の3ドキュメントに関してはデータが存在していたので、既存データにageフィールドが追加されています。

最後の1ドキュメントに関してはデータが存在していなかったため、新規ドキュメントとしてデータが追加されています。

Pythonで操作

上記の処理をPythonで行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import pymongo
from pymongo import MongoClient
from pymongo import UpdateOne
from pymongo.errors import BulkWriteError

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得
db1 = client.db1

# 一括入れ替え
requests = [
UpdateOne({'LastName': '武元'},{'$set':{'age':12}}, upsert=True),
UpdateOne({'LastName': '松田'},{'$set':{'age':23}}, upsert=True),
UpdateOne({'LastName': '加藤'},{'$set':{'age':33}}, upsert=True),
UpdateOne({'LastName': '伊藤'},{'$set':{'age':43}}, upsert=True)
]

try:
db1.member.bulk_write(requests) # 順次処理
# db.member.bulk_write(requests, ordered=False) # 並列処理
except BulkWriteError as bwe:
pprint(bwe.details)

# 入れ替え内容の確認
docs = db1.member.find()
for doc in docs:
print(doc)

Pythonでupsert処理を行う場合は、UpdateOneを使ってupsert=Trueオプションを追加します。


次回は、一括削除処理(remove)を行います。

MongoDB(34) - 一括処理(Bulk)③一括入れ替え

今回は、一括入れ替え処理を行います。

一括入れ替え

順次処理で一括入れ替えを行います。

(並列処理で実行したい場合は、initializeOrderedBulkOp()の代わりにinitializeUnorderedBulkOp()を使います。)

処理の詳細は以下の通りです。

  1. Bulk実行タイプを設定。(1行目)
  2. replaceクエリーを設定。(3~5行目)
  3. 一括実行(Bulk実行)。(7行目)

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
> var bulk = db.member.initializeOrderedBulkOp()

> bulk.find({name:"武元"}).replaceOne({LastName:"武元", FirstName:"ゆい"})
> bulk.find({name:"松田"}).replaceOne({LastName:"松田", FirstName:"りな"})
> bulk.find({name:"加藤"}).replaceOne({LastName:"加藤", FirstName:"しほ"})

> bulk.execute()
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 0,
"nUpserted" : 0,
"nMatched" : 3,
"nModified" : 3,
"nRemoved" : 0,
"upserted" : [ ]
})
> db.member.find()
{ "_id" : ObjectId("613e8e9a0fb0f3e6cf9e2b87"), "LastName" : "武元", "FirstName" : "ゆい" }
{ "_id" : ObjectId("613e8e9a0fb0f3e6cf9e2b88"), "LastName" : "松田", "FirstName" : "りな" }
{ "_id" : ObjectId("613e8e9a0fb0f3e6cf9e2b89"), "LastName" : "加藤", "FirstName" : "しほ" }

問題なく3つのドキュメントの入れ替えを行うことができました。

Pythonで操作

上記の処理をPythonで行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import pymongo
from pymongo import MongoClient
from pymongo import ReplaceOne
from pymongo.errors import BulkWriteError

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得
db1 = client.db1

# 一括入れ替え
requests = [
ReplaceOne({'name': '武元'},{'LastName':'武元', 'FirstName':'ゆい'}),
ReplaceOne({'name': '松田'},{'LastName':'松田', 'FirstName':'りな'}),
ReplaceOne({'name': '加藤'},{'LastName':'加藤', 'FirstName':'しほ'})
]

try:
db1.member.bulk_write(requests) # 順次処理
# db.member.bulk_write(requests, ordered=False) # 並列処理
except BulkWriteError as bwe:
pprint(bwe.details)

# 入れ替え内容の確認
docs = db1.member.find()
for doc in docs:
print(doc)

ReplaceOneの第1引数には検索条件を指定し、第2引数には入れ替えるドキュメントの内容を設定します。

次回は、一括登録/一括更新処理(upsert)を行います。

MongoDB(33) - 一括処理(Bulk)②一括更新

今回は、一括更新(Bulk)に関する操作を行います。

一括更新

順次処理で一括更新を行います。

(並列処理で実行したい場合は、initializeOrderedBulkOp()の代わりにinitializeUnorderedBulkOp()を使います。)

処理の詳細は以下の通りです。

  1. Bulk実行タイプを設定。(1行目)
  2. updateクエリーを設定。(3~5行目)
  3. 一括実行(Bulk実行)。(7行目)

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
> var bulk = db.member.initializeOrderedBulkOp()    //順次処理

> bulk.find({name:'武元'}).updateOne({$set:{age:11}})
> bulk.find({name:'松田'}).updateOne({$set:{age:21}})
> bulk.find({name:'加藤'}).updateOne({$set:{age:31}})

> bulk.execute()
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 0,
"nUpserted" : 0,
"nMatched" : 3,
"nModified" : 3,
"nRemoved" : 0,
"upserted" : [ ]
})

> db.member.find()
{ "_id" : ObjectId("613e8c820fb0f3e6cf9e2b81"), "name" : "武元", "age" : 11 }
{ "_id" : ObjectId("613e8c820fb0f3e6cf9e2b82"), "name" : "松田", "age" : 21 }
{ "_id" : ObjectId("613e8c820fb0f3e6cf9e2b83"), "name" : "加藤", "age" : 31 }

問題なく3つのドキュメントの年齢(age)を更新することができました。

Pythonで操作

上記の処理をPythonで行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import pymongo
from pymongo import MongoClient
from pymongo import UpdateOne
from pymongo.errors import BulkWriteError

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得
db1 = client.db1

# 一括更新
requests = [
UpdateOne({'name':'武元'}, {'$set':{'age':11}}),
UpdateOne({'name':'松田'}, {'$set':{'age':21}}),
UpdateOne({'name':'加藤'}, {'$set':{'age':31}})
]

try:
db1.member.bulk_write(requests) # 順次処理
# db.member.bulk_write(requests, ordered=False) # 並列処理
except BulkWriteError as bwe:
pprint(bwe.details)

# 更新内容の確認
docs = db1.member.find()
for doc in docs:
print(doc)

次回は、一括入れ替え処理を行います。

MongoDB(32) - 一括処理(Bulk)①一括登録

今回から、一括処理(Bulk)に関する操作を行っていきます。

一括処理(Bulk)とは

大量にinsertやupdateを行う場合、一括処理(Bulk)を使うとまとめて処理を行うことができるため速く実行することができます

一括処理(Bulk)には、下記の2種類があります。

  1. 順次処理
    登録の順番に意味がある場合に使用します。
    前の処理が終わってから次の処理を行うため、2.並列処理よりも処理は遅くなります、
  2. 並列処理
    順番に関係なく処理を行います。
    そのため1.順次処理よりも速く処理が終了します。

一括登録

順次処理で一括登録を行います。

(並列処理で実行したい場合は、initializeOrderedBulkOp()ではなくinitializeUnorderedBulkOp()を使います。)

処理の詳細は以下の通りです。

  1. Bulk実行タイプを設定。(1行目)
  2. insertクエリーを設定。(3~5行目)
  3. 一括実行(Bulk実行)。(7行目)

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
> var bulk = db.member.initializeOrderedBulkOp()  //順次処理

> bulk.insert({name:"武元", age:10})
> bulk.insert({name:"松田", age:20})
> bulk.insert({name:"加藤", age:30})

> bulk.execute()
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 3,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})

> db.member.find()
{ "_id" : ObjectId("613e728c0fb0f3e6cf9e2b7e"), "name" : "武元", "age" : 10 }
{ "_id" : ObjectId("613e728c0fb0f3e6cf9e2b7f"), "name" : "松田", "age" : 20 }
{ "_id" : ObjectId("613e728c0fb0f3e6cf9e2b80"), "name" : "加藤", "age" : 30 }

問題なく3つのドキュメントを追加することができました。

Pythonで操作

上記の処理をPythonで行うと、次のようになります。

3~4行目に新たにimport文を追加していますのでご注意下さい。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import pymongo
from pymongo import MongoClient
from pymongo import InsertOne
from pymongo.errors import BulkWriteError

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得
db1 = client.db1

# 一括更新
requests = [
InsertOne({'name':'武元', 'age':10}),
InsertOne({'name':'松田', 'age':20}),
InsertOne({'name':'加藤', 'age':30})
]

try:
db1.member.bulk_write(requests) # 順次処理
# db.member.bulk_write(requests, ordered=False) # 並列処理
except BulkWriteError as bwe:
pprint(bwe.details)

# 登録内容の確認
docs = db1.member.find()
for doc in docs:
print(doc)

次回は、一括更新処理を行います。

MongoDB(31) - 集計処理(aggregate)⑫集計結果のソート/表示件数制限/取得開始位置の指定

今回は、集計結果のソート/表示件数制限/取得開始位置の指定を行います。

サンプルデータ

以前用意した会社の部、課、従業員データを使用します。

集計処理(aggregate)①データ準備 - https://ailog.site/2021/09/02/2021/0902/

集計結果のソート

集計結果をソートしてみます。

集計結果のソートには$sortを使います。

[Mongoシェル]

1
2
3
4
5
6
7
8
> db.employee.aggregate([
{$group:{_id:"$k_id", max_salary:{$max: "$salary"}}},
{$sort:{max_salary:1}} // 1:昇順 -1:降順
])
{ "_id" : "ka4", "max_salary" : 120000 }
{ "_id" : "ka2", "max_salary" : 250000 }
{ "_id" : "ka1", "max_salary" : 400000 }
{ "_id" : "ka3", "max_salary" : 550000 }

課ごとの最大給料に関して、昇順でソートすることができました。

集計結果の表示件数制限

集計結果の表示件数を制限します。

集計結果の表示件数制限には$limitを使います。

[Mongoシェル]

1
2
3
4
5
6
> db.employee.aggregate([
{$group:{_id:"$k_id", max_salary:{$max: "$salary"}}},
{$sort:{max_salary:1}},
{$limit: 1}
])
{ "_id" : "ka4", "max_salary" : 120000 }

課ごとの最大給料に関して、表示件数を1件に制限することができました。

集計結果の取得開始位置の指定

集計結果の取得開始位置を指定します。

集計結果の表示開始位置を指定するためには$skipを使用します。

[Mongoシェル]

1
2
3
4
5
6
7
8
> db.employee.aggregate([
{$group:{_id:"$k_id", max_salary:{$max: "$salary"}}},
{$sort:{max_salary:1}},
{$skip: 1}
])
{ "_id" : "ka2", "max_salary" : 250000 }
{ "_id" : "ka1", "max_salary" : 400000 }
{ "_id" : "ka3", "max_salary" : 550000 }

課ごとの最大給料に関して、表示開始位置を2つめからとすることができました。

$skipに指定するインデックスは0から始まりますので、1を指定した場合は2つめの集計結果から表示されることになります。


$sort$limit$skip は組み合わせて実行することが可能ですが、上から順番に実行されるので順番には気を付ける必要があります。

Pythonで操作

上記の集計処理をPythonで行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import pymongo
from pymongo import MongoClient

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得
db1 = client.db1

# 集計結果のソート
docs = db1.employee.aggregate([
{'$group':{'_id':'$k_id', 'max_salary':{'$max': '$salary'}}},
{'$sort':{'max_salary':1}} # 1:昇順 -1:降順
])
for doc in docs:
print(doc)

# 集計結果の表示件数制限
docs = db1.employee.aggregate([
{'$group':{'_id':'$k_id', 'max_salary':{'$max': '$salary'}}},
{'$sort':{'max_salary':1}},
{'$limit': 1}
])
for doc in docs:
print(doc)


# 集計結果の取得開始位置の指定
docs = db1.employee.aggregate([
{'$group':{'_id':'$k_id', 'max_salary':{'$max': '$salary'}}},
{'$sort':{'max_salary':1}},
{'$skip': 1}
])
for doc in docs:
print(doc)

次回からは、一括高速処理(Bulk)を行います。

MongoDB(30) - 集計処理(aggregate)⑪複数コレクションの結合

今回は、複数コレクションの結合を行います。

サンプルデータ

以前用意した会社の部、課、従業員データを使用します。

集計処理(aggregate)①データ準備 - https://ailog.site/2021/09/02/2021/0902/

複数コレクションの結合

1つのコレクションから複数のコレクションを同時に結合します。

コレクションの結合方法は下記の通りです。

  • 従業員コレクション(employee)と課コレクション(division)を結合
    結合キーは課ID(k_id)
  • 従業員コレクション(employeeと役職コレクション(position)を結合
    結合キーは役職ID(p_id)

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> db.employee.aggregate([
{$lookup:{
from: "division",
localField: "k_id",
foreignField: "k_id",
as: "division_docs"
}},
{$lookup:{
from: "position",
localField: "p_id",
foreignField: "p_id",
as: "position_docs"
}}
])
{ "_id" : ObjectId("612f07f568110fe533ed9d21"), "e_id" : "emp001", "k_id" : "ka1", "p_id" : "po1", "name" : "高山", "age" : 31, "salary" : 400000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d19"), "k_id" : "ka1", "b_id" : "bu1", "name" : "総務課" } ], "position_docs" : [ { "_id" : ObjectId("612f07d768110fe533ed9d1d"), "p_id" : "po1", "name" : "社長" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d22"), "e_id" : "emp002", "k_id" : "ka2", "p_id" : "po2", "name" : "生駒", "age" : 37, "salary" : 250000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1a"), "k_id" : "ka2", "b_id" : "bu2", "name" : "企画課" } ], "position_docs" : [ { "_id" : ObjectId("612f07d768110fe533ed9d1e"), "p_id" : "po2", "name" : "部長" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d23"), "e_id" : "emp003", "k_id" : "ka3", "p_id" : "po3", "name" : "嘉喜", "age" : 23, "salary" : 200000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1b"), "k_id" : "ka3", "b_id" : "bu3", "name" : "開発1課" } ], "position_docs" : [ { "_id" : ObjectId("612f07d768110fe533ed9d1f"), "p_id" : "po3", "name" : "課長" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d24"), "e_id" : "emp004", "k_id" : "ka4", "p_id" : "po4", "name" : "与田", "age" : 33, "salary" : 100000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1c"), "k_id" : "ka4", "b_id" : "bu3", "name" : "開発2課" } ], "position_docs" : [ { "_id" : ObjectId("612f07d768110fe533ed9d20"), "p_id" : "po4", "name" : "一般社員" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d25"), "e_id" : "emp005", "k_id" : "ka3", "p_id" : "po4", "name" : "松田", "age" : 35, "salary" : 550000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1b"), "k_id" : "ka3", "b_id" : "bu3", "name" : "開発1課" } ], "position_docs" : [ { "_id" : ObjectId("612f07d768110fe533ed9d20"), "p_id" : "po4", "name" : "一般社員" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d26"), "e_id" : "emp006", "k_id" : "ka4", "p_id" : "po4", "name" : "菅井", "age" : 45, "salary" : 120000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1c"), "k_id" : "ka4", "b_id" : "bu3", "name" : "開発2課" } ], "position_docs" : [ { "_id" : ObjectId("612f07d768110fe533ed9d20"), "p_id" : "po4", "name" : "一般社員" } ] }

従業員コレクションから、課コレクションと役職コレクションを結合することができました。

表示フィールドの絞り込み

複数コレクションの結合を行った結果に対して、$projectを使って表示フィールドを絞り込みます。

オブジェクトID(_id)を非表示にし、従業員ID(e_id)・従業員名・課の名称・役職名を表示します。

また同時にunwindを使い、配列をオブジェクト型に展開します。

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
> db.employee.aggregate([
{$lookup:{
from: "division",
localField: "k_id",
foreignField: "k_id",
as: "division_docs"
}},
{$lookup:{
from: "position",
localField: "p_id",
foreignField: "p_id",
as: "position_docs"
}},
{$project:{
"_id": 0,
"e_id": 1,
"name": 1,
"division_docs.name": 1,
"position_docs.name": 1
}},
{$unwind: "$division_docs"}, // 課コレクションの展開
{$unwind: "$position_docs"} // 役職コレクションの展開
])
{ "e_id" : "emp001", "name" : "高山", "division_docs" : { "name" : "総務課" }, "position_docs" : { "name" : "社長" } }
{ "e_id" : "emp002", "name" : "生駒", "division_docs" : { "name" : "企画課" }, "position_docs" : { "name" : "部長" } }
{ "e_id" : "emp003", "name" : "嘉喜", "division_docs" : { "name" : "開発1課" }, "position_docs" : { "name" : "課長" } }
{ "e_id" : "emp004", "name" : "与田", "division_docs" : { "name" : "開発2課" }, "position_docs" : { "name" : "一般社員" } }
{ "e_id" : "emp005", "name" : "松田", "division_docs" : { "name" : "開発1課" }, "position_docs" : { "name" : "一般社員" } }
{ "e_id" : "emp006", "name" : "菅井", "division_docs" : { "name" : "開発2課" }, "position_docs" : { "name" : "一般社員" } }

コレクションを結合した結果から、想定通りのフィールドを表示することができました。

Pythonで操作

上記の集計処理をPythonで行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import pymongo
from pymongo import MongoClient

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得
db1 = client.db1

# 複数コレクションの結合
docs = db1.employee.aggregate([
{'$lookup':{
'from': 'division',
'localField': 'k_id',
'foreignField': 'k_id',
'as': 'division_docs'
}},
{'$lookup':{
'from': 'position',
'localField': 'p_id',
'foreignField': 'p_id',
'as': 'position_docs'
}}
])
for doc in docs:
print(doc)

# 表示フィールドの絞り込み
docs = db1.employee.aggregate([
{'$lookup':{
'from': 'division',
'localField': 'k_id',
'foreignField': 'k_id',
'as': 'division_docs'
}},
{'$lookup':{
'from': 'position',
'localField': 'p_id',
'foreignField': 'p_id',
'as': 'position_docs'
}},
{'$project':{
'_id': 0,
'e_id': 1,
'name': 1,
'division_docs.name': 1,
'position_docs.name': 1
}},
{'$unwind': '$division_docs'}, # 課コレクションの展開
{'$unwind': '$position_docs'} # 役職コレクションの展開
])
for doc in docs:
print(doc)

次回は、集計結果のソート/表示件数制限/取得開始位置の指定を行います。

MongoDB(29) - 集計処理(aggregate)⑩コレクションの二重結合

今回は、コレクションの二重結合を行います。

サンプルデータ

以前用意した会社の部、課、従業員データを使用します。

集計処理(aggregate)①データ準備 - https://ailog.site/2021/09/02/2021/0902/

コレクションの二重結合

これまでは$lookupを使い2つのコレクションを結合してきました。

$lookupは1度に複数指定することができ、3つ以上のコレクションを結合することも可能です。

次のように3つのコレクションを結合してみます。

  • 従業員コレクション(employee)と課コレクション(division)を結合
    結合キーは課ID(k_id)
  • 課コレクション(division)と部署コレクション(department)を結合
    結合キーは部ID(b_id)

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> db.employee.aggregate([
{$lookup:{
from: "division",
localField: "k_id",
foreignField: "k_id",
as: "division_docs"
}},
{$lookup:{
from: "department",
localField: "division_docs.b_id",
foreignField: "b_id",
as: "department_docs"
}}
])
{ "_id" : ObjectId("612f07f568110fe533ed9d21"), "e_id" : "emp001", "k_id" : "ka1", "p_id" : "po1", "name" : "高山", "age" : 31, "salary" : 400000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d19"), "k_id" : "ka1", "b_id" : "bu1", "name" : "総務課" } ], "department_docs" : [ { "_id" : ObjectId("612f075468110fe533ed9d16"), "b_id" : "bu1", "name" : "総務部" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d22"), "e_id" : "emp002", "k_id" : "ka2", "p_id" : "po2", "name" : "生駒", "age" : 37, "salary" : 250000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1a"), "k_id" : "ka2", "b_id" : "bu2", "name" : "企画課" } ], "department_docs" : [ { "_id" : ObjectId("612f075468110fe533ed9d17"), "b_id" : "bu2", "name" : "企画部" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d23"), "e_id" : "emp003", "k_id" : "ka3", "p_id" : "po3", "name" : "嘉喜", "age" : 23, "salary" : 200000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1b"), "k_id" : "ka3", "b_id" : "bu3", "name" : "開発1課" } ], "department_docs" : [ { "_id" : ObjectId("612f075468110fe533ed9d18"), "b_id" : "bu3", "name" : "開発部" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d24"), "e_id" : "emp004", "k_id" : "ka4", "p_id" : "po4", "name" : "与田", "age" : 33, "salary" : 100000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1c"), "k_id" : "ka4", "b_id" : "bu3", "name" : "開発2課" } ], "department_docs" : [ { "_id" : ObjectId("612f075468110fe533ed9d18"), "b_id" : "bu3", "name" : "開発部" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d25"), "e_id" : "emp005", "k_id" : "ka3", "p_id" : "po4", "name" : "松田", "age" : 35, "salary" : 550000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1b"), "k_id" : "ka3", "b_id" : "bu3", "name" : "開発1課" } ], "department_docs" : [ { "_id" : ObjectId("612f075468110fe533ed9d18"), "b_id" : "bu3", "name" : "開発部" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d26"), "e_id" : "emp006", "k_id" : "ka4", "p_id" : "po4", "name" : "菅井", "age" : 45, "salary" : 120000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1c"), "k_id" : "ka4", "b_id" : "bu3", "name" : "開発2課" } ], "department_docs" : [ { "_id" : ObjectId("612f075468110fe533ed9d18"), "b_id" : "bu3", "name" : "開発部" } ] }

3つのコレクションを結合してデータを取得することができました。

表示フィールドの絞り込み

コレクションを二重結合して取得したドキュメントに対して、$projectを使って表示フィールドを絞り込みます。

また同時にunwindを使い、配列をオブジェクト型に展開します。

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
> db.employee.aggregate([
{$lookup:{
from: "division",
localField: "k_id",
foreignField: "k_id",
as: "division_docs"
}},
{$lookup:{
from: "department",
localField: "division_docs.b_id",
foreignField: "b_id",
as: "department_docs"
}},
{$project: {
"_id":0,
"e_id":1,
"name":1,
"department_docs.name": 1,
"division_docs.name": 1
}},
{$unwind: "$department_docs"}, // 部署コレクションの展開
{$unwind: "$division_docs"} // 課コレクションの展開
])
{ "e_id" : "emp001", "name" : "高山", "division_docs" : { "name" : "総務課" }, "department_docs" : { "name" : "総務部" } }
{ "e_id" : "emp002", "name" : "生駒", "division_docs" : { "name" : "企画課" }, "department_docs" : { "name" : "企画部" } }
{ "e_id" : "emp003", "name" : "嘉喜", "division_docs" : { "name" : "開発1課" }, "department_docs" : { "name" : "開発部" } }
{ "e_id" : "emp004", "name" : "与田", "division_docs" : { "name" : "開発2課" }, "department_docs" : { "name" : "開発部" } }
{ "e_id" : "emp005", "name" : "松田", "division_docs" : { "name" : "開発1課" }, "department_docs" : { "name" : "開発部" } }
{ "e_id" : "emp006", "name" : "菅井", "division_docs" : { "name" : "開発2課" }, "department_docs" : { "name" : "開発部" } }

すっきりとした表示結果にすることができました。

Pythonで操作

上記の集計処理をPythonで行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import pymongo
from pymongo import MongoClient

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得
db1 = client.db1

# コレクションの結合(オブジェクト型に展開)
docs = db1.employee.aggregate([
{'$lookup':{
'from': 'division',
'localField': 'k_id',
'foreignField': 'k_id',
'as': 'division_docs'
}},
{'$lookup':{
'from': 'department',
'localField': 'division_docs.b_id',
'foreignField': 'b_id',
'as': 'department_docs'
}}
])
for doc in docs:
print(doc)

# コレクションの結合(文字列型に展開)
docs = db1.employee.aggregate([
{'$lookup':{
'from': 'division',
'localField': 'k_id',
'foreignField': 'k_id',
'as': 'division_docs'
}},
{'$lookup':{
'from': 'department',
'localField': 'division_docs.b_id',
'foreignField': 'b_id',
'as': 'department_docs'
}},
{'$project': {
'_id':0,
'e_id':1,
'name':1,
'department_docs.name': 1,
'division_docs.name': 1
}},
{'$unwind': '$department_docs'},
{'$unwind': '$division_docs'}
])
for doc in docs:
print(doc)

次回は、複数コレクションの結合を行います。

MongoDB(28) - 集計処理(aggregate)⑨コレクション結合・オブジェクト型と文字列型に展開

今回は、結合したコレクションをオブジェクト型と文字列型に展開します。

サンプルデータ

以前用意した会社の部、課、従業員データを使用します。

集計処理(aggregate)①データ準備 - https://ailog.site/2021/09/02/2021/0902/

オブジェクト型に変換

前回のコレクション結合では、結合先コレクションのdivision_docsが [{}] というように配列の中にオブジェクトが入っている構造になっています。

$unwindを使うと、配列の中の値を展開することができます。

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> db.employee.aggregate([
{$lookup:{
from: "division",
localField:"k_id",
foreignField:"k_id",
as: "division_docs"
}},
{$project:{
"_id":0,
"name":1,
"division_docs.name":1
}},
{$unwind:"$division_docs"}
])
{ "name" : "高山", "division_docs" : { "name" : "総務課" } }
{ "name" : "生駒", "division_docs" : { "name" : "企画課" } }
{ "name" : "嘉喜", "division_docs" : { "name" : "開発1課" } }
{ "name" : "与田", "division_docs" : { "name" : "開発2課" } }
{ "name" : "松田", "division_docs" : { "name" : "開発1課" } }
{ "name" : "菅井", "division_docs" : { "name" : "開発2課" } }

division_docsの[]がとれて、{}というオブジェクト型になりました。

文字列型に変換

次にオブジェクト型を文字列型に変換します。

オブジェクト型に変換したデータに対して$groupを使い_id(集計キー)にe_id(従業員ID)を指定します。

e_idはユニークなので集計されません。

さらに表示したいフィールドに何らかの集計関数(maxなど)を使って、オブジェクト型から文字列型に変換します。

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
> db.employee.aggregate([
{$lookup:{
from: "division",
localField:"k_id",
foreignField:"k_id",
as: "division_docs"
}},
{$project:{
"_id":0,
"e_id":1,
"name":1,
"division_docs.name":1
}},
{$unwind:"$division_docs"},
{$group:{
_id:"$e_id",
employeeName:{$max:"$name"},
divisionName:{$max:"$division_docs.name"}
}}
])
{ "_id" : "emp002", "employeeName" : "生駒", "divisionName" : "企画課" }
{ "_id" : "emp001", "employeeName" : "高山", "divisionName" : "総務課" }
{ "_id" : "emp004", "employeeName" : "与田", "divisionName" : "開発2課" }
{ "_id" : "emp006", "employeeName" : "菅井", "divisionName" : "開発2課" }
{ "_id" : "emp005", "employeeName" : "松田", "divisionName" : "開発1課" }
{ "_id" : "emp003", "employeeName" : "嘉喜", "divisionName" : "開発1課" }

結合した課コレクションの課の名称(division_docs.name)を、オブジェクト型から文字列型に変換することができました。

少々強引な方法なので、アプリケーション側で処理した方が楽に展開できるかもしれません。

Pythonで操作

上記の集計処理をPythonで行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import pymongo
from pymongo import MongoClient

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得
db1 = client.db1

# コレクションの結合(オブジェクト型に展開)
docs = db1.employee.aggregate([
{'$lookup':{
'from': 'division',
'localField':'k_id',
'foreignField':'k_id',
'as': 'division_docs'
}},
{'$project':{
'_id':0,
'name':1,
'division_docs.name':1
}},
{'$unwind':'$division_docs'}
])
for doc in docs:
print(doc)

# コレクションの結合(文字列型に展開)
docs = db1.employee.aggregate([
{'$lookup':{
'from': 'division',
'localField':'k_id',
'foreignField':'k_id',
'as': 'division_docs'
}},
{'$project':{
'_id':0,
'e_id':1,
'name':1,
'division_docs.name':1
}},
{'$unwind':'$division_docs'},
{'$group':{
'_id':'$e_id',
'employeeName':{'$max':'$name'},
'divisionName':{'$max':'$division_docs.name'}
}}
])
for doc in docs:
print(doc)

次回は、コレクションの二重結合を行います。

MongoDB(27) - 集計処理(aggregate)⑧コレクション結合

今回は、コレクションの結合を行います。

サンプルデータ

以前用意した会社の部、課、従業員データを使用します。

集計処理(aggregate)①データ準備 - https://ailog.site/2021/09/02/2021/0902/

コレクションの結合

$lookupを使うと、コレクションの結合を行うことができます。

従業員コレクション(employee)と課コレクション(division)を結合します。

設定値の意味は下記の通りです。

  • from
    結合先のコレクション名
  • localField
    結合元のフィールド名
  • foreignField
    結合先のフィールド名
  • as
    結合先コレクションの別名

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> db.employee.aggregate([
{$lookup:{
from: "division",
localField:"k_id",
foreignField:"k_id",
as: "division_docs"
}}
])
{ "_id" : ObjectId("612f07f568110fe533ed9d21"), "e_id" : "emp001", "k_id" : "ka1", "p_id" : "po1", "name" : "高山", "age" : 31, "salary" : 400000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d19"), "k_id" : "ka1", "b_id" : "bu1", "name" : "総務課" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d22"), "e_id" : "emp002", "k_id" : "ka2", "p_id" : "po2", "name" : "生駒", "age" : 37, "salary" : 250000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1a"), "k_id" : "ka2", "b_id" : "bu2", "name" : "企画課" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d23"), "e_id" : "emp003", "k_id" : "ka3", "p_id" : "po3", "name" : "嘉喜", "age" : 23, "salary" : 200000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1b"), "k_id" : "ka3", "b_id" : "bu3", "name" : "開発1課" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d24"), "e_id" : "emp004", "k_id" : "ka4", "p_id" : "po4", "name" : "与田", "age" : 33, "salary" : 100000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1c"), "k_id" : "ka4", "b_id" : "bu3", "name" : "開発2課" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d25"), "e_id" : "emp005", "k_id" : "ka3", "p_id" : "po4", "name" : "松田", "age" : 35, "salary" : 550000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1b"), "k_id" : "ka3", "b_id" : "bu3", "name" : "開発1課" } ] }
{ "_id" : ObjectId("612f07f568110fe533ed9d26"), "e_id" : "emp006", "k_id" : "ka4", "p_id" : "po4", "name" : "菅井", "age" : 45, "salary" : 120000, "division_docs" : [ { "_id" : ObjectId("612f07b568110fe533ed9d1c"), "k_id" : "ka4", "b_id" : "bu3", "name" : "開発2課" } ] }

“division_docs”という配列のデータが、結合したコレクションから取得したデータになります。

結合したコレクションの表示フィールドを絞る

$projectを使うと、結合したコレクションの表示フィールドを絞って表示することができます。

_idを非表示にして、従業員コレクションのnameと課コレクションのnameだけを表示してみます。

[Mongoシェル]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> db.employee.aggregate([
{$lookup:{
from: "division",
localField:"k_id",
foreignField:"k_id",
as: "division_docs"
}},
{$project:{
"_id":0,
"name":1,
"division_docs.name":1
}}
])
{ "name" : "高山", "division_docs" : [ { "name" : "総務課" } ] }
{ "name" : "生駒", "division_docs" : [ { "name" : "企画課" } ] }
{ "name" : "嘉喜", "division_docs" : [ { "name" : "開発1課" } ] }
{ "name" : "与田", "division_docs" : [ { "name" : "開発2課" } ] }
{ "name" : "松田", "division_docs" : [ { "name" : "開発1課" } ] }
{ "name" : "菅井", "division_docs" : [ { "name" : "開発2課" } ] }

指定した通りのフィールドを表示することができました。

Pythonで操作

上記の集計処理をPythonで行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import pymongo
from pymongo import MongoClient

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得
db1 = client.db1

# コレクションの結合
docs = db1.employee.aggregate([
{'$lookup':{
'from': 'division',
'localField':'k_id',
'foreignField':'k_id',
'as': 'division_docs'
}}
])
for doc in docs:
print(doc)

# 結合したコレクションの表示フィールドを絞る
docs = db1.employee.aggregate([
{'$lookup':{
'from': 'division',
'localField':'k_id',
'foreignField':'k_id',
'as': 'division_docs'
}},
{'$project':{
'_id':0,
'name':1,
'division_docs.name':1
}}
])
for doc in docs:
print(doc)

次回は、結合したコレクションをオブジェクト型と文字列型に展開します。

MongoDB(26) - 集計処理(aggregate)⑦データ数のカウント

今回は、データ数のカウントを行います。

サンプルデータ

以前用意した会社の部、課、従業員データを使用します。

集計処理(aggregate)①データ準備 - https://ailog.site/2021/09/02/2021/0902/

データ数のカウント

データ数をカウントする場合は、$countを使います。

$countには表示名を指定します。

[Mongoシェル]

1
2
3
4
> db.employee.aggregate([
{$count: "employee_count"}
])
{ "employee_count" : 6 }

従業員コレクション(employee)全体のデータ数をカウントできました。

条件を指定してデータ数のカウント

$matchに検索条件を指定すると、条件を指定してのデータ数カウントができます。

30歳以上の従業員数をカウントする場合は、以下のようになります。

[Mongoシェル]

1
2
3
4
5
> db.employee.aggregate([
{$match: {age: {$gte:30}}},
{$count: "employee_count"}
])
{ "employee_count" : 5 }

グループごとのカウント

$countでは、グループごとのカウントを行うことができません。

$group$sumを使うことでグループごとのカウントを行うことができます。

ポイントは{$sum:1}とすることです。

[Mongoシェル]

1
2
3
4
5
6
7
> db.employee.aggregate([
{$group: {_id:"$k_id", employee_count:{$sum:1}}}
])
{ "_id" : "ka2", "employee_count" : 1 }
{ "_id" : "ka3", "employee_count" : 2 }
{ "_id" : "ka4", "employee_count" : 2 }
{ "_id" : "ka1", "employee_count" : 1 }

課ごとの従業員数をカウントすることができました。

Pythonで操作

上記の集計処理をPythonで行うと、次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import pymongo
from pymongo import MongoClient

# MongoDB操作用のインスタンスを作成
client = MongoClient() # [IPとポートを指定する場合] MongoClient('10.200.243.203', 27017')

# データベースの取得
db1 = client.db1

# コレクション全体のカウント(従業員数)
docs = db1.employee.aggregate([
{'$count': 'employee_count'}
])
for doc in docs:
print(doc)

# 30歳以上の従業員数をカウント
docs = db1.employee.aggregate([
{'$match': {'age': {'$gte':30}}},
{'$count': 'employee_count'}
])
for doc in docs:
print(doc)

# 課ごとの従業員数をカウント
docs = db1.employee.aggregate([
{'$group': {'_id':'$k_id', 'employee_count':{'$sum':1}}}
])
for doc in docs:
print(doc)

次回は、コレクションの結合を行います。


Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×