.NET MongoDb - 如何在n-嵌套结构中插入要列出的元素

时间:2018-03-29 23:35:58

标签: c# mongodb linq mongodb-query

我有以下结构 - 为简单起见我是缩写。几个属性。 您可以看到根文档(ProjectSchema)具有属性rootFolder (FolderSchema),它可以具有 n嵌套级别。对于反序列化,我使用的是用C#编写的类 - ProjectSchemaFolderSchema,如上所述。

目标

将BsonDocument反序列化到我的自定义类ProjectSchema,我需要在Id 中找到特定的文件夹,并在该文件夹中插入新的FolderSchema

问题

你能告诉我如何迭代,分别找到一个嵌套可能n级的特定文件,毕竟,如何在找到的元素中插入一个元素。


结构

{
    "_id" : ObjectId("5abce10cb02728006f1460fd"),
    "owner_ref" : ObjectId("5ababb7188f6ba0079199dd0"),
    "description" : "",
    "rootFolder" : [ 
        {
            "_id" : ObjectId("5abce10cb02728006f1460fc"),
            "folders" : [ 
                {
                    "_id" : ObjectId("5abce9b5b02728006f1460ff"),
                    "folders" : [ 
                        {
                            "_id" : ObjectId("5abd5775b02728006f146130"),
                            "folders" : [ 
                                {
                                    "_id" : ObjectId("5abd5781b02728006f146131"),
                                    "folders" : [ 
                                        {
                                            "_id" : ObjectId("5abd578ab02728006f146132"),
                                            "folders" : [],
                                            "fileRefs" : [],
                                            "docs" : [],
                                            "name" : "NSubFolder1"
                                        }
                                    ],
                                    "fileRefs" : [],
                                    "docs" : [],
                                    "name" : "SubSubFolder1"
                                }
                            ],
                            "fileRefs" : [],
                            "docs" : [],
                            "name" : "SubFolder1"
                        }
                    ],
                    "fileRefs" : [],
                    "docs" : [],
                    "name" : "Folder1"
                }, 
                {
                    "_id" : ObjectId("5abd576db02728006f14612f"),
                    "folders" : [],
                    "fileRefs" : [],
                    "docs" : [],
                    "name" : "Folder2"
                }
            ],
            "fileRefs" : [],
            "docs" : [ 
                {
                    "_id" : ObjectId("5abce10cb02728006f1460fe"),
                    "name" : "main.tex"
                }
            ],
            "name" : "rootFolder"
        }
    ]
}

项目架构 - 这包含文件夹和其他属性。这是我的主要文件。

 public class ProjectSchema
    {
        [BsonId]
        public BsonObjectId ProjectId { get; set; }

        [BsonElement("description")]
        public string Description { get; set; } = "some desc";

        [BsonElement("owner_ref")]
        public BsonObjectId OwnerRef { get; set; }

        [BsonElement("rootFolder")]
        public List<FolderSchema> RootFolder { get; set; } = new List<FolderSchema>();

        public ProjectSchema()
        {
            ProjectId = new BsonObjectId(ObjectId.GenerateNewId());
        }
    }

文件夹架构,它包含文件夹

 public class FolderSchema
    {
        [BsonId]
        public BsonObjectId FolderId { get; set; }

        [BsonElement("name")]
        public string Name { get; set; } = "new folder";

        [BsonElement("docs")]
        public IEnumerable<DocSchema> Docs { get; set; } = new List<DocSchema>();

        [BsonElement("fileRefs")]
        public IEnumerable<FileSchema> FileRefs { get; set; } = new List<FileSchema>();

        [BsonElement("folders")]
        public IEnumerable<FolderSchema> Folders { get; set; } = new List<FolderSchema>();

        public FolderSchema()
        {
            FolderId = new BsonObjectId(ObjectId.GenerateNewId());
        }
    }

我的尝试

