web-dev-qa-db-fra.com

Obtenir les noms de toutes les clés de la collection

J'aimerais obtenir le nom de toutes les clés d'une collection MongoDB.

Par exemple, à partir de ceci:

db.things.insert( { type : ['dog', 'cat'] } );
db.things.insert( { Egg : ['cat'] } );
db.things.insert( { type : [] } );
db.things.insert( { hello : []  } );

J'aimerais avoir les clés uniques:

type, Egg, hello
278
Steve

Vous pouvez le faire avec MapReduce:

mr = db.runCommand({
  "mapreduce" : "my_collection",
  "map" : function() {
    for (var key in this) { emit(key, null); }
  },
  "reduce" : function(key, stuff) { return null; }, 
  "out": "my_collection" + "_keys"
})

Puis lancez distinct sur la collection résultante afin de trouver toutes les clés:

db[mr.result].distinct("_id")
["foo", "bar", "baz", "_id", ...]
304
kristina

Avec la réponse de Kristina comme inspiration, j'ai créé un outil open source appelé Variety qui fait exactement cela: https://github.com/variety/variety

187
James Cropcho

Vous pouvez utiliser l'agrégation avec la nouvelle $objectToArrray dans la version 3.4.4 pour convertir toutes les paires clé/valeur supérieures en tableaux de documents, suivies de $unwind & $group avec $addToSet pour obtenir clés distinctes sur toute la collection.

$$ROOT pour référencer le document de niveau supérieur.

