はじめに
この実験では、MongoDB のプロジェクションを使用して、クエリ結果にどのフィールドを表示するかを制御する方法を学びます。プロジェクションは、返す特定のフィールドを選択できる強力な機能であり、ネットワーク経由で転送されるデータ量を削減し、クエリパフォーマンスを向上させることができます。フィールドの含め方と除外方法、ネストされたドキュメント内のフィールドのプロジェクション、および集計フレームワークを使用して出力を再形成する方法を練習します。この実験の終わりには、MongoDB クエリでプロジェクションを効果的に使用する方法について、確かな理解が得られるでしょう。
プロジェクションで特定のフィールドを選択する
この最初のステップでは、クエリ結果で表示したい特定のフィールドのみを選択することで、プロジェクションの基本を学びます。これは「包括的」プロジェクションとして知られています。
まず、MongoDB Shell を開きます。この対話型のコマンドラインインターフェイスで、この実験のすべてのデータベースコマンドを実行します。
mongosh
これで MongoDB Shell 内に入りました。ターミナルのプロンプトが接続中であることを示すように変わります。projectlab_database という新しいデータベースに切り替え、サンプルデータを挿入しましょう。データベースが存在しない場合、MongoDB は最初にデータを保存したときに自動的に作成します。
use projectlab_database
次に、users という名前のコレクションに 3 つのドキュメントを挿入します。
db.users.insertMany([
{
name: "John Doe",
age: 30,
email: "john.doe@example.com",
city: "New York",
job: "Software Engineer"
},
{
name: "Jane Smith",
age: 28,
email: "jane.smith@example.com",
city: "San Francisco",
job: "Data Scientist"
},
{
name: "Mike Johnson",
age: 35,
email: "mike.johnson@example.com",
city: "Chicago",
job: "Product Manager"
}
]);
次に、このコレクションをクエリします。すべてのドキュメントの name フィールドと age フィールドのみを返すには、find() メソッドにプロジェクションドキュメントを 2 番目の引数として追加します。プロジェクションドキュメントでは、含めたいフィールドに 1 を指定します。デフォルトでは、_id フィールドは常に返されます。これを 0 に設定することで明示的に除外できます。
db.users.find({}, { name: 1, age: 1, _id: 0 });
各ユーザーの name と age フィールドのみを含む以下の出力が表示されるはずです。
[
{ "name": "John Doe", "age": 30 },
{ "name": "Jane Smith", "age": 28 },
{ "name": "Mike Johnson", "age": 35 }
]
この手法は、必要なデータのみを取得するのに非常に役立ちます。これは、大きなドキュメントを扱う場合に特に重要です。
結果から特定のフィールドを除外する
含めるフィールドを選択することに加えて、除外するフィールドを指定することもできます。これは「排他的」プロジェクションとして知られており、ほとんどのフィールドを表示したいが、機密性の高いフィールドや大きなフィールドなど、いくつかを除外したい場合に便利です。
引き続き mongosh シェルで projectlab_database が選択されている状態であると想定します。
フィールドを除外するには、プロジェクションドキュメントでそれらの値を 0 に設定します。すべてのユーザーデータを取得し、email フィールドと city フィールドを除外するクエリを実行してみましょう。
db.users.find({}, { email: 0, city: 0 });
出力には、email と city を除くすべてのフィールドが含まれます。デフォルトでは _id フィールドは引き続き含まれていることに注意してください。
[
{
_id: ObjectId("..."),
name: 'John Doe',
age: 30,
job: 'Software Engineer'
},
{
_id: ObjectId("..."),
name: 'Jane Smith',
age: 28,
job: 'Data Scientist'
},
{
_id: ObjectId("..."),
name: 'Mike Johnson',
age: 35,
job: 'Product Manager'
}
]
プロジェクションにおける重要なルールは、同じプロジェクションドキュメント内で含めること (1) と除外すること (0) を混在させることはできないということです。このルール唯一の例外は _id フィールドです。前のステップで行ったように、他のフィールドを含める場合でも、_id フィールドを明示的に除外 (_id: 0) できます。排他的プロジェクションで除外することも可能です。
例えば、email、city、および _id を除外するには、次のように実行します。
db.users.find({}, { email: 0, city: 0, _id: 0 });
このクエリは、name、age、および job フィールドのみを含むドキュメントを返します。
ネストされたドキュメントのフィールドをプロジェクションする
実際のデータは、ネストされたドキュメントや配列で構造化されていることがよくあります。MongoDB のプロジェクション機能を使用すると、ドキュメント構造の奥深くに埋め込まれたフィールドであっても、正確に選択できます。
まず、既存の users コレクションをクリアし、ネストされた contact オブジェクトと skills 配列を持つ新しいドキュメントを挿入しましょう。
db.users.deleteMany({});
次に、新しいデータを挿入します。
db.users.insertMany([
{
name: "John Doe",
contact: {
email: "john.doe@example.com",
phone: {
mobile: "123-456-7890",
work: "987-654-3210"
}
},
skills: ["JavaScript", "MongoDB", "React"]
},
{
name: "Jane Smith",
contact: {
email: "jane.smith@example.com",
phone: {
mobile: "234-567-8901",
work: "876-543-2109"
}
},
skills: ["Python", "Data Science", "Machine Learning"]
}
]);
ネストされたドキュメント内のフィールドをプロジェクションするには、「ドット記法」を使用します。例えば、ユーザーの名前と携帯電話番号を選択するには、"contact.phone.mobile": 1 を指定します。name、email、および mobile 電話番号をプロジェクションしてみましょう。
db.users.find(
{},
{
name: 1,
"contact.email": 1,
"contact.phone.mobile": 1,
_id: 0
}
);
結果は、選択されたフィールドのネストされた構造を保持します。
[
{
"name": "John Doe",
"contact": {
"email": "john.doe@example.com",
"phone": { "mobile": "123-456-7890" }
}
},
{
"name": "Jane Smith",
"contact": {
"email": "jane.smith@example.com",
"phone": { "mobile": "234-567-8901" }
}
}
]
$slice 演算子を使用して配列の一部をプロジェクションすることもできます。各ユーザーの名前と最初の 2 つのスキルのみを取得するには、次のコマンドを実行します。
db.users.find({}, { name: 1, skills: { $slice: 2 }, _id: 0 });
出力には、各ユーザーの skills 配列に 2 つの要素のみが含まれます。
[
{ "name": "John Doe", "skills": ["JavaScript", "MongoDB"] },
{ "name": "Jane Smith", "skills": ["Python", "Data Science"] }
]
集計による出力フィールドの形状変更
フィールド名の変更や新しい計算フィールドの作成など、より高度な変換には、find() メソッドのプロジェクションだけでは不十分です。代わりに、集計フレームワーク、特に $project ステージを使用できます。
このステップのためにデータを準備しましょう。コレクションをクリアし、給与情報を持つユーザーを挿入します。
db.users.deleteMany({});
db.users.insertMany([
{
name: "John Doe",
age: 30,
salary: 75000,
department: "Engineering"
},
{
name: "Jane Smith",
age: 28,
salary: 85000,
department: "Data Science"
},
{
name: "Mike Johnson",
age: 35,
salary: 95000,
department: "Management"
}
]);
集計パイプラインの $project ステージは、新しいフィールドの追加、既存フィールドの名前変更、またはフィールドの削除によってドキュメントを再形成できます。これを使用するには、ステージの配列を aggregate() メソッドに渡します。
フィールドの名前を変更するには、新しいフィールド名を定義し、それに既存フィールドの値($ 記号を先頭に付けたもの)を割り当てます。name を fullName に、age を yearsOld に名前を変更してみましょう。
db.users.aggregate([
{
$project: {
fullName: "$name",
yearsOld: "$age",
_id: 0
}
}
]);
計算に基づいて新しいフィールドを作成することもできます。年間の salary を 12 で割って monthlySalary フィールドを作成し、$switch 演算子を使用した条件付きロジックに基づいて salaryTier フィールドを作成しましょう。
db.users.aggregate([
{
$project: {
name: 1,
monthlySalary: { $divide: ["$salary", 12] },
salaryTier: {
$switch: {
branches: [
{ case: { $lt: ["$salary", 80000] }, then: "Junior" },
{ case: { $gte: ["$salary", 80000] }, then: "Senior" }
],
default: "Unknown"
}
},
_id: 0
}
}
]);
出力は、$project ステージで定義した完全に新しいドキュメント構造になります。
[
{ "name": "John Doe", "monthlySalary": 6250, "salaryTier": "Junior" },
{
"name": "Jane Smith",
"monthlySalary": 7083.333333333333,
"salaryTier": "Senior"
},
{
"name": "Mike Johnson",
"monthlySalary": 7916.666666666667,
"salaryTier": "Senior"
}
]
$project ステージにより、クエリ結果の最終的な形状を完全に制御でき、データベース内で直接強力なデータ変換が可能になります。
まとめ
この実験では、プロジェクションを使用して MongoDB クエリの出力を効果的に制御する方法を学びました。まず、find() メソッドを使用してフィールドを含めたり除外したりする基本的な方法から始めました。次に、ドット記法を使用してネストされたドキュメント内のフィールドをプロジェクションする方法や、$slice 演算子を使用して配列のサブセットを返す方法など、より高度なトピックに進みました。最後に、集計フレームワークの $project ステージを使用して、フィールドの名前を変更したり、新しい計算フィールドを作成したり、条件付きロジックを適用したりすることでドキュメントを完全に再形成する力を探りました。これらのスキルは、MongoDB で効率的かつ正確なクエリを作成するための基本となります。

