我有以下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#中使用它吗?
我尝试使用以下代码但似乎无法找到QueryDocument
和MongoCursor
的类型 - 我认为它们已被弃用?
BsonDocument document = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>("{ name : value }");
QueryDocument queryDoc = new QueryDocument(document);
MongoCursor toReturn = _connectionCollection.Find(queryDoc);
答案 0 :(得分:25)
无需解析JSON。这里的所有内容实际上都可以使用LINQ或Aggregate Fluent接口直接完成。
只是使用一些示范课程,因为这个问题并没有给你带来太大的帮助。
基本上我们这里有两个系列,是
<强>实体强>
{ "_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", "B" ] } } },
{ "$lookup" : {
"from" : "others",
"let" : { "entity" : "$_id" },
"pipeline" : [
{ "$match" : { "$expr" : { "$eq" : [ "$$entity", "$entity" ] } } }
],
"as" : "others"
} },
{ "$unwind" : "$others" },
{ "$sort" : { "others.name" : -1 } }
]
Fluent&#34; Builder&#34;不直接支持语法,LINQ表达式也不支持$expr
运算符,但您仍然可以使用BsonDocument
和BsonArray
或其他有效表达式构造。在这里我们也&#34;键入&#34; $unwind
结果是为了使用表达式而不是BsonDocument
来应用$sort
,如前所示。
除了其他用途之外,&#34;子管道的主要任务&#34;是减少目标数组$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 } }
]
这几乎完全相同,但只是使用不同的接口并产生略微不同的BSON语句,实际上只是因为功能语句中的简化命名。这确实提出了仅使用SelectMany()
生成的$unwind
的另一种可能性:
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"
而不是使用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;
上的隐含命名来解决此问题。如果不是这样的话,当你知道自己有一个&#34;一个&#34;时,这实际上比$arrayElemAt
好。相关结果。如果你想要$unwind
&#34;合并&#34;,那么你最好使用流畅的界面,或者稍后演示的不同形式。
[
{ "$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 } }
]
发送到服务器的请求:
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;
所有人都非常熟悉,而且实际上只是功能命名。就像使用$unwind
选项一样:
[
{ "$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 } }
]
发送到服务器的请求:
select
实际上使用的是"optimized coalescence"表单。翻译仍然坚持添加$project
,因为我们需要中间BsonDocument
才能使语句有效。
因此,有很多方法可以基本上达到基本相同的查询语句,结果完全相同。虽然你可以&#34;可以&#34;将JSON解析为Aggregate()
表单并将其提供给流畅的Lookup()
命令,使用自然构建器或LINQ接口通常更好,因为它们可以轻松映射到同一语句中。 / p>
$unwind
的选项主要是因为即使使用&#34;单数&#34;匹配&#34;合并&#34;表单实际上远比使用$arrayElemAt
更好地采用&#34;第一个&#34;数组元素。考虑到诸如BSON限制之类的事情,这甚至变得更加重要,其中$lookup
目标数组可能导致父文档超过16MB而无需进一步过滤。这里有另一篇帖子Aggregate $lookup Total size of documents in matching pipeline exceeds maximum document size,我实际上讨论了如何通过在此时使用这些选项或其他return
语法来使用fluent接口来避免限制。
答案 1 :(得分:0)
这是使用Lookups that span relationships- Django Doc 的方法。如果两个实体处于一对多或多对多关系中,则可以获取反向关系访问权限,而不必手动进行联接,如下所示。 [免责声明:我是图书馆的作者]
using System;
using System.Linq;
using MongoDB.Entities;
using MongoDB.Driver.Linq;
namespace StackOverflow
{
public class Program
{
public class Author : Entity
{
public string Name { get; set; }
public Many<Book> Books { get; set; }
public Author() => this.InitOneToMany(() => Books);
}
public class Book : Entity
{
public string Title { get; set; }
}
static void Main(string[] args)
{
new DB("test");
var book = new Book { Title = "The Power Of Now" };
book.Save();
var author = new Author { Name = "Eckhart Tolle" };
author.Save();
author.Books.Add(book);
//build a query for finding all books that has Power in the title.
var bookQuery = DB.Queryable<Book>()
.Where(b => b.Title.Contains("Power"));
//find all the authors of books that has a title with Power in them
var authors = author.Books
.ParentsQueryable<Author>(bookQuery); //also can pass in an ID or array of IDs
//get the result
var result = authors.ToArray();
//output the aggregation pipeline
Console.WriteLine(authors.ToString());
Console.ReadKey();
}
}
}