[MongoDB] Version 4.4 新機能: unionWith を使ってみる

MongoDB 4.4で追加される新規から、unionWithを使ったクエリ紹介します。unionWithはSQLのunionに相当し、複数のコレクション(RDBでいうところのテーブルに相当)に対するクエリ結果を結合することが可能です。
2020.06.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、菊池です。

今週はMongoDBのオンラインカンファレンス、MongoDB Liveが開催されました。その中で、MongoDBの最新メジャーバージョンである、4.4についても多く触れられていました。MongoDB 4.4は現在、リリース候補版(以下RC版)が公開されており、検証などで利用可能になっています。(本番利用はしないようにしましょう)

今回は、MongoDB 4.4で追加される新規から、unionWithを使ったクエリ紹介します。unionWithはSQLのunionに相当し、複数のコレクション(RDBでいうところのテーブルに相当)に対するクエリ結果を結合することが可能です。

$unionWith を使ってみる

公式ドキュメントに従い、試してみます。

MongoDB 4.4 RC のインストールと接続

まずは、MondoDB 4,4 RCをインストールします。環境はCentOS7を利用しています。

まずはMogoDBのサーバ本体である、mongodをダウンロードしてインストールします。

$ wget https://repo.mongodb.org/yum/redhat/7/mongodb-org/testing/x86_64/RPMS/mongodb-org-server-4.4.0-0.1.rc9.el7.x86_64.rpm
$ sudo yum install mongodb-org-server-4.4.0-0.1.rc9.el7.x86_64.rpm

続いて同様に、コマンドラインツールである、Mongo Shellをインストールします。

$ wget https://repo.mongodb.org/yum/redhat/7/mongodb-org/testing/x86_64/RPMS/mongodb-org-shell-4.4.0-0.1.rc9.el7.x86_64.rpm
$ sudo yum install mongodb-org-shell-4.4.0-0.1.rc9.el7.x86_64.rpm

mongodを起動します。

$ sudo systemctl start mongod

接続してみましょう。

$ mongo localhost
MongoDB shell version v4.4.0-rc9
connecting to: mongodb://127.0.0.1:27017/localhost?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("245ce209-0fc8-4f9e-93e1-dd365e4871c3") }
MongoDB server version: 4.4.0-rc9
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
	http://docs.mongodb.org/
Questions? Try the support group
	http://groups.google.com/group/mongodb-user
---
The server generated these startup warnings when booting:
        2020-06-12T14:58:25.275+09:00: ***** SERVER RESTARTED *****
        2020-06-12T14:58:26.024+09:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
        2020-06-12T14:58:26.025+09:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never'
        2020-06-12T14:58:26.025+09:00: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. We suggest setting it to 'never'
---
---
        Enable MongoDB's free cloud-based monitoring service, which will then receive and display
        metrics about your deployment (disk utilization, CPU, operation statistics, etc).

        The monitoring data will be available on a MongoDB website with a unique URL accessible to you
        and anyone you share the URL with. MongoDB may use this information to make product
        improvements and to suggest MongoDB products and deployment options to you.

        To enable free monitoring, run the following command: db.enableFreeMonitoring()
        To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
>

無事、接続できました。

$unionWith を使ったクエリ

まずは、suppliersコレクションにデータを入れます。

> db.suppliers.insertMany([
...   { _id: 1, supplier: "Aardvark and Sons", state: "Texas" },
...   { _id: 2, supplier: "Bears Run Amok.", state: "Colorado"},
...   { _id: 3, supplier: "Squid Mark Inc. ", state: "Rhode Island" },
... ])
{ "acknowledged" : true, "insertedIds" : [ 1, 2, 3 ] }

同様に、warehousesコレクションにもデータを入れておきます。

> db.warehouses.insertMany([
...   { _id: 1, warehouse: "A", region: "West", state: "California" },
...   { _id: 2, warehouse: "B", region: "Central", state: "Colorado"},
...   { _id: 3, warehouse: "C", region: "East", state: "Florida" },
... ])
{ "acknowledged" : true, "insertedIds" : [ 1, 2, 3 ] }

各コレクション、このようにデータが入りました。

> db.suppliers.find()
{ "_id" : 1, "supplier" : "Aardvark and Sons", "state" : "Texas" }
{ "_id" : 2, "supplier" : "Bears Run Amok.", "state" : "Colorado" }
{ "_id" : 3, "supplier" : "Squid Mark Inc. ", "state" : "Rhode Island" }
>
> db.warehouses.find()
{ "_id" : 1, "warehouse" : "A", "region" : "West", "state" : "California" }
{ "_id" : 2, "warehouse" : "B", "region" : "Central", "state" : "Colorado" }
{ "_id" : 3, "warehouse" : "C", "region" : "East", "state" : "Florida" }
>

aggregateパイプラインで、unionWithを使って、stateを取得してみます。

> db.suppliers.aggregate([
...    { $project: { state: 1, _id: 0 } },
...    { $unionWith: { coll: "warehouses", pipeline: [ { $project: { state: 1, _id: 0 } } ]} }
... ])
{ "state" : "Texas" }
{ "state" : "Colorado" }
{ "state" : "Rhode Island" }
{ "state" : "California" }
{ "state" : "Colorado" }
{ "state" : "Florida" }
>

この結果だと、"state" : "Colorado" が重複します。(SQLにおけるUNION ALLに近い結果です)重複を除くため、$groupにてstateでグループ化します。

> db.suppliers.aggregate([
...    { $project: { state: 1, _id: 0 } },
...    { $unionWith: { coll: "warehouses", pipeline: [ { $project: { state: 1, _id: 0 } } ]} },
...    { $group: { _id: "$state" } }
... ])
{ "_id" : "Texas" }
{ "_id" : "Florida" }
{ "_id" : "Rhode Island" }
{ "_id" : "Colorado" }
{ "_id" : "California" }
>

重複が含まれない結果が取れました。

 最後に

MongoDB 4.4 の新機能、unionWithを使ったクエリを試してみました。クエリの幅が広がることで、アプリケーションの開発がやりやすくなるかと思います。4.4が正式リリースされたらぜひアップデートしていきましょう。