定義
互換性
次の環境でホストされる配置には $project
を使用できます。
MongoDB Atlas はクラウドでの MongoDB 配置のためのフルマネージド サービスです
MongoDB Enterprise: サブスクリプションベースの自己管理型 MongoDB バージョン
MongoDB Community: ソースが利用可能で、無料で使用できる自己管理型の MongoDB のバージョン
構文
$project
ステージのプロトタイプ形式は次のとおりです。
{ $project: { <specification(s)> } }
$project
が受け入れるドキュメントでは、含めるフィールド、非表示にする _id
フィールド、新たに加えるフィールド、値をリセットする既存フィールドの指定が可能です。除外するフィールドを指定することもできます。
$project
の指定形式は次のとおりです。
フォーム | 説明 |
---|---|
| フィールドを含めるかどうかを指定します。ゼロ以外の整数も |
|
条件付きでフィールドを除外するには、代わりに |
| 新規フィールドを追加するか、既存フィールドの値をリセットします。 式が |
| フィールドの除外を指定します。 条件付きでフィールドを除外するには、代わりに
フィールドを除外するには、「 |
動作
含めるフィールドの指定
_id
フィールド
_id
フィールドはデフォルトで出力ドキュメントに含まれます。出力ドキュメントから _id
フィールドを除外するには、$project
で _id
フィールドの非表示を指定します。
フィールドの除外
1 つまたは複数のフィールドを除外するように指定すると、他のすべてのフィールドが出力ドキュメントに返されます。
{ $project: { "<field1>": 0, "<field2>": 0, ... } } // Return all but the specified fields
_id
以外のフィールドを除外するよう指定した場合、$project
の他の指定形式は採用できません。つまり、フィールドを除外する場合、含めるフィールドの指定、既存フィールド値のリセット、新規フィールドの追加のいずれの操作もできません。この制限は、REMOVE
変数を使用したフィールドの条件付き除外には適用されません。
フィールドを除外するには、「$unset
」ステージも参照してください。
条件付きでのフィールドの除外
集計式で変数 REMOVE
を使用して、条件付きでフィールドを非表示にできます。使用例については、「条件付きでのフィールドの除外」を参照してください。
新規フィールドの追加または既存フィールドのリセット
注意
MongoDB には、ドキュメントに新しいフィールドを追加するための $addFields
も用意されています。
新しいフィールドを追加したり、既存フィールドの値をリセットしたりするには、フィールド名を指定して、その値を何らかの式に設定します。式の詳細については、「式演算子」を参照してください。
リテラル値
フィールドの値をリテラルに解決される式ではなく、数値やブール値のリテラルに直接設定する場合は、$literal
演算子を使用しますそれ以外の場合、$project
は数値またはブール値のリテラルを、フィールドを含めるまたは除外するためのフラグとして扱います。
フィールド名の変更
新規フィールドを指定し、既存フィールドのフィールドパスに値を設定すると、フィールド名を実質的に変更できます。
新しい配列フィールド
$project
ステージでは、角括弧 []
を使用して新しい配列フィールドを直接作成できます。ドキュメントに存在しない配列フィールドを指定した場合にこの操作を実行すると、その配列フィールドの値は null
に置き換わります。使用例については、「プロジェクトの新しい配列フィールド」を参照してください。
$project
ステージでは配列インデックスを使用できません。詳細については、「配列インデックスの非対応」を参照してください。
埋め込みドキュメントのフィールド
埋め込みドキュメント内のフィールドをプロジェクションするか追加/リセットする場合、次の例のようにドット表記を使用できます。
"contact.address.country": <1 or 0 or expression>
または、フィールドをネストできます。
contact: { address: { country: <1 or 0 or expression> } }
フィールドをネストする場合、次例のとおり、フィールドを指定するために埋め込みドキュメント内でドット表記を使用することはできません。contact: {
"address.country": <1 or 0 or expression> }
は無効です。
埋め込みフィールドでのパスの不一致エラー
埋め込みドキュメントとその埋め込みドキュメント内のフィールドの両方を同じプロジェクションで指定することはできません。
次の $project
ステージは、埋め込み contact
ドキュメントと contact.address.country
フィールドの両方をプロジェクトしようとするため、Path collision
エラーで失敗します。
{ $project: { contact: 1, "contact.address.country": 1 } }
親ドキュメントと埋め込みフィールドの指定順序にかかわらず、エラーが発生します。次の $project
は、同じエラーで失敗します。
{ $project: { "contact.address.country": 1, contact: 1 } }
$project
ステージ配置
$project
ステージを使用する場合、通常このステージをパイプラインの最後のステージにして、クライアントに返すフィールドを指定するために使用します。
パイプラインの最初や途中に $project
ステージを使用して、その後のパイプラインステージに渡されるフィールドの数を減らすことは、パフォーマンスの向上にはつながりません。なぜなら、データベースはこの最適化を自動的に行うからです。
Considerations
空の仕様
MongoDB は、$project
ステージが空のドキュメントとして渡される場合、エラーを返します。
たとえば、次のパイプラインを実行するとエラーが発生します。
db.myCollection.aggregate( [ { $project: { } } ] )
Array Index
$project
ステージでは配列インデックスを使用できません。詳細については、「配列インデックスの非対応」を参照してください。
例
出力ドキュメントへの特定のフィールドの追加
次の文書を持つbooks
コレクションを考えます。
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5 }
次の $project
ステージでは、_id
フィールド、title
フィールド、author
フィールドのみが出力ドキュメントに含まれます。
db.books.aggregate( [ { $project : { title : 1 , author : 1 } } ] )
この操作の結果、次のドキュメントが作成されます。
{ "_id" : 1, "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } }
_id
フィールドを出力ドキュメントに表示しない
_id
フィールドは常時含まれるようにデフォルト設定されています。$project
ステージの出力ドキュメントから _id
フィールドを除外するには、プロジェクション ドキュメントで _id
フィールドを 0
に設定してこのフィールドの除外を指定します。
次の文書を持つbooks
コレクションを考えます。
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5 }
次の $project
ステージでは _id
フィールドは出力ドキュメントから除外されていますが、title
フィールドと author
フィールドは出力ドキュメントに含まれています。
db.books.aggregate( [ { $project : { _id: 0, title : 1 , author : 1 } } ] )
この操作の結果、次のドキュメントが作成されます。
{ "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } }
出力ドキュメントからのフィールドの除外
次の文書を持つbooks
コレクションを考えます。
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5, lastModified: "2016-07-28" }
次の $project
ステージでは、lastModified
フィールドが出力から除外されます。
db.books.aggregate( [ { $project : { "lastModified": 0 } } ] )
フィールドを除外するには、「$unset
」ステージも参照してください。
埋め込みドキュメントからフィールドを除外する場合
次の文書を持つbooks
コレクションを考えます。
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5, lastModified: "2016-07-28" }
次の $project
ステージでは、author.first
フィールドと lastModified
フィールドが出力から除外されます。
db.books.aggregate( [ { $project : { "author.first" : 0, "lastModified" : 0 } } ] )
あるいは、除外指定をドキュメント内にネストすることもできます。
db.bookmarks.aggregate( [ { $project: { "author": { "first": 0}, "lastModified" : 0 } } ] )
どちらを指定しても出力結果は同じになります。
{ "_id" : 1, "title" : "abc123", "isbn" : "0001122223334", "author" : { "last" : "zzz" }, "copies" : 5, }
フィールドを除外するには、「$unset
」ステージも参照してください。
フィールドの条件付き除外
集計式で変数 REMOVE
を使用して、条件付きでフィールドを非表示にできます。
次の文書を持つbooks
コレクションを考えます。
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5, lastModified: "2016-07-28" } { "_id" : 2, title: "Baked Goods", isbn: "9999999999999", author: { last: "xyz", first: "abc", middle: "" }, copies: 2, lastModified: "2017-07-21" } { "_id" : 3, title: "Ice Cream Cakes", isbn: "8888888888888", author: { last: "xyz", first: "abc", middle: "mmm" }, copies: 5, lastModified: "2017-07-22" }
次の $project
ステージでは、author.middle
フィールドが ""
と等しい場合に限り、REMOVE
変数を使用してそのフィールドを除外します。
db.books.aggregate( [ { $project: { title: 1, "author.first": 1, "author.last" : 1, "author.middle": { $cond: { if: { $eq: [ "", "$author.middle" ] }, then: "$$REMOVE", else: "$author.middle" } } } } ] )
集計操作の結果、次の出力が得られます。
{ "_id" : 1, "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } } { "_id" : 2, "title" : "Baked Goods", "author" : { "last" : "xyz", "first" : "abc" } } { "_id" : 3, "title" : "Ice Cream Cakes", "author" : { "last" : "xyz", "first" : "abc", "middle" : "mmm" } }
Tip
$addFields との比較
ドキュメントフィールドを削除するには、$addFields
または $project
ステージを使用できます。最適なアプローチは、パイプラインと、元のドキュメントをどれだけ保持するかによって異なります。
$addFields
ステージで $$REMOVE
を使用する例については、「フィールドの削除」を参照してください。
埋め込みドキュメントの特定フィールドを含める場合
以下のドキュメントを持つbookmarks
コレクションを考えてみましょう。
{ _id: 1, user: "1234", stop: { title: "book1", author: "xyz", page: 32 } } { _id: 2, user: "7890", stop: [ { title: "book2", author: "abc", page: 5 }, { title: "book3", author: "ijk", page: 100 } ] }
埋め込みドキュメントの title
フィールドのみを stop
フィールドに含めるには、ドット表記を使用できます。
db.bookmarks.aggregate( [ { $project: { "stop.title": 1 } } ] )
または、ドキュメント内に含める指定をネストすることもできます。
db.bookmarks.aggregate( [ { $project: { stop: { title: 1 } } } ] )
どちらを指定しても結果は次のドキュメントになります。
{ "_id" : 1, "stop" : { "title" : "book1" } } { "_id" : 2, "stop" : [ { "title" : "book2" }, { "title" : "book3" } ] }
計算フィールドの追加
次の文書を持つbooks
コレクションを考えます。
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5 }
次の $project
ステージは、新フィールドの isbn
、lastName
、copiesSold
を追加します。
db.books.aggregate( [ { $project: { title: 1, isbn: { prefix: { $substr: [ "$isbn", 0, 3 ] }, group: { $substr: [ "$isbn", 3, 2 ] }, publisher: { $substr: [ "$isbn", 5, 4 ] }, title: { $substr: [ "$isbn", 9, 3 ] }, checkDigit: { $substr: [ "$isbn", 12, 1] } }, lastName: "$author.last", copiesSold: "$copies" } } ] )
この操作の結果、次のドキュメントが作成されます。
{ "_id" : 1, "title" : "abc123", "isbn" : { "prefix" : "000", "group" : "11", "publisher" : "2222", "title" : "333", "checkDigit" : "4" }, "lastName" : "zzz", "copiesSold" : 5 }
新しい配列フィールドのプロジェクト
たとえば、コレクションに次のドキュメントが含まれているとします。
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "x" : 1, "y" : 1 }
次の操作では、x
フィールドと y
フィールドを新規フィールドである myArray
の要素としてプロジェクションします。
db.collection.aggregate( [ { $project: { myArray: [ "$x", "$y" ] } } ] )
この操作を実行すると次のドキュメントが返されます。
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "myArray" : [ 1, 1 ] }
ドキュメントに存在しないフィールドが配列の指定に含まれている場合、操作とそのフィールドの値は null
に置き換えられます。
たとえば、上記と同じドキュメントで次の操作を実行すると、x
フィールドと y
フィールド、および存在しない $someField
フィールドが新規フィールドである myArray
の要素としてプロジェクションされます。
db.collection.aggregate( [ { $project: { myArray: [ "$x", "$y", "$someField" ] } } ] )
この操作を実行すると次のドキュメントが返されます。
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "myArray" : [ 1, 1, null ] }
配列インデックスはサポート対象外
$project
ステージでは配列インデックスを使用できません。このセクションで一例を挙げます。
次の pizzas
コレクションを作成します。
db.pizzas.insert( [ { _id: 0, name: [ 'Pepperoni' ] }, ] )
次の例ではピザが返されます。
db.pizzas.aggregate( [ { $project: { x: '$name', _id: 0 } }, ] )
出力例ではピザが返されます。
[ { x: [ 'Pepperoni' ] } ]
次の例では、配列インデックス($name.0
)を使用してピザを返そうと試みています。
db.pizzas.aggregate( [ { $project: { x: '$name.0', _id: 0 } }, ] )
出力例では、ピザは返されません。
[ { x: [] } ]
このページのC#の例では、Atlasサンプルデータセット の sample_mflix
データベースを使用します。MongoDB Atlasクラスターを無料で作成して、サンプルデータセットをロードする方法については、 MongoDB .NET/ C#ドライバーのドキュメントの「 開始 」を参照してください。
次の Movie
クラスと ImdbData
クラスは、sample_mflix.movies
コレクション内のドキュメントをモデル化します。
public class Movie { public ObjectId Id { get; set; } public string Title { get; set; } public List<string> Genres { get; set; } public List<string> Directors { get; set; } public List<string> Writers { get; set; } public string Type { get; set; } public string Plot { get; set; } public ImdbData Imdb { get; set; } public List<string> Cast { get; set; } }
public class ImdbData { public string Id { get; set; } public int Votes { get; set; } public float Rating { get; set; } }
注意
パスカルケース用の ConventionPack
このページのC#クラスはプロパティ名にパスカルケースを使用しますが、 MongoDBコレクション内のフィールド名はキャメルケースを使用します。この違いを考慮するには、アプリケーションの起動時に次のコードを使用して ConventionPack
を登録します。
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() }; ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);
MongoDB .NET/ C#ドライバーを使用して $project
ステージを集計パイプラインに追加するには、PipelineDefinition
オブジェクトで Project()
メソッドを呼び出し、ProjectionDefinitionBuilder<TDocument>
オブジェクトを渡します。 TDocument
は、コレクション内のドキュメントを表すクラスです。
次のセクションでは、$project
ステージの出力ドキュメントをカスタマイズするさまざまな方法を示します。
出力ドキュメントへの特定のフィールドの追加
.NET/ C#ドライバーを使用するときに特定のフィールドを含めるには、プロジェクションビルダで Include()
メソッドを呼び出します。複数のフィールドを含めるには、Include()
の呼び出しを連鎖させることができます。
次のコード例では、_id
、plot
、title
フィールドのみを含むドキュメントが生成されます。
var pipeline = new EmptyPipelineDefinition<Movie> () .Project( Builders<Movie>.Projection .Include(m => m.Title) .Include(m => m.Plot) ); var result = movieCollection.Aggregate(pipeline).FirstOrDefault();
パイプラインは、次のドキュメントを返します。
{ "_id" : { "$oid" : "573a1390f29313caabcd42e8" }, "plot" : "A group of bandits stage a brazen train hold-up, only to find a determined posse hot on their heels.", "title" : "The Great Train Robbery" }
出力ドキュメントからのフィールドの除外
.NET/ C#ドライバーを使用するときに結果ドキュメントからフィールドを除外するには、プロジェクションビルダーで Exclude()
メソッドを呼び出します。 Exclude()
の呼び出しを連鎖させて、複数のフィールドを除外できます。
次のコード例では、Type
フィールドを除外するドキュメントを生成します。
var pipeline = new EmptyPipelineDefinition<Movie>() .Project( Builders<Movie>.Projection .Exclude(m => m.Type) ); var result = movieCollection.Aggregate(pipeline).FirstOrDefault();
デフォルトでは 、結果ドキュメントには常に _id
フィールドが含まれます。次のコード例では、_id
フィールドを除外するが、plot
フィールドと title
フィールドを含むドキュメントが生成されます。
var pipeline = new EmptyPipelineDefinition<Movie> () .Project( Builders<Movie>.Projection .Exclude(m => m.Id) .Include(m => m.Title) .Include(m => m.Plot) ); var result = movieCollection.Aggregate(pipeline).FirstOrDefault();
パイプラインは、次のドキュメントを生成します。
{ "plot" : "A group of bandits stage a brazen train hold-up, only to find a determined posse hot on their heels.", "title" : "The Great Train Robbery" }
埋め込みドキュメントからフィールドを除外する場合
.NET/ C#ドライバーを使用するときに埋め込みドキュメント内のフィールドを除外するには、プロジェクションビルダーで Exclude()
メソッドを呼び出し、対応するクラスプロパティへのパスを渡します。 Exclude()
の呼び出しを連鎖させて、複数のフィールドを除外できます。
次のコード例では、imdb.id
フィールドと type
フィールドを除外するドキュメントが生成されます。
var pipeline = new EmptyPipelineDefinition<Movie> () .Project( Builders<Movie>.Projection .Exclude("Imdb.id") .Exclude(m => m.Type) ); var result = movieCollection.Aggregate(pipeline).FirstOrDefault();
パイプラインは、次の出力を生成します。
{ "plot" : "A group of bandits stage a brazen train hold-up, only to find a determined posse hot on their heels.", "title" : "The Great Train Robbery" ... "imdb" : { "rating" : 7.4000000000000004, "votes" : 9847 } }
注意
埋め込みIDフィールドに文字列を使用
埋め込みドキュメントにIDフィールドをプロジェクトには、フィールド名を ラムダ式ではなく string として指定します。
フィールドの条件付き除外
集計で変数REMOVE
を使用して、条件付きでフィールドを非表示にできます。
var stage = new BsonDocument { { "title", 1 }, { "imdb.id", 1 }, { "imdb.rating", 1 }, { "imdb.votes", new BsonDocument("$cond", new BsonDocument { { "if", new BsonDocument("$eq", new BsonArray { "", "$imdb.votes" }) }, { "then", "$$REMOVE" }, { "else", "$imdb.votes" } }) } }; var pipeline = new EmptyPipelineDefinition<Movie>() .Project(stage) .Sample(1); var result = movieCollection.Aggregate(pipeline).FirstOrDefault();
注意
条件付き除外のビルダなし
上記の例ではBsonDocument
オブジェクトを使用します.NET/ C#ドライバーは条件付きでフィールドを除外するビルダを提供していないためです。他のMongoDB言語ドライバーはこの機能をサポートしている可能性があります。詳しくは、 MongoDBドライバーのドキュメント を参照してください。
サンプリングされたドキュメントに imdb.votes
フィールドが含まれている場合、パイプラインは次のようなドキュメントを返します。
{ "_id" : { "$oid" : "573a1390f29313caabcd42e8" }, "title" : "The Great Train Robbery" "imdb" : { "rating" : 7.4000000000000004, "id" : 439, "votes" : 9847 } }
ドキュメントに imdb.votes
フィールドが含まれていない場合、パイプラインは次のようなドキュメントを返します。
{ "_id" : { "$oid" : "573a1398f29313caabce94a3" }, "title" : "This Is Spinal Tap", "imdb" : { "rating" : 8.0, "id" : 88258 } }
計算フィールドの追加
.NET/ C#ドライバーを使用するときに結果ドキュメントに計算フィールドを含めるには、プロジェクションビルダーで Expression()
メソッドを呼び出し、計算フィールドを含む式を渡します。型の安全性を高めるために、次の ProjectedMovie
クラスのように、結果ドキュメントのモデルクラスを定義できます。
public class ProjectedMovie { public ObjectId Id { get; set; } public string Title { get; set; } public string LeadActor { get; set; } public List<string> Crew { get; set; } }
次のコード例では、複数の計算フィールドを含むドキュメントを生成します。
var pipeline = new EmptyPipelineDefinition<Movie>() .Project( Builders<Movie> .Projection .Expression(m => new ProjectedMovie { Id = m.Id, Title = m.Title, LeadActor = m.Cast[0], }) ); var result = movieCollection.Aggregate(pipeline).FirstOrDefault();
パイプラインは、次のドキュメントを生成します。
{ "_id" : { "$oid" : "573a1390f29313caabcd42e8" }, "title" : "The Great Train Robbery" "leadActor" : "A.C. Abadie", ... }
新しい配列フィールドのプロジェクト
.NET/ C#ドライバーを使用するときに結果ドキュメントに新しい配列フィールドをプロジェクトには、プロジェクションビルダーで Expression()
メソッドを呼び出し、新しい配列フィールドを含む式を渡します。型の安全性を高めるために、次の ProjectedMovie
クラスのように、結果ドキュメントのモデルクラスを定義できます。
public class ProjectedMovie { public ObjectId Id { get; set; } public string Title { get; set; } public string LeadActor { get; set; } public List<string> Crew { get; set; } }
次のコード例では、directors
フィールドと writers
フィールドの値を含む新しい配列フィールド、crew
を含むドキュメントを生成します。
var pipeline = new EmptyPipelineDefinition<Movie> () .Project( Builders<Movie> .Projection .Expression(m => new ProjectedMovie { Id = m.Id, Title = m.Title, LeadActor = m.Cast[0], Crew = m.Directors.Concat(m.Writers).ToList() } ) ) .Sample(1); var result = movieCollection.Aggregate(pipeline).FirstOrDefault();
パイプラインは、次のようなドキュメントを返します。
{ "_id" : { "$oid" : "573a1395f29313caabce2297" }, "title" : "The Chalk Garden", "leadActor" : "Deborah Kerr", "crew" : ["Ronald Neame", "John Michael Hayes (screenplay)", "Enid Bagnold (from the play by)"] }
ドキュメントに存在しないフィールドが配列の指定に含まれている場合、パイプラインはそのフィールドの値として null
を置き換えます。例、次のコード例では、 directors
、writers
、および存在しないフィールド である makeupArtists
を、crew
という名前の新しいフィールドの要素としてプロジェクションします。
var stage = new BsonDocument { { "crew", new BsonArray { "$directors", "$writers", "$makeupArtists" } } }; var pipeline = new EmptyPipelineDefinition<Movie>() .Project(stage) .Sample(1); var result = movieCollection.Aggregate(pipeline).FirstOrDefault();
パイプラインは、次のようなドキュメントを返します。
{ "_id" : { "$oid" : "573a1399f29313caabced0d9" }, "crew" : [["Bill Kroyer"], ["Jim Cox (screenplay)", "Diana Young (original stories)"], null] }
注意
ビルダ クラスは欠落フィールドを防止します
上記の例ではBsonDocument
オブジェクトを使用します。これは、ビルダーを使用して欠落しているフィールドを配列に追加しようとすると、 .NET/ C#ドライバーがコンパイル時エラーを発生させるためです。他のMongoDB言語ドライバーはこの機能をサポートしている可能性があります。詳しくは、 MongoDBドライバーのドキュメント を参照してください。