I have the following structure – for simplicity I abbrev. few properties.
The root document (ProjectSchema) as you can see has property rootFolder (FolderSchema) which can have n nested level. For deserialization I am using classes written in C# – ProjectSchema and FolderSchema, mentioned above.
Goal
Deserialize BsonDocument into my custom class ProjectSchema where I need to find specific folder by Id and in that folder insert new FolderSchema
Problem
Could you tell me please how to iterate, respectively find a specific document nested possibly n levels and after all, how can I insert an element into the found one.
Structure
{
"_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 - this holds folders and other attributes. That's my main document.
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());
}
}
Folder schema this holds folders
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());
}
}
My attempt
Unfortunately, this works without success, everything worked without error , but nothing has happened. I assume there is an error in that filter, because Any() targets only first level. I have no idea how to target FolderSchema nested n levels.
Initial call - in this example I am trying to add new folder within folder SubFolder1 named 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);
#Edit:
It's a good point, to replace whole document with the updated one, the simplest one – and I can admit, it works. However, my documents can be pretty big, So I would rather to update a piece rather then whole document. However, If I pick the option of partial update, I am still not able to update certain part of my document, I do not know how to put it together. So I tried the following one:
For clarification, I know ProjectId ProjectSchema - respectively for which project I want to update a Folder, as well as I know the parent FolderId FolderSchema to which I want to add a new folder.
//**Schema simplification:**
Project Schema
Folder Schema
Folder Schema
Folder Schema
[n level folder schema]
Folder Schema
[n level]
Written filter, to receive Parent folder named "NSubFolder1", or I would use Folder id instead of folder name, because I know it.
var eq = Builders<FolderSchema>.Filter.Eq(f => f.Name, "NSubFolder1");
var emN = Builders<FolderSchema>.Filter.ElemMatch(_ => _.Folders, eq);
Written filter to receive project where I want to add folder.
var eqProj = Builders<ProjectSchema>.Filter.Eq(p => p.ProjectId, "project id here");
var emP = Builders<ProjectSchema>.Filter.ElemMatch(_ => _.ProjectId, eqProj);
How these two filters combine together in order to receive specified FolderSchema under ProjectSchema and then push new FolderSchema to parent array?