db.things.aggregate([
  {"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
  {"$unwind":"$arrayofkeyvalue"},
  {"$group":{"_id":null,"allkeys":{"$addToSet":"$arrayofkeyvalue.k"}}}
])

Vous pouvez utiliser la requête ci-dessous pour obtenir les clés dans un seul document.

db.things.aggregate([
  {"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
  {"$project":{"keys":"$arrayofkeyvalue.k"}}
])
38
Veeram

Essaye ça:

doc=db.thinks.findOne();
for (key in doc) print(key);
22
Carlos LM

Si votre collection cible n'est pas trop volumineuse, vous pouvez essayer ceci sous le client Shell mongo:

var allKeys = {};

db.YOURCOLLECTION.find().forEach(function(doc){Object.keys(doc).forEach(function(key){allKeys[key]=1})});

allKeys;
13
Li Chunlin

Utilisation de python. Renvoie le jeu de toutes les clés de niveau supérieur de la collection:

#Using pymongo and connection named 'db'

reduce(
    lambda all_keys, rec_keys: all_keys | set(rec_keys), 
    map(lambda d: d.keys(), db.things.find()), 
    set()
)
10
Laizer

Voici l'exemple utilisé en Python: Cet exemple renvoie les résultats en ligne.

from pymongo import MongoClient
from bson.code import Code

mapper = Code("""
    function() {
                  for (var key in this) { emit(key, null); }
               }
""")
reducer = Code("""
    function(key, stuff) { return null; }
""")

distinctThingFields = db.things.map_reduce(mapper, reducer
    , out = {'inline' : 1}
    , full_response = True)
## do something with distinctThingFields['results']
7
BobHy

Si vous utilisez mongodb 3.4.4 et versions ultérieures, vous pouvez utiliser l’agrégation ci-dessous à l’aide de $objectToArray et $group agrégation.

db.collection.aggregate([
  { "$project": {
    "data": { "$objectToArray": "$$ROOT" }
  }},
  { "$project": { "data": "$data.k" }},
  { "$unwind": "$data" },
  { "$group": {
    "_id": null,
    "keys": { "$addToSet": "$data" }
  }}
])

Voici le travail exemple

2
Anthony Winzlet

Une solution nettoyée et réutilisable utilisant du pymongo:

from pymongo import MongoClient
from bson import Code

def get_keys(db, collection):
    client = MongoClient()
    db = client[db]
    map = Code("function() { for (var key in this) { emit(key, null); } }")
    reduce = Code("function(key, stuff) { return null; }")
    result = db[collection].map_reduce(map, reduce, "myresults")
    return result.distinct('_id')

Usage:

get_keys('dbname', 'collection')
>> ['key1', 'key2', ... ]
2
ifischer

Cela fonctionne bien pour moi:

var arrayOfFieldNames = [];

var items = db.NAMECOLLECTION.find();

while(items.hasNext()) {
  var item = items.next();
  for(var index in item) {
    arrayOfFieldNames[index] = index;
   }
}

for (var index in arrayOfFieldNames) {
  print(index);
}
2
ackuser

Je pense que la meilleure façon de faire ceci, comme mentionné ici , est de mongod 3.4.4+ mais sans utiliser l'opérateur $unwind et de n'utiliser que deux étapes dans le pipeline. À la place, nous pouvons utiliser les opérateurs $mergeObjects et $objectToArray .

À l'étape $group, nous utilisons l'opérateur $mergeObjects pour renvoyer un seul document où la clé/la valeur provient de tous les documents de la collection.

Vient ensuite le $project où nous utilisons $map et $objectToArray pour renvoyer les clés.

let allTopLevelKeys =  [
    {
        "$group": {
            "_id": null,
            "array": {
                "$mergeObjects": "$$ROOT"
            }
        }
    },
    {
        "$project": {
            "keys": {
                "$map": {
                    "input": { "$objectToArray": "$array" },
                    "in": "$$this.k"
                }
            }
        }
    }
];

Maintenant, si nous avons des documents imbriqués et que nous voulons aussi obtenir les clés, c'est faisable. Pour plus de simplicité, considérons un document avec un document incorporé simple ressemblant à ceci:

{field1: {field2: "abc"}, field3: "def"}
{field1: {field3: "abc"}, field4: "def"}

Le pipeline suivant renvoie toutes les clés (champ1, champ2, champ3, champ4).

let allFistSecondLevelKeys = [
    {
        "$group": {
            "_id": null,
            "array": {
                "$mergeObjects": "$$ROOT"
            }
        }
    },
    {
        "$project": {
            "keys": {
                "$setUnion": [
                    {
                        "$map": {
                            "input": {
                                "$reduce": {
                                    "input": {
                                        "$map": {
                                            "input": {
                                                "$objectToArray": "$array"
                                            },
                                            "in": {
                                                "$cond": [
                                                    {
                                                        "$eq": [
                                                            {
                                                                "$type": "$$this.v"
                                                            },
                                                            "object"
                                                        ]
                                                    },
                                                    {
                                                        "$objectToArray": "$$this.v"
                                                    },
                                                    [
                                                        "$$this"
                                                    ]
                                                ]
                                            }
                                        }
                                    },
                                    "initialValue": [

                                    ],
                                    "in": {
                                        "$concatArrays": [
                                            "$$this",
                                            "$$value"
                                        ]
                                    }
                                }
                            },
                            "in": "$$this.k"
                        }
                    }
                ]
            }
        }
    }
]

Avec un peu d'effort, nous pouvons obtenir la clé de tous les sous-documents d'un champ de tableau où les éléments sont également des objets. 

1
styvane

Je suis surpris, personne ici n’a eu recours à la logique simple javascript et Set pour filtrer automatiquement les valeurs en double, exemple simple sur mongo Shell comme ci-dessous:

var allKeys = new Set()
db.collectionName.find().forEach( function (o) {for (key in o ) allKeys.add(key)})
for(let key of allKeys) print(key)

Ceci imprimera toutes les clés uniques possibles dans le nom de la collection: collectionName .

1
krishna Prasad

Pour obtenir la liste de toutes les clés moins _id, envisagez d'exécuter le pipeline d'agrégat suivant:

var keys = db.collection.aggregate([
    { "$project": {
       "hashmaps": { "$objectToArray": "$$ROOT" } 
    } }, 
    { "$project": {
       "fields": "$hashmaps.k"
    } },
    { "$group": {
        "_id": null,
        "fields": { "$addToSet": "$fields" }
    } },
    { "$project": {
            "keys": {
                "$setDifference": [
                    {
                        "$reduce": {
                            "input": "$fields",
                            "initialValue": [],
                            "in": { "$setUnion" : ["$$value", "$$this"] }
                        }
                    },
                    ["_id"]
                ]
            }
        }
    }
]).toArray()[0]["keys"];
1
chridam

Peut-être un peu hors sujet, mais vous pouvez imprimer de manière récursive en jolie toutes les clés/champs d'un objet:

function _printFields(item, level) {
    if ((typeof item) != "object") {
        return
    }
    for (var index in item) {
        print(" ".repeat(level * 4) + index)
        if ((typeof item[index]) == "object") {
            _printFields(item[index], level + 1)
        }
    }
}

function printFields(item) {
    _printFields(item, 0)
}

Utile lorsque tous les objets d'une collection ont la même structure. 

0
qed

Selon mongoldb documentation , une combinaison de distinct

Recherche les valeurs distinctes pour un champ spécifié dans une collection ou une vue unique et renvoie les résultats dans un tableau.

et indexes les opérations de collecte sont ce qui renverrait toutes les valeurs possibles pour une clé ou un index donné:

Renvoie un tableau contenant une liste de documents identifiant et décrivant les index existants de la collection.

Ainsi, dans une méthode donnée, on pourrait utiliser une méthode comme la suivante pour interroger une collection sur tous ses index enregistrés et renvoyer, par exemple, un objet avec les index pour les clés (cet exemple utilise async/wait pour NodeJS, mais vous pouvez évidemment utiliser toute autre approche asynchrone):

async function GetFor(collection, index) {

    let currentIndexes;
    let indexNames = [];
    let final = {};
    let vals = [];

    try {
        currentIndexes = await collection.indexes();
        await ParseIndexes();
        //Check if a specific index was queried, otherwise, iterate for all existing indexes
        if (index && typeof index === "string") return await ParseFor(index, indexNames);
        await ParseDoc(indexNames);
        await Promise.all(vals);
        return final;
    } catch (e) {
        throw e;
    }

    function ParseIndexes() {
        return new Promise(function (result) {
            let err;
            for (let ind in currentIndexes) {
                let index = currentIndexes[ind];
                if (!index) {
                    err = "No Key For Index "+index; break;
                }
                let Name = Object.keys(index.key);
                if (Name.length === 0) {
                    err = "No Name For Index"; break;
                }
                indexNames.Push(Name[0]);
            }
            return result(err ? Promise.reject(err) : Promise.resolve());
        })
    }

    async function ParseFor(index, inDoc) {
        if (inDoc.indexOf(index) === -1) throw "No Such Index In Collection";
        try {
            await DistinctFor(index);
            return final;
        } catch (e) {
            throw e
        }
    }
    function ParseDoc(doc) {
        return new Promise(function (result) {
            let err;
            for (let index in doc) {
                let key = doc[index];
                if (!key) {
                    err = "No Key For Index "+index; break;
                }
                vals.Push(new Promise(function (pushed) {
                    DistinctFor(key)
                        .then(pushed)
                        .catch(function (err) {
                            return pushed(Promise.resolve());
                        })
                }))
            }
            return result(err ? Promise.reject(err) : Promise.resolve());
        })
    }

    async function DistinctFor(key) {
        if (!key) throw "Key Is Undefined";
        try {
            final[key] = await collection.distinct(key);
        } catch (e) {
            final[key] = 'failed';
            throw e;
        }
    }
}

Ainsi, l'interrogation d'une collection avec l'index de base _id renverrait ce qui suit (la collection de test ne contient qu'un seul document au moment du test):

Mongo.MongoClient.connect(url, function (err, client) {
    assert.equal(null, err);

    let collection = client.db('my db').collection('the targeted collection');

    GetFor(collection, '_id')
        .then(function () {
            //returns
            // { _id: [ 5ae901e77e322342de1fb701 ] }
        })
        .catch(function (err) {
            //manage your error..
        })
});

Attention, ceci utilise des méthodes natives du pilote NodeJS. Comme d'autres réponses l'ont suggéré, il existe d'autres approches, telles que le cadre global. Personnellement, je trouve cette approche plus flexible, car vous pouvez facilement créer et affiner la manière de renvoyer les résultats. Évidemment, cela ne concerne que les attributs de niveau supérieur, pas ceux imbriqués . De plus, pour garantir la représentation de tous les documents en cas d'index secondaires (autres que celui principal _id), ces index doivent être définis sur required.

0
jlmurph

J'essayais d'écrire dans nodejs et j'ai finalement trouvé ceci:

db.collection('collectionName').mapReduce(
function() {
    for (var key in this) {
        emit(key, null);
    }
},
function(key, stuff) {
    return null;
}, {
    "out": "allFieldNames"
},
function(err, results) {
    var fields = db.collection('allFieldNames').distinct('_id');
    fields
        .then(function(data) {
            var finalData = {
                "status": "success",
                "fields": data
            };
            res.send(finalData);
            delteCollection(db, 'allFieldNames');
        })
        .catch(function(err) {
            res.send(err);
            delteCollection(db, 'allFieldNames');
        });
 });

Après avoir lu la collection nouvellement créée "allFieldNames", supprimez-la.

db.collection("allFieldNames").remove({}, function (err,result) {
     db.close();
     return; 
});
0
Gautam

Nous pouvons y parvenir en utilisant le fichier mongo js. Ajoutez le code ci-dessous dans votre fichier getCollectionName.js et exécutez le fichier js dans la console Linux comme indiqué ci-dessous:

mongo --Host 192.168.1.135 getCollectionName.js

db_set = connect("192.168.1.135:27017/database_set_name"); // for Local testing
// db_set.auth("username_of_db", "password_of_db"); // if required

db_set.getMongo().setSlaveOk();

var collectionArray = db_set.getCollectionNames();

collectionArray.forEach(function(collectionName){

    if ( collectionName == 'system.indexes' || collectionName == 'system.profile' || collectionName == 'system.users' ) {
        return;
    }

    print("\nCollection Name = "+collectionName);
    print("All Fields :\n");

    var arrayOfFieldNames = []; 
    var items = db_set[collectionName].find();
    // var items = db_set[collectionName].find().sort({'_id':-1}).limit(100); // if you want fast & scan only last 100 records of each collection
    while(items.hasNext()) {
        var item = items.next(); 
        for(var index in item) {
            arrayOfFieldNames[index] = index;
        }
    }
    for (var index in arrayOfFieldNames) {
        print(index);
    }

});

quit();

Merci @ackuser

0
Irshad Khan

Suite au fil de réponse de @James Cropcho, j'ai atterri sur ce qui suit, que j'ai trouvé super facile à utiliser. C'est un outil binaire, et c'est exactement ce que je cherchais: mongoeye .

En utilisant cet outil, il a fallu environ 2 minutes pour obtenir mon schéma exporté à partir de la ligne de commande.

0
paneer_tikka