db.Entity.aggregate( [ { "$match":{"Id": "12345"} },{ "$lookup": { "from": "OtherCollection","localField": "otherCollectionId","foreignField": "Id","as": "ent" } },{ "$project": { "Name": 1,"Date": 1,"OtherObject": { "$arrayElemAt": [ "$ent",0 ] } } },{ "$sort": { "OtherObject.Profile.Name": 1 } } ] )
这将检索与另一个集合中的匹配对象连接的对象列表.
有没有人知道如何使用LINQ或使用这个确切的字符串在C#中使用它?
我尝试使用以下代码,但它似乎无法找到QueryDocument和MongoCursor的类型 – 我认为它们已被弃用?
BsonDocument document = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>("{ name : value }"); QueryDocument queryDoc = new QueryDocument(document); MongoCursor toReturn = _connectionCollection.Find(queryDoc);
解决方法
只是使用一些演示类,因为这个问题并没有真正发挥作用.
建立
基本上我们这里有两个系列
实体
{ "_id" : ObjectId("5b08ceb40a8a7614c70a5710"),"name" : "A" } { "_id" : ObjectId("5b08ceb40a8a7614c70a5711"),"name" : "B" }
和别的
{ "_id" : ObjectId("5b08cef10a8a7614c70a5712"),"entity" : ObjectId("5b08ceb40a8a7614c70a5710"),"name" : "Sub-A" } { "_id" : ObjectId("5b08cefd0a8a7614c70a5713"),"entity" : ObjectId("5b08ceb40a8a7614c70a5711"),"name" : "Sub-B" }
还有几个绑定它们的类,就像非常基本的例子一样:
public class Entity { public ObjectId id; public string name { get; set; } } public class Other { public ObjectId id; public ObjectId entity { get; set; } public string name { get; set; } } public class EntityWithOthers { public ObjectId id; public string name { get; set; } public IEnumerable<Other> others; } public class EntityWithOther { public ObjectId id; public string name { get; set; } public Other others; }
流畅的界面
var listNames = new[] { "A","B" }; var query = entities.Aggregate() .Match(p => listNames.Contains(p.name)) .Lookup( foreignCollection: others,localField: e => e.id,foreignField: f => f.entity,@as: (EntityWithOthers eo) => eo.others ) .Project(p => new { p.id,p.name,other = p.others.First() } ) .Sort(new BsonDocument("other.name",-1)) .ToList();
请求发送到服务器:
[ { "$match" : { "name" : { "$in" : [ "A","B" ] } } },{ "$lookup" : { "from" : "others","localField" : "_id","foreignField" : "entity","as" : "others" } },{ "$project" : { "id" : "$_id","name" : "$name","other" : { "$arrayElemAt" : [ "$others",0 ] },"_id" : 0 } },{ "$sort" : { "other.name" : -1 } } ]
可能最容易理解,因为流畅的界面基本上与一般的BSON结构相同. $lookup
阶段具有所有相同的参数,$arrayElemAt
用First()表示.对于$sort
,您只需提供BSON文档或其他有效表达式即可.
另一种是$lookup
的新表现形式,带有MongoDB 3.6及以上版本的子管道声明.
BsonArray subpipeline = new BsonArray(); subpipeline.Add( new BsonDocument("$match",new BsonDocument( "$expr",new BsonDocument( "$eq",new BsonArray { "$$entity","$entity" } ) )) ); var lookup = new BsonDocument("$lookup",new BsonDocument("from","others") .Add("let",new BsonDocument("entity","$_id")) .Add("pipeline",subpipeline) .Add("as","others") ); var query = entities.Aggregate() .Match(p => listNames.Contains(p.name)) .AppendStage<EntityWithOthers>(lookup) .Unwind<EntityWithOthers,EntityWithOther>(p => p.others) .SortByDescending(p => p.others.name) .ToList();
请求发送到服务器:
[ { "$match" : { "name" : { "$in" : [ "A",{ "$lookup" : { "from" : "others","let" : { "entity" : "$_id" },"pipeline" : [ { "$match" : { "$expr" : { "$eq" : [ "$$entity","$entity" ] } } } ],{ "$unwind" : "$others" },{ "$sort" : { "others.name" : -1 } } ]
Fluent“Builder”不直接支持语法,LINQ表达式也不支持$expr
运算符,但您仍然可以使用BsonDocument和BsonArray或其他有效表达式构造.在这里,我们还“键入”$unwind
结果,以便使用表达式而不是BsonDocument应用$sort
,如前所示.
除了其他用途之外,“子管道”的主要任务是减少目标数组$lookup
中返回的文档.此外,$unwind
在服务器执行时实际上是being “merged”用于$lookup
语句的目的,因此这通常是比只抓取结果数组的第一个元素更有效.
可查询的GroupJoin
var query = entities.AsQueryable() .Where(p => listNames.Contains(p.name)) .GroupJoin( others.AsQueryable(),p => p.id,o => o.entity,(p,o) => new { p.id,other = o.First() } ) .OrderByDescending(p => p.other.name);
请求发送到服务器:
[ { "$match" : { "name" : { "$in" : [ "A","as" : "o" } },{ "$project" : { "id" : "$_id","other" : { "$arrayElemAt" : [ "$o",{ "$sort" : { "other.name" : -1 } } ]
这几乎完全相同,但只是使用不同的接口并产生略微不同的BSON语句,实际上只是因为功能语句中的简化命名.这确实提出了使用从SelectMany()生成的$unwind
的另一种可能性:
var query = entities.AsQueryable() .Where(p => listNames.Contains(p.name)) .GroupJoin( others.AsQueryable(),other = o } ) .SelectMany(p => p.other,other) => new { p.id,other }) .OrderByDescending(p => p.other.name);
请求发送到服务器:
[ { "$match" : { "name" : { "$in" : [ "A","as" : "o" }},"other" : "$o",{ "$unwind" : "$other" },{ "$project" : { "id" : "$id","other" : "$other","_id" : 0 }},{ "$sort" : { "other.name" : -1 } } ]
通常在$lookup
之后直接放置$unwind
实际上是聚合框架的“optimized pattern”.然而,.NET驱动程序通过在两者之间强制使用$项目而不是使用“as”上的隐含命名来解决这个问题.如果不是这样,当你知道你有“一个”相关结果时,这实际上比$arrayElemAt
好.如果你想要$unwind
“合并”,那么你最好使用流畅的界面,或者稍后演示的不同形式.
自然的
var query = from p in entities.AsQueryable() where listNames.Contains(p.name) join o in others.AsQueryable() on p.id equals o.entity into joined select new { p.id,other = joined.First() } into p orderby p.other.name descending select p;
请求发送到服务器:
[ { "$match" : { "name" : { "$in" : [ "A","as" : "joined" } },"other" : { "$arrayElemAt" : [ "$joined",{ "$sort" : { "other.name" : -1 } } ]
所有这些都非常熟悉,而且实际上只是功能命名.就像使用$unwind
选项一样:
var query = from p in entities.AsQueryable() where listNames.Contains(p.name) join o in others.AsQueryable() on p.id equals o.entity into joined from sub_o in joined.DefaultIfEmpty() select new { p.id,other = sub_o } into p orderby p.other.name descending select p;
请求发送到服务器:
[ { "$match" : { "name" : { "$in" : [ "A",{ "$unwind" : { "path" : "$joined","preserveNullAndEmptyArrays" : true } },"other" : "$joined",{ "$sort" : { "other.name" : -1 } } ]
实际上使用的是“optimized coalescence”表格.翻译仍然坚持添加$project
,因为我们需要中间选择以使语句有效.
因此,有很多方法可以基本上达到基本相同的查询语句,结果完全相同.虽然您“可以”将JSON解析为BsonDocument表单并将其提供给流畅的Aggregate()命令,但通常最好使用自然构建器或LINQ接口,因为它们可以轻松映射到同一语句.
显示$unwind
的选项很大程度上是因为即使使用“单一”匹配,“合并”形式实际上远比使用$arrayElemAt
采用“第一”数组元素更加优化.考虑到诸如BSON限制之类的事情,这甚至变得更加重要,其中$lookup
目标阵列可能导致父文档超过16MB而无需进一步过滤.这里有另一篇文章在Aggregate $lookup Total size of documents in matching pipeline exceeds maximum document size,我实际上讨论了如何通过此时仅使用这些选项或其他可用于Fluent接口的Lookup()语法来避免限制.