如何通过C#驱动程序查询MongoDB的嵌入式文档?

时间:2013-01-26 15:20:42

标签: c# linq mongodb

我想知道如何通过official C# driver版本1.7查询MongoDB集合中的嵌入式文档(在数组中)?通过查询嵌入式文档,我的意思是我只想检索嵌入的文档,而不是包含它的文档,AKA projection

带有嵌入式文档的数据模型的示例,我正在查询:

// Library, contained in db.Libraries
{
    _id: 1,
    Categories: [{_id: 2, Name: "Classics", Books: [{_id: 3, Name: The Count of Monte Cristo}]}]
}

这里的问题是如何在库集合中查询_id 1及其中一本_id 3本书的对象,并仅返回本书。它可行吗?据我所知,可以通过MongoDB shell中的投影来实现这一点。

我的示例查询将查询db.Libraries以查找包含在_id的库中的_id 3的图书.1。返回的图书将是此子文档:

{_id: 3, Name: The Count of Monte Cristo}]}

我看过问题How to retrieve an embedded document using the official C# driver for MongoDB?,但我不能让接受的答案对我有用。

修改

我现在可以看到How to retrieve an embedded document using the official C# driver for MongoDB?的已接受答案有效,有点。我没有看到它迭代每个找到的文档,相当于:

var libraryCollection = new MongoCollection<Library>();
var refBook = new Book { Id = ObjectId.GenerateNewId().ToString(), Name = "The Count of Monte Cristo" };
libraryCollection.Insert(new Library { Id = ObjectId.GenerateNewId().ToString(), Categories = new[] { new Category { Books = new[] { refBook } } } });

MongoCursor<Library> libraries = libraryCollection.Find(new QueryDocument(
                    new Dictionary<string, object>
                        {
                            {"_id", new ObjectId()},
                        }
                    ));
Book book;
foreach (var library in libraries)
{
    book = library.Categories.SelectMany(c => c.Books).FirstOrDefault(b => b.Id == refBook.Id);
}

但是,这个解决方案没有实际意义,因为它检索整个Library文档而不仅仅是嵌入的Book文档。我真的只需要反编译嵌入式书籍,AKA projection

1 个答案:

答案 0 :(得分:3)

要执行投影,结果文档不仅仅是已过滤,而是重新整形,您需要使用Aggregate而不是Find方法之一,如下所示:

var result = collection.Aggregate(
    // Only include the document where _id = 1
    new BsonDocument {{"$match", new BsonDocument {{"_id", 1}}}},
    // 'Unwind' the Categories array by duplicating the docs, one per element.
    new BsonDocument {{"$unwind", "$Categories"}},
    // Now do the same for the Books array inside each Categories element.
    new BsonDocument {{"$unwind", "$Categories.Books"}},
    // Only include the resulting docs with a Book _id of 3
    new BsonDocument {{"$match", new BsonDocument {{"Categories.Books._id", 3}}}},
    // Reshape the document to bring the book attributes out to the top level 
    new BsonDocument {{"$project", new BsonDocument {
        {"_id", "$Categories.Books._id"},
        {"Name", "$Categories.Books.Name"}
    }}}
);

result.Response.toJson()

{"result": [{ "_id": 3.0, "Name": "The Count of Monte Cristo" }], "ok": 1.0 }