今回は「MongoDB で コレクションを結合する方法」についてまとめます。
MongoDB 3.2 以上 と 3.6 以上 で使える構文が違いますが、今回は両方共の構文を紹介します。
3.6以上であれば条件指定でコレクション結合できるのでよりRDBと似たような複雑なクエリが記載できるようになています。
前提データ
以下のデータを mongo コマンドで投入してある状態で name フィールド を displayName フィールドに変換して取得する方法を見ていきます。
> db.fruits.insertMany([
{ code: "0101", name: "apple", price: 120, instock: 10 },
{ code: "0201", name: "banana", price: 180, instock: 8 },
{ code: "0301", name: "orange", price: 130, instock: 5 },
{ code: "0302", name: "grapefruit", price: 150, instock: 7 },
{ code: "0401", name: "persimmon", price: 100, instock: 6 },
{ code: "0102", name: "pear", price: 130, instock: 12 },
]);
> db.orders.insertMany([
{ item: "apple", quantity: 2 },
{ item: "orange", quantity: 6 }
]);
結合フィールドを指定してコレクション結合
単一のフィールド同士を結合条件としてコレクション結合を行う方法です。
MongoDB 3.2 以上で利用可能な構文です。
構文
{
$lookup: {
from: <結合したいコレクション名>
localField: <入力コレクションで結合条件にしたいフィールド名>
foreignField: <結合したいコレクションで結合条件にしたいフィールド名>
as: <結果配列の出力フィールド名>
}
}
サンプルコード
この構文を利用したコレクション結合のサンプルとして、
orders コレクション に対して fruits コレクションを結合するサンプルコードを見ていきましょう。
SQLだと以下のようなイメージのコードをMongoDBで実装するサンプルになります。
SELECT * FROM orders LEFT JOIN fruits WHERE orders.item = fruits.name;
コード (index.js)
var MongoClient = require("mongodb").MongoClient;
var URL = "mongodb://localhost:27017/test";
MongoClient.connect(URL, (err, client) => {
var db = client.db("test");
db.collection("orders").aggregate([
{
$lookup: {
from: "fruits",
localField: "item",
foreignField: "name",
as: "inventory"
}
}
]).toArray()
.then((docs) => {
console.log(docs);
})
.catch((err) => {
console.log(err);
})
.then(() => {
client.close();
});
});
実行 & 結果
> node .\index.js
[ { _id: 5a63f919f43c88db76c72327,
item: 'apple',
quantity: 2,
inventory:
[ { _id: 5a63f913f43c88db76c72321,
code: '0101',
name: 'apple',
price: 120,
instock: 10 } ] },
{ _id: 5a63f919f43c88db76c72328,
item: 'orange',
quantity: 6,
inventory:
[ { _id: 5a63f913f43c88db76c72323,
code: '0301',
name: 'orange',
price: 130,
instock: 5 } ] } ]
inventory
プロパティは配列で実際の実行結果は inventory[ [Object] ]
で表示されます。
この状態では中身がわからないので理解しやすいよう上記の結果は中身を展開した形で記載しています。
配列フィールドを展開したい場合、$unwind
ステージを利用すると展開することができます。
条件指定してコレクション結合
より複雑な条件を指定してコレクション結合を行う方法です。
こちらは MongoDb 3.6 以上で利用可能な構文になります。
構文
{
$lookup: {
from: <結合したいコレクション名>
let: { <変数名1>: <値1> ... }
pipeline: [ <Aggregateパイプライン> ]
as: <結果配列の出力フィールド名>
}
}
pipeline
の中で入力コレクションのフィールドへ単純アクセスはできません。
あらかじめ let
プロパティへ pipeline 中で利用したい入力コレクションのフィールドを指定しておき、pipeline中では $expr
オペレーター を利用してアクセスします。
サンプルコード
orders コレクション に対して fruits コレクションを結合してみます。
今回は結合条件として fruits コレクションの instock が不足している orders ドキュメントのみを抽出できるようにします。
コード (index.js)
var MongoClient = require("mongodb").MongoClient;
var URL = "mongodb://localhost:27017/test";
MongoClient.connect(URL, (err, client) => {
var db = client.db("test");
db.collection("orders").aggregate([
{
$lookup: {
from: "fruits",
let: { order_item: "$item", order_qty: "$quantity" },
pipeline: [{
$match: {
$expr: {
$and: [
{ $eq: ["$$order_item", "$name"] },
{ $gt: ["$$order_qty", "$instock"] }]
}
}
}],
as: "inventory"
}
}, {
$match: { inventory: { $not: { $size: 0 } } }
}
]).toArray()
.then((docs) => {
console.log(docs);
})
.catch((err) => {
console.log(err);
})
.then(() => {
client.close();
});
});
実行 & 結果
> node .\index.js
[ { _id: 5a63f919f43c88db76c72328,
item: 'orange',
quantity: 6,
inventory:
[ { _id: 5a63f913f43c88db76c72323,
code: '0301',
name: 'orange',
price: 130,
instock: 5 } ] } ]
inventory
プロパティは配列で実際の実行結果は inventory[ [Object] ]
で表示されます。
前述の「結合フィールドを指定してコレクション結合」の実行結果と同様に、この状態では中身がわからないので理解しやすいよう上記の結果は中身を展開した形で記載しています。
今回は「MongoDB で コレクションを結合する方法」についてまとめました。
ポイントは以下の通りです。
- 3.2以上なら単一フィールド結合
- 3.6以上ならより複雑な結合
参考になったでしょうか?
本記事がお役に立っていると嬉しいです!!
参考記事