如何使用 C# 从多级嵌入式 MongoDB 文档中获取具有相应父元素的确切子元素
How to get the exact child element with corresponding parent only from multilevel embedded MongoDB document using C#
尝试使用 C# 从嵌入式 MongoDB 文档中获取确切的子文档及其对应的父文档,但查询 return 所有子文档和对应的父文档以及其他文档。如何将参数设置为
通过 Filter 和 findOptions 获得精确匹配。我的预期结果是 Channel 1 -> Episode 1 & Child Track which id is given 。
这是我的代码:
// Class
namespace CrudWithMultilvelNestedDoc
{
public class Channel
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Episode[] Episodes { get; set; }
}
public class Episode
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Track[] Tracks { get; set; }
}
public class Track
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Like[] Likes { get; set; }
}
public class Like
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
}
}
//Code
var chanelId = "5e4606e6ae7b090688671416";
var episodeId = "5e46071d385a672b0cea0f86";
var trackId = "5e460dbe2bc5e70c9cfeac21";
var filter = Builders<Channel>.Filter.And(
Builders<Channel>.Filter.Where(x => x.Id == chanelId),
Builders<Channel>.Filter.Eq("Episodes._id", episodeId),
Builders<Channel>.Filter.Eq("Episodes.Tracks._id", trackId));
var result = collection.Find(filter).ToList();
// Data
{"_id":"5e4606e6ae7b090688671416","Name":"Channel 1","Episodes":[{"_id":"5e46071d385a672b0cea0f86","Name":"Episode 1","Tracks":[{"_id":"5e460dbe2bc5e70c9cfeac21","Name":"Trak 1","Likes":[{"_id":"5e4635d41f949d43a8194790","Name":"Like 1"},{"_id":"5e464e1ccfa17a25181234f6","Name":"Like 2"},{"_id":"5e464e94776dc808383326d5","Name":"Like 3"}]},{"_id":"5e4612d60747a2121870c815","Name":"Trak 2","Likes":[]}]},{"_id":"5e460851d29c1b3df4d27b7d","Name":"Episode 2","Tracks":[{"_id":"5e460e307ca6843758ce814e","Name":"Trak 1","Likes":[{"_id":"5e4686aa2813d41fa428b621","Name":"Like 1"}]}]}]}
由于您的文档是多级的,因此您必须使用 $filter 运算符。在 C# 中,使用 LINQ 进行此类查询很容易,因为它会在幕后自动生成 $filter
并且您保留了强类型查询:
var q = from channel in mongoDBCollection.AsQueryable()
from episode in channel.Episodes
select new Episode()
{
Id = episode.Id,
Name = episode.Name,
Tracks = episode.Tracks.Where(x => x.Id == trackId)
};
var query = q.Where(x => x.Tracks.Any());
var result = query.ToList();
由 MongoDB .NET 驱动程序生成的聚合管道如下所示:
{
"aggregate" : "col",
"pipeline" : [
{ "$unwind" : "$Episodes" },
{
"$project" : {
"Id" : "$Episodes._id",
"Name" : "$Episodes.Name",
"Tracks" : {
"$filter" : {
"input" : "$Episodes.Tracks",
"as" : "x",
"cond" : { "$eq" : ["$$x._id", "5e460dbe2bc5e70c9cfeac21"] }
}
},
"_id" : 0
}
},
{
"$match" : {
"Tracks" : {
"$ne" : null,
"$not" : { "$size" : 0 }
}
}
}],
"cursor" : { },
}
编辑:
要提高上述查询的性能,您可以考虑通过添加 where
语句在 channel
级别进行过滤:
from channel in mongoDBCollection.AsQueryable()
where channel.Id == channelId
编辑:当你想要 return Channel
时,它会变得有点棘手。我的建议是 return 一种包含 Channel
和 Episode
的匿名类型,并将它们组合在内存中,因为它将允许您 运行 在数据库端进行大部分过滤并获得最佳性能:
var q = from channel in mongoDBCollection.AsQueryable()
where channel.Id == "5e4606e6ae7b090688671416"
from episode in channel.Episodes
select new
{
Channel = new Channel(){ Id = channel.Id, Name = channel.Name},
Episode = new Episode()
{
Id = episode.Id,
Name = episode.Name,
Tracks = episode.Tracks.Where(x => x.Id == trackId)
}
};
var query = q.Where(x => x.Episode.Tracks.Any());
var result = query.ToList();
这里是 mickl 答案的 lambda 版本,还保留了频道信息:
var result = collection.AsQueryable()
.Where(c => c.Id == "5e4606e6ae7b090688671416")
.SelectMany(c => c.Episodes,
(c, e) => new
{
cid = c.Id,
cname = c.Name,
eid = e.Id,
ename = e.Name,
etracks = (Track[])e.Tracks.Where(t => t.Id == "5e460dbe2bc5e70c9cfeac21")
})
.Where(x => x.etracks.Any())
.Select(x => new Channel
{
ID = x.cid,
Name = x.cname,
Episodes = new Episode[] {
new Episode {
Id = x.eid,
Name = x.ename,
Tracks = x.etracks } }
})
.ToList();
尝试使用 C# 从嵌入式 MongoDB 文档中获取确切的子文档及其对应的父文档,但查询 return 所有子文档和对应的父文档以及其他文档。如何将参数设置为 通过 Filter 和 findOptions 获得精确匹配。我的预期结果是 Channel 1 -> Episode 1 & Child Track which id is given 。 这是我的代码:
// Class
namespace CrudWithMultilvelNestedDoc
{
public class Channel
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Episode[] Episodes { get; set; }
}
public class Episode
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Track[] Tracks { get; set; }
}
public class Track
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Like[] Likes { get; set; }
}
public class Like
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
}
}
//Code
var chanelId = "5e4606e6ae7b090688671416";
var episodeId = "5e46071d385a672b0cea0f86";
var trackId = "5e460dbe2bc5e70c9cfeac21";
var filter = Builders<Channel>.Filter.And(
Builders<Channel>.Filter.Where(x => x.Id == chanelId),
Builders<Channel>.Filter.Eq("Episodes._id", episodeId),
Builders<Channel>.Filter.Eq("Episodes.Tracks._id", trackId));
var result = collection.Find(filter).ToList();
// Data
{"_id":"5e4606e6ae7b090688671416","Name":"Channel 1","Episodes":[{"_id":"5e46071d385a672b0cea0f86","Name":"Episode 1","Tracks":[{"_id":"5e460dbe2bc5e70c9cfeac21","Name":"Trak 1","Likes":[{"_id":"5e4635d41f949d43a8194790","Name":"Like 1"},{"_id":"5e464e1ccfa17a25181234f6","Name":"Like 2"},{"_id":"5e464e94776dc808383326d5","Name":"Like 3"}]},{"_id":"5e4612d60747a2121870c815","Name":"Trak 2","Likes":[]}]},{"_id":"5e460851d29c1b3df4d27b7d","Name":"Episode 2","Tracks":[{"_id":"5e460e307ca6843758ce814e","Name":"Trak 1","Likes":[{"_id":"5e4686aa2813d41fa428b621","Name":"Like 1"}]}]}]}
由于您的文档是多级的,因此您必须使用 $filter 运算符。在 C# 中,使用 LINQ 进行此类查询很容易,因为它会在幕后自动生成 $filter
并且您保留了强类型查询:
var q = from channel in mongoDBCollection.AsQueryable()
from episode in channel.Episodes
select new Episode()
{
Id = episode.Id,
Name = episode.Name,
Tracks = episode.Tracks.Where(x => x.Id == trackId)
};
var query = q.Where(x => x.Tracks.Any());
var result = query.ToList();
由 MongoDB .NET 驱动程序生成的聚合管道如下所示:
{
"aggregate" : "col",
"pipeline" : [
{ "$unwind" : "$Episodes" },
{
"$project" : {
"Id" : "$Episodes._id",
"Name" : "$Episodes.Name",
"Tracks" : {
"$filter" : {
"input" : "$Episodes.Tracks",
"as" : "x",
"cond" : { "$eq" : ["$$x._id", "5e460dbe2bc5e70c9cfeac21"] }
}
},
"_id" : 0
}
},
{
"$match" : {
"Tracks" : {
"$ne" : null,
"$not" : { "$size" : 0 }
}
}
}],
"cursor" : { },
}
编辑:
要提高上述查询的性能,您可以考虑通过添加 where
语句在 channel
级别进行过滤:
from channel in mongoDBCollection.AsQueryable()
where channel.Id == channelId
编辑:当你想要 return Channel
时,它会变得有点棘手。我的建议是 return 一种包含 Channel
和 Episode
的匿名类型,并将它们组合在内存中,因为它将允许您 运行 在数据库端进行大部分过滤并获得最佳性能:
var q = from channel in mongoDBCollection.AsQueryable()
where channel.Id == "5e4606e6ae7b090688671416"
from episode in channel.Episodes
select new
{
Channel = new Channel(){ Id = channel.Id, Name = channel.Name},
Episode = new Episode()
{
Id = episode.Id,
Name = episode.Name,
Tracks = episode.Tracks.Where(x => x.Id == trackId)
}
};
var query = q.Where(x => x.Episode.Tracks.Any());
var result = query.ToList();
这里是 mickl 答案的 lambda 版本,还保留了频道信息:
var result = collection.AsQueryable()
.Where(c => c.Id == "5e4606e6ae7b090688671416")
.SelectMany(c => c.Episodes,
(c, e) => new
{
cid = c.Id,
cname = c.Name,
eid = e.Id,
ename = e.Name,
etracks = (Track[])e.Tracks.Where(t => t.Id == "5e460dbe2bc5e70c9cfeac21")
})
.Where(x => x.etracks.Any())
.Select(x => new Channel
{
ID = x.cid,
Name = x.cname,
Episodes = new Episode[] {
new Episode {
Id = x.eid,
Name = x.ename,
Tracks = x.etracks } }
})
.ToList();