MongoDB / C#如何仅查询深度嵌套的数组?

时间:2020-09-23 15:52:02

标签: c# json mongodb

我因无法(无论是单独还是通过SO)无法弄清楚如何直接在#+OPTIONS: num:nil toc:nil html-postamble:nil ^:nil #+HTML_HEAD: <style type="text/css"> #+HTML_HEAD: .styledtable { width: 400px;} #+HTML_HEAD: .styledtable col:nth-of-type(1) { width: 25%; background: yellow; } #+HTML_HEAD: .styledtable col:nth-of-type(2) { width: 50%; background: magenta; } #+HTML_HEAD: .styledtable col:nth-of-type(3) { width: 25%; background: cyan; } #+HTML_HEAD: </style> #+ATTR_HTML: :class styledtable | column1 | column2 | column3 | |---------+-------------+---------------------| | small | medium text | very very long text | 或自定义类中提取深层嵌套的数据而感到厌烦。我知道我应该确定使用过滤器和投影来找出嵌套在另一个数组中的BsonDocument数组/列表。 以下是结构(简化):

Guid

我同时使用//Thread { Id: "B2", Answers: [ { Id: "A1", Likes: [ "GUID1", "GUID2", "ETC" ] //<- this array, and only this. } ] } Thread.Id作为过滤数据,但随后尝试使用:

Answer.Id

但是此查询始终返回null,这是我从POV做错了什么?

2 个答案:

答案 0 :(得分:2)

我无法通过单个过滤和投影来获取点赞。但是,我可以通过使用聚合管道来实现它。

private async Task<BsonArray> GetLikes(string docId, string answerId)
{
    var client = new MongoClient();
    var idFilter = Builders<BsonDocument>.Filter.Eq("ID", docId);
    var answerIdFilter = Builders<BsonDocument>.Filter.Eq("Answers.ID", answerId);
    var projection = Builders<BsonDocument>.Projection.Exclude("_id").Include("Answers.Likes");
    var likes = await client.GetDatabase("test").GetCollection<BsonDocument>("items")
        .Aggregate(new AggregateOptions())
        .Match(idFilter)
        .Unwind("Answers")
        .Match(answerIdFilter)
        .Project(projection)
        .FirstOrDefaultAsync();

    return likes == null ? null
        : (likes.GetElement("Answers").Value as BsonDocument).GetElement("Likes").Value as BsonArray;
}

由于某种原因,结果包含原始结构的文档,而不是仅包含具有Likes属性的文档,因此我之后必须进行一些后期处理。

答案 1 :(得分:2)

使用常规查询无法在投影中投影数组中的各个字段。

您最多可以使用常规查询来投影匹配元素,然后映射喜欢的对象。

类似

var f = Builders<BsonDocument>.Filter;
var filter = f.And(f.Eq("Id", ids.ThreadId), f.Eq("Answers.Id", ids.AnswerId));
var projection = Builders<BsonDocument>.Projection.Include("Answers.$");
var answer = await dbClient.GetCollection<BsonDocument>(nameof(Thread))
    .Find(filter)
    .Project(projection)
    .FirstOrDefaultAsync();

或者,您可以将过滤器与具有聚合功能的map结合使用,以通过id匹配答案元素,然后通过投影来匹配like字段。

类似

var f = Builders<BsonDocument>.Filter;
var match = f.And(f.Eq("Id", ids.ThreadId), f.Eq("Answers.Id", ids.AnswerId));

var project = new BsonDocument("newRoot", 
            new BsonDocument("$arrayElemAt", new BsonArray {
                new BsonDocument("$map", 
                    new BsonDocument
                        { 
                            { "input", 
                                new BsonDocument("$filter", new BsonDocument
                                    {
                                        { "input", "$Answers"},
                                        {"cond", new BsonDocument("$eq", new BsonArray { "$$this.Id", ids.AnswerId})}
                                    })
                            },
                            { "in", new BsonDocument("Likes", "$$this.UserLikes") }
                        }), 
                0}));    

var pipeline = collection.Aggregate()
.Match(match)
.AppendStage<BsonDocument, BsonDocument, BsonDocument>(new BsonDocument("$replaceRoot", project));

var list = pipeline.ToList();

此处的工作示例-https://mongoplayground.net/p/wM1z6q92_mV