gpt4 book ai didi

c# - 用C#聚合$ lookup

转载 作者:IT老高 更新时间:2023-10-28 13:11:08 24 4
gpt4 key购买 nike

我有以下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
}
}
]
)

这将从另一个集合中检索与匹配对象结合在一起的对象列表。

有人知道我如何使用LINQ或使用此确切的字符串在C#中使用它吗?

我尝试使用以下代码,但似乎找不到 QueryDocumentMongoCursor的类型-我认为它们已被弃用?
BsonDocument document = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>("{ name : value }");
QueryDocument queryDoc = new QueryDocument(document);
MongoCursor toReturn = _connectionCollection.Find(queryDoc);

最佳答案

无需解析JSON。实际上,这里的所有操作都可以直接使用LINQ或Aggregate Fluent接口(interface)完成。

仅使用一些演示类,因为问题并不能解决很多问题。

设置

基本上我们这里有两个收藏

实体

{ "_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"
}

以及一些将它们绑定(bind)到的类,就像非常基本的示例一样:
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", "B" ] } } },
{ "$lookup" : {
"from" : "others",
"let" : { "entity" : "$_id" },
"pipeline" : [
{ "$match" : { "$expr" : { "$eq" : [ "$$entity", "$entity" ] } } }
],
"as" : "others"
} },
{ "$unwind" : "$others" },
{ "$sort" : { "others.name" : -1 } }
]

Fluent“Builder”还不直接支持语法,LINQ表达式也不支持 $expr 运算符,但是您仍然可以使用 BsonDocumentBsonArray或其他有效表达式进行构造。在这里,我们还“键入” $unwind 结果,以便使用表达式而不是前面显示的 $sort来应用 BsonDocument

除了其他用途,“子管道”的主要任务是减少在 $lookup 目标数组中返回的文档。同样,此处的 $unwind 的目的是在服务器执行时将 being "merged"实际放入 $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);

请求已发送到服务器:
[ 
{ "$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 } }
]

这几乎是相同的,但是只是使用了不同的接口(interface),并且产生了稍有不同的BSON语句,这实际上仅是因为功能性语句中的命名简化了。这确实带来了简单地使用由 $unwind生成的 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);

请求已发送到服务器:
[
{ "$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 } }
]

通常,将 $unwind 直接放在 $lookup 之后实际上是聚合框架的 "optimized pattern"。但是,.NET驱动程序通过在两者之间强制使用 $project而不是在 "as"上使用隐式命名,确实使这种组合变得困惑。如果不是那样,当您知道自己有“一个”相关结果时,它实际上比 $arrayElemAt 好。如果您想要 $unwind “coalescence”,那么最好使用流畅的界面或稍后演示的其他形式。

自然酷
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;

请求已发送到服务器:
[
{ "$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 } }
]

所有这些都很熟悉,实际上只是功能命名。就像使用 $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;

请求已发送到服务器:
[ 
{ "$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 } }
]

实际使用的是 "optimized coalescence"表单。译者仍然坚持添加 $project ,因为我们需要中间 select来使语句有效。

概括

因此,有很多种方法可以从根本上得出具有相同结果的基本相同查询语句。虽然您可以“将” JSON解析为 BsonDocument形式并将其提供给流畅的 Aggregate()命令,但通常最好使用自然生成器或LINQ接口(interface),因为它们很容易映射到同一条语句。

很大程度上显示了带有 $unwind 的选项,因为即使使用“奇异”匹配,“凝聚”形式实际上也比使用 $arrayElemAt 采取“第一个”数组元素更好。考虑到BSON Limit之类的问题,这甚至变得更加重要,在这种情况下 $lookup 目标数组可能会导致父文档超过16MB,而无需进一步过滤。在 Aggregate $lookup Total size of documents in matching pipeline exceeds maximum document size上还有另一篇文章,我实际上在讨论如何通过仅在当前仅使用这种选项或其他fluent接口(interface)可用的 Lookup()语法来避免达到该限制。

关于c# - 用C#聚合$ lookup,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50530363/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com