0

I'm trying to get all the keys in all levels of a Mongo collection. I have something like:

{ 
    "_id" : ObjectId("000000000000000000050904"),
    "CRM" : {
        "Teste" : "true"
    },
    "Endereco" : {
        "Cidade" : "MARINGA",
    },
    "Vendas" : [
        {
            "idP" : NumberInt(34),
            "txt" : "001"
        },{
            "idP" : NumberInt(34),
            "txt" : "002"
        }
    ],
    "Tipos" : [
        {
            "idT" : NumberInt(34),
            "idTipo" : NumberInt(34)
        },{
            "idT" : NumberInt(34),
            "idTipo" : NumberInt(34)
        }
    ]
} 

I tried:

var database = _mongoDatabase.Client.GetDatabase(_project.DbConfig.Database);
var collection = database.GetCollection<dynamic>(collectionName);

var query = collection.AsQueryable<dynamic>();
IDictionary<string, string> myDict = (IDictionary<string, string>)query.FirstOrDefault();

List<string> collectionKeys = new List<string>(myDict.Keys.ToList());

I get:

["CRM", "Teste", "Endereco", "Cidade", "Vendas", "Tipos"]

But I want all levels:

["CRM", "Teste", "Endereco", "Cidade", "Vendas", "IdP", "txt", "Tipos", "idT", "idTipo"]

What is wrong with my code?

Yong Shun
  • 35,286
  • 4
  • 24
  • 46
  • as far as I see, you do not do what you expect to do. `FirstOrDefault` just gets a first record from your collection (ie just applies `limit(1)` option). No more logic here. I still don't really understand what you want to do, but as I see, you need a whole document and then analyze it in some way. Then just use BsonDocument instead dynamic. Then analyze this document considering the structure as json. – dododo Aug 10 '22 at 00:49

1 Answers1

1

@dododo has pointed the direction, you need BsonDocument rather than dynamic.

The implementation logic applies the concept of a recursive function, in which your data may potential with nested documents, or array with (nested) documents, or even with the nested arrays.

GetDocumentKeys:

  1. Returns an empty array when it is null.

  2. Returns an empty array when it is not a BsonDocument.

  3. Iterate each property in BsonDocument, add Name into array and recursive call GetDocumentKeys and GetArrayKeys functions.

GetArrayKeys:

  1. Returns an empty array when it is null.

  2. Returns an empty array when it is not a BsonArray.

  3. Iterate each element in BsonArray, recursive call GetDocumentKeys and GetArrayKeys functions.

private List<string> GetDocumentKeys(BsonValue value)
{
    List<string> keys = new List<string>();

    if (value == null)
        return keys;

    if (value.GetType() != typeof(BsonDocument))
        return keys;

    foreach (var kvp in (BsonDocument)value)
    {
        keys.Add(kvp.Name);

        keys.AddRange(GetArrayKeys(kvp.Value));
        keys.AddRange(GetDocumentKeys(kvp.Value));
    }

    return keys;
}

private List<string> GetArrayKeys(BsonValue value)
{
    List<string> keys = new List<string>();

    if (value == null)
        return keys;

    if (value.GetType() != typeof(BsonArray))
        return keys;

    foreach (var item in (BsonArray)value)
    {
        keys.AddRange(GetArrayKeys(item));
        keys.AddRange(GetDocumentKeys(item));
    }

    return keys;
}

Assume that you work with a single document only.

Caller function:

using MongoDB.Bson;
using System.Collections.Generic;
using System.Linq;

var collection = database.GetCollection<BsonDocument>(collectionName);
var query = collection.AsQueryable<BsonDocument>();
BsonDocument bson = query.FirstOrDefault();
List<string> collectionKeys = GetRootDocumentAllKeys(bson);


private List<string> GetRootDocumentAllKeys(BsonDocument bson)
{
    List<string> collectionKeys = new List<string>();
    collectionKeys.AddRange(GetDocumentKeys(bson));

    return collectionKeys.Distinct()
        .ToList();
}

Sample .NET Fiddle

Yong Shun
  • 35,286
  • 4
  • 24
  • 46