linq .NET MongoDb -如何将元素插入到n嵌套结构的列表中

9rnv2umw  于 2023-02-20  发布在  .NET
关注(0)|答案(1)|浏览(151)

我有以下结构-为了简单起见,我缩写了一些属性。如您所见,根文档 (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"
        }
    ]
}
    • Project schema**-这包含文件夹和其他属性。这是我的主文档。
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级。

    • 初始调用**-在此示例中,我尝试在文件夹SubFolder1中添加名为 * HEUREKA * 的新文件夹
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 *。

//**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推送到父数组?**
o7jaxewo

o7jaxewo1#

从概念上讲,您要做的是构造一个任意深度的递归$elemMatch查询,我认为在当前的数据库设计中,使用一个find/update命令是无法做到的。
看起来好像您事先知道顶层ProjectSchemaProjectId-如果是这样的话,就可以检索该文档,遍历它,并构造一个n层深的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);

尽管此时,在反序列化对象中添加新的FolderSchema并替换数据库中的文档似乎是最简单的方法,但如果不想替换整个文档,您仍然可以用与生成ElemMatch查询相同的方法来构造Update查询。
如果您能够更改模式,将多个FolderSchema对象插入到集合中,而不是插入一个任意深度的ProjectSchema文档,那么您似乎可以使用MongoDb聚合框架$graphLookup,如https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/中所述,更多示例请参见https://docs.mongodb.com/manual/tutorial/model-tree-structures/
在不了解您的情况的情况下,很难说这是一个更好的选择,还是仅仅是一个替代方案,使用MongoDB Aggregate框架的能力应该被认为是一个加分项。

相关问题