Search code examples
c#mongodbmongodb-querymongodb-.net-driver

Using MongoDB C# driver find and update a node from parent children hierarchy


I have a hierarchical category document, like parent - Children - Children and so on....

{
    id: 1,
    value: {

    }Children: [{
        id: 2,
        value: {

        }Children: [{
            id: 3,
            value: {

            }Children: [{
                id: 4,
                value: {

                }Children: [{
                    id: 5,
                    value: {

                    }Children: [{
                        id: 6,
                        value: {

                        }Children: [{
                            id: 7,
                            value: {

                            }Children: []
                        }]
                    }]
                }]
            }]
        }]
    }]
}

In such documents, using MongoDB C# driver, how can I find a node where Id = x

I tried something like this

var filter = Builders<Type>.Filter.Eq(x => x.Id, 3);
                var node = mongoDbRepository.GetOne(filter) ??
                             mongoDbRepository.GetOne(Builders<Type>.Filter.Where(x => x.Children.Any(c=>c.Id == 3)));

But this covers only two levels. In my example, I have 7 levels and I don't have a restriction on depth of level

Once I find that node I need to update that node.

MongoDB Documentation talks about hierarchical documents, but doesn't cover my scenario.


Solution

  • In your situation if you

    don't have a restriction on depth of level

    you can`t create update query. You must change schema for store data:

    https://docs.mongodb.com/v3.2/tutorial/model-tree-structures/

    If you depth is fixed:

    public class TestEntity
    {
        public int Id { get; set; }
        public TestEntity[] Value { get; set; }
    }
    
    class Program
    {
        static void Main()
        {
            const string connectionString = "mongodb://localhost:27017";
            var client = new MongoClient(connectionString);
    
            var db = client.GetDatabase("TestEntities");
            var collection = db.GetCollection<TestEntity>("Entities");
    
            collection.InsertOne(CreateTestEntity(1, CreateTestEntity(2, CreateTestEntity(3, CreateTestEntity(4)))));
            const int selctedId = 3;
            var update = Builders<TestEntity>.Update.AddToSet(x => x.Value, CreateTestEntity(9));
    
            var depth1 = Builders<TestEntity>.Filter.Eq(x => x.Id, selctedId);
            var depth2 = Builders<TestEntity>.Filter.Where(x => x.Value.Any(item => item.Id == selctedId));
            var depth3 = Builders<TestEntity>.Filter.Where(x => x.Value.Any(item => item.Value.Any(item2 => item2.Id == selctedId)));
    
            var filter = depth1 | depth2 | depth3;
    
            collection.UpdateMany(filter, update);
    
            // if you need update document on same depth that you match it in query (for example 3 as selctedId), 
            // you must make 2 query (bad approach, better way is change schema):
            //var testEntity = collection.FindSync(filter).First();
            //testEntity.Value[0].Value[0].Value = new[] {CreateTestEntity(9)}; //todo you must calculate depth what you need in C#
            //collection.ReplaceOne(filter, testEntity);
        }
    
        private static TestEntity CreateTestEntity(int id, params TestEntity[] values)
        {
            return new TestEntity { Id = id, Value = values };
        }
    }