Tom*_*eck 12 c# mongodb aggregation-framework mongodb-.net-driver
我有以下MongoDb查询工作:
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
}
}
]
)
Run Code Online (Sandbox Code Playgroud)
这将检索与另一个集合中的匹配对象连接的对象列表.
有没有人知道如何使用LINQ或使用这个确切的字符串在C#中使用它?
我尝试使用以下代码,但它似乎无法找到类型QueryDocument和MongoCursor- 我认为它们已被弃用?
BsonDocument document = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>("{ name : value }");
QueryDocument queryDoc = new QueryDocument(document);
MongoCursor toReturn = _connectionCollection.Find(queryDoc);
Run Code Online (Sandbox Code Playgroud)
Nei*_*unn 33
无需解析JSON.这里的所有内容实际上都可以直接使用LINQ或Aggregate Fluent接口完成.
只是使用一些演示类,因为这个问题并没有真正发挥作用.
基本上我们这里有两个系列
实体
{ "_id" : ObjectId("5b08ceb40a8a7614c70a5710"), "name" : "A" }
{ "_id" : ObjectId("5b08ceb40a8a7614c70a5711"), "name" : "B" }
Run Code Online (Sandbox Code Playgroud)
和其他人
{
"_id" : ObjectId("5b08cef10a8a7614c70a5712"),
"entity" : ObjectId("5b08ceb40a8a7614c70a5710"),
"name" : "Sub-A"
}
{
"_id" : ObjectId("5b08cefd0a8a7614c70a5713"),
"entity" : ObjectId("5b08ceb40a8a7614c70a5711"),
"name" : "Sub-B"
}
Run Code Online (Sandbox Code Playgroud)
还有几个绑定它们的类,就像非常基本的例子一样:
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;
}
Run Code Online (Sandbox Code Playgroud)
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();
Run Code Online (Sandbox Code Playgroud)
请求发送到服务器:
[
{ "$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 } }
]
Run Code Online (Sandbox Code Playgroud)
可能最容易理解,因为流畅的界面基本上与一般的BSON结构相同.该$lookup阶段具有所有相同的参数,并$arrayElemAt用First().表示.对于$sort您只需提供一个BSON文件或其他有效的表达式.
另一种是使用$lookupMongoDB 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();
Run Code Online (Sandbox Code Playgroud)
请求发送到服务器:
[
{ "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
{ "$lookup" : {
"from" : "others",
"let" : { "entity" : "$_id" },
"pipeline" : [
{ "$match" : { "$expr" : { "$eq" : [ "$$entity", "$entity" ] } } }
],
"as" : "others"
} },
{ "$unwind" : "$others" },
{ "$sort" : { "others.name" : -1 } }
]
Run Code Online (Sandbox Code Playgroud)
Fluent"Builder"不直接支持语法,LINQ表达式也不支持$expr运算符,但您仍然可以构造使用BsonDocument和/ BsonArray或其他有效表达式.在这里,我们还"键入" $unwind结果,以便$sort使用表达式而不是BsonDocument前面所示的表达式.
除了其他用途之外,"子管道"的主要任务是减少目标数组中返回的文档$lookup.此外,$unwind这里的目的是实际"合并"到$lookup服务器执行的语句中,因此这通常比仅仅获取结果数组的第一个元素更有效.
var query = entities.AsQueryable()
.Where(p => listNames.Contains(p.name))
.GroupJoin(
others.AsQueryable(),
p => p.id,
o => o.entity,
(p, o) => new { p.id, p.name, other = o.First() }
)
.OrderByDescending(p => p.other.name);
Run Code Online (Sandbox Code Playgroud)
请求发送到服务器:
[
{ "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
{ "$lookup" : {
"from" : "others",
"localField" : "_id",
"foreignField" : "entity",
"as" : "o"
} },
{ "$project" : {
"id" : "$_id",
"name" : "$name",
"other" : { "$arrayElemAt" : [ "$o", 0 ] },
"_id" : 0
} },
{ "$sort" : { "other.name" : -1 } }
]
Run Code Online (Sandbox Code Playgroud)
这几乎完全相同,但只是使用不同的接口并产生略微不同的BSON语句,实际上只是因为功能语句中的简化命名.这确实提出了另一种简单地使用$unwindas产生的可能性SelectMany():
var query = entities.AsQueryable()
.Where(p => listNames.Contains(p.name))
.GroupJoin(
others.AsQueryable(),
p => p.id,
o => o.entity,
(p, o) => new { p.id, p.name, other = o }
)
.SelectMany(p => p.other, (p, other) => new { p.id, p.name, other })
.OrderByDescending(p => p.other.name);
Run Code Online (Sandbox Code Playgroud)
请求发送到服务器:
[
{ "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
{ "$lookup" : {
"from" : "others",
"localField" : "_id",
"foreignField" : "entity",
"as" : "o"
}},
{ "$project" : {
"id" : "$_id",
"name" : "$name",
"other" : "$o",
"_id" : 0
} },
{ "$unwind" : "$other" },
{ "$project" : {
"id" : "$id",
"name" : "$name",
"other" : "$other",
"_id" : 0
}},
{ "$sort" : { "other.name" : -1 } }
]
Run Code Online (Sandbox Code Playgroud)
通常,$unwind直接跟随$lookup实际上是聚合框架的"优化模式".然而,.NET驱动程序通过强制$project介于两者之间而不是使用隐含的命名来解决这个问题"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, p.name, other = joined.First() }
into p
orderby p.other.name descending
select p;
Run Code Online (Sandbox Code Playgroud)
请求发送到服务器:
[
{ "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
{ "$lookup" : {
"from" : "others",
"localField" : "_id",
"foreignField" : "entity",
"as" : "joined"
} },
{ "$project" : {
"id" : "$_id",
"name" : "$name",
"other" : { "$arrayElemAt" : [ "$joined", 0 ] },
"_id" : 0
} },
{ "$sort" : { "other.name" : -1 } }
]
Run Code Online (Sandbox Code Playgroud)
所有这些都非常熟悉,而且实际上只是功能命名.就像使用$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, p.name, other = sub_o }
into p
orderby p.other.name descending
select p;
Run Code Online (Sandbox Code Playgroud)
请求发送到服务器:
[
{ "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
{ "$lookup" : {
"from" : "others",
"localField" : "_id",
"foreignField" : "entity",
"as" : "joined"
} },
{ "$unwind" : {
"path" : "$joined", "preserveNullAndEmptyArrays" : true
} },
{ "$project" : {
"id" : "$_id",
"name" : "$name",
"other" : "$joined",
"_id" : 0
} },
{ "$sort" : { "other.name" : -1 } }
]
Run Code Online (Sandbox Code Playgroud)
实际上是使用"优化合并"形式.翻译仍然坚持添加一个$project因为我们需要中间体select以使语句有效.
因此,有很多方法可以基本上达到基本相同的查询语句,结果完全相同.虽然您"可以"解析JSON以BsonDocument形成并将其提供给fluent Aggregate()命令,但通常最好使用自然构建器或LINQ接口,因为它们可以轻松映射到同一语句.
显示的选项$unwind很大程度上是因为即使使用"单一"匹配,"合并"形式实际上远比使用$arrayElemAt"第一"数组元素更优化.考虑到诸如BSON限制之类的事情,这甚至变得更加重要,其中$lookup目标数组可能导致父文档超过16MB而无需进一步过滤.这里有关于Aggregate $ lookup的另一篇文章.匹配管道中文档的总大小超过了最大文档大小,我实际上讨论了如何通过此时仅使用此类选项或其他Lookup()可用于语言界面的语法来避免限制.