Search code examples
c#mongodbbsondocument

How to check for nested property in BsonDocument - Contains doesn't work


When I wrote code to grab a nested property, it fails when the entire path doesn't exist - which is fair enough bsonDoc["Meeting"]["Session"]["Time"]

I can't see any way to protect against this, writing bsonDoc.Contains("Meeting.Session.Time") returns false even when it exists

Writing bsonDoc.Contains("Time") also returns fails, even when it does exist, so it can't even check the field property at all, if it's nested... ?

The documentation and even the code contains no clue how to do what I need. Not sure it's possible.

Is there a way to write a guard clause for nested bson document properties - ie does the BsonDocument class have a nested key checking mechanism?


Solution

  • I don't think there is any method from BsonDocument that supports getting element/value with the nested property.

    But you can implement the logic/extension method to deal with:

    Concept:

    1. Split key by '.'.

    2. First-level search.

      2.1 First chainKey will search from BsonDocument via TryGetValue.

      2.2 If the key has existed, @value (BsonValue) will contain value (This is needed while querying for nested levels).

      2.3 If the key doesn't exist, break the loop logic.

    3. Second-level or further level search

      3.1 Concept is the same with 2, just will search from BsonValue (as stated in 2.2).

    public static class BsonDocumentExtensions
    {
        public static bool TryGetChainedValue(this BsonDocument doc, string key, out BsonValue @value)
        {
            @value = default;
            // Get key, value from passed BsonDocument for first time, then refer to BsonValue to get key, value for nested property
            bool first = true;
            
            try
            {
                if (String.IsNullOrWhiteSpace(key))
                    return false;
            
                string[] chainedKeys = key.Split(".").ToArray();
                
                bool hasKey = false;
                foreach (var chainKey in chainedKeys)
                {       
                    if (first)
                    {
                        hasKey = doc.TryGetValue(chainKey, out @value);
                        
                        first = false;
                    }
                    else
                    {
                        hasKey = (@value.ToBsonDocument()).TryGetValue(chainKey, out @value);
                    }
    
                    // Throw exception if key not existed.
                    if (!hasKey)
                        throw new Exception();
                }
            
                return true;
            }
            catch
            {           
                @value = default;
                return false;   
            }
        }
    }
    

    Sample .NET Fiddle


    The concept above is same as multiple if logic as below:

    if (doc.TryGetValue("Meeting", out BsonValue meetingValue))
    {
        if ((meetingValue.ToBsonDocument()).TryGetValue("Session", out BsonValue sessionValue))
        {
            if ((sessionValue.ToBsonDocument()).TryGetValue("Time", out BsonValue timeValue))
            {
                Console.WriteLine(timeValue);
            }
        }
    }