不幸的是,这种方法没有成功,一切正常,但没有发生任何事情。我假设该过滤器中存在错误,因为Any()仅针对第一级。我不知道如何定位FolderSchema嵌套的n级。

初始调用 - 在此示例中,我尝试在名为 HEUREKA 的文件夹 SubFolder1 中添加新文件夹

var projects = database.GetCollection<ProjectSchema>("projects");
var folder = new FolderSchema() { Name = "HEUREKA" };

var filter = Builders<ProjectSchema>.Filter.Where(p => p.ProjectId == new BsonObjectId(new ObjectId("5abce10cb02728006f1460fd"))
                && p.RootFolder.Any(l => l.FolderId == new BsonObjectId(new ObjectId("5abd5775b02728006f146130"))));
var update = Builders<ProjectSchema>.Update.Push(p => p.RootFolder, folder);

await projects.FindOneAndUpdateAsync(filter, update);

<小时/>

编辑:

这是一个很好的观点,用最新的文件替换整个文件,最简单的 - 我可以承认,它的工作原理。然而,我的文件可能很大,所以我宁愿更新一块而不是整个文档。但是,如果我选择部分更新的选项,我仍然无法更新我的文档的某些部分,我不知道如何将它放在一起。所以我尝试了以下一个:

为了澄清,我知道ProjectId ProjectSchema - 分别对于我要更新文件夹的项目,以及我知道父FolderId FolderSchema < / em>我要添加一个新文件夹。

//**Schema simplification:**

Project Schema
    Folder Schema
       Folder Schema
           Folder Schema
           [n level folder schema]
       Folder Schema
       [n level]

写过滤器,接收名为“NSubFolder1”的父文件夹,或者我会使用文件夹ID而不是文件夹名称,因为我知道。

var eq = Builders<FolderSchema>.Filter.Eq(f => f.Name, "NSubFolder1");
var emN = Builders<FolderSchema>.Filter.ElemMatch(_ => _.Folders, eq);

写入过滤器以接收我想要添加文件夹的项目。

var eqProj = Builders<ProjectSchema>.Filter.Eq(p => p.ProjectId, "project id here");
var emP = Builders<ProjectSchema>.Filter.ElemMatch(_ => _.ProjectId, eqProj);

如何将这两个过滤器组合在一起,以便在ProjectSchema下接收指定的FolderSchema,然后将新的FolderSchema推送到父阵列?

1 个答案:

答案 0 :(得分:2)

从概念上讲,您要做的是构建一个任意深度的递归$elemMatch查询。我不认为使用您当前的数据库设计使用单个find / update命令可以完成此操作。

好像您事先知道了顶级ProjectId的{​​{1}} - 如果是这种情况,则可以检索该文档,遍历它并构造一个n-等级深ProjectSchema,将会这样描述(尽管写得不同)

FilterDefinition

虽然在这一点上,在反序列化对象中添加新的var eq = Builders<FolderSchema>.Filter.Eq(f => f.Name, "HEUREKA"); var emN = Builders<FolderSchema>.Filter.ElemMatch(_ => _.Folders, eq); ... var em2 = Builders<FolderSchema>.Filter.ElemMatch(_ => _.RootFolder, em3); var em1 = Builders<FolderSchema>.Filter.ElemMatch(_ => _.RootFolder, em2); var emRoot = Builders<ProjectSchema>.Filter.ElemMatch(_ => _.RootFolder, em1); 并替换数据库中的文档似乎是最简单的。不过,如果您不想替换整个文档,您可以使用与生成ElemMatch查询相同的方式构建更新查询。

如果您能够更改模式,以便在集合中插入多个FolderSchema对象而不是一个任意深度FolderSchema文档,那么您似乎可以使用MongoDb Aggregate框架{{1如https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/所述,https://docs.mongodb.com/manual/tutorial/model-tree-structures/

有更多示例

如果不了解您的情况,很难说这是一个更好的选择,还是仅仅是另一种选择。使用MongoDB Aggregate框架的能力绝对应该被认为是一个优势。