Question

Je voudrais obtenir les noms de toutes les clés dans une collection MongoDB.

Par exemple, de:

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

Je voudrais obtenir les clés uniques:

type, egg, hello
Était-ce utile?

La solution

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"
})

Ensuite, exécutez distincte sur la collection résultante de façon à trouver toutes les clés:

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

Autres conseils

réponse Kristina comme source d'inspiration, je créé un outil open source appelée variété qui fait exactement ceci: https://github.com/variety/variety

Vous pouvez utiliser l'agrégation avec une nouvelle $objectToArrray la version de 3.4.4 pour convertir toutes les clés supérieure rech valeur dans les tableaux de documents suivi par $unwind et $group avec $addToSet pour obtenir les clés distinctes à travers 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 ci-dessous pour obtenir requête clés dans un seul document.

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

Essayez ceci:

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

Si votre collection cible est pas trop grand, vous pouvez essayer cette sous client shell mongo:

var allKeys = {};

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

allKeys;

En utilisant python. Renvoie l'ensemble de toutes les clés de haut niveau dans 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()
)

Voici l'exemple travaillé en Python: Cet exemple retourne la ligne de résultats.

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']

Une solution nettoyé et réutilisable en utilisant 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')

Utilisation:

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

Si vous utilisez MongoDB 3.4.4 et au-dessus, vous pouvez utiliser ci-dessous l'agrégation en utilisant $objectToArray et < strong> $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

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);
}

Je suis surprise, personne n'a ici ans en utilisant javascript simple et logique de Set pour filtrer automatiquement les valeurs de doublons, simple exemple sur shell mongo 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)

imprimera toutes les possibilités uniques touches dans le nom de la collection. collectionName

Pour obtenir une liste de toutes les clés moins _id, envisagez d'exécuter le pipeline global 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"];

Je pense que la meilleure façon le faire comme mentionné ci est en mongod 3.4.4+ mais sans utiliser l'opérateur $unwind et en utilisant seulement deux étapes du pipeline. Au lieu de cela, nous pouvons utiliser le $mergeObjects et les opérateurs de $objectToArray.

Dans la phase de $group, nous utilisons l'opérateur $mergeObjects pour renvoyer un document unique où la clé / valeur sont de tous les documents de la collection.

Puis vient le $project où nous utilisons $map et $objectToArray pour remettre les clés.

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

Maintenant, si nous avons un des documents imbriqués et que vous souhaitez obtenir les clés aussi bien, ce qui est faisable. Par souci de simplicité, considérons un document simple document intégré qui ressemble à ceci:

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

Le rendement du pipeline suivant toutes les clés (champ1, champ2, field3, field4).

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é pour tous dans un sous-document champ tableau où les éléments sont des objets aussi bien.

Je suis en train d'écrire dans nodejs et est finalement arrivé à 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-le.

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

Selon le mongoldb documentation , une combinaison de distinct

  

Trouve les valeurs distinctes pour une zone spécifiée dans une seule collection ou la vue et renvoie les résultats dans un tableau.

et opérations de collecte d'indices sont ce qui renverrait toutes les valeurs possibles pour une clé donnée, ou de l'indice:

  

Retourne un tableau qui contient une liste de documents qui identifient et décrivent les index existants sur la collection

Ainsi, dans une méthode donnée on pourrait faire utiliser une méthode comme la suivante, afin d'interroger une collection pour tous ses index enregistré, et revenir, dire un objet avec les index pour les clés (cet exemple utilise async / attendre 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;
        }
    }
}

l'interrogation d'une collection avec l'indice de base _id, renverrait les éléments suivants (collection de test ne comporte 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..
        })
});

Rappelez-vous, celui-ci utilise des méthodes natives du pilote NodeJS. Comme d'autres réponses ont suggéré, il y a d'autres approches, telles que le cadre global. Personnellement, je trouve cette approche plus souple, comme vous pouvez facilement créer et affiner la façon de retourner les résultats. De toute évidence, cette adresse uniquement les attributs de haut niveau, et non celles imbriquées. En outre, pour garantir que tous les documents sont représentés devrait y avoir des indices secondaires (autres que le principal _id un), ces indices devraient être définis comme required.

Peut-être un peu hors-sujet, mais vous pouvez récursive jolie imprimer 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 a la même structure.

Nous pouvons y parvenir en utilisant le fichier de mongo. Ajouter ci-dessous le code dans votre getCollectionName.js fichier de fichier et lancez dans la console de Linux donnée 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

Je tendis la solution de Carlos LM un peu il est donc plus détaillée.

Exemple de schéma:

var schema = {
    _id: 123,
    id: 12,
    t: 'title',
    p: 4.5,
    ls: [{
            l: 'lemma',
            p: {
                pp: 8.9
            }
        },
         {
            l: 'lemma2',
            p: {
               pp: 8.3
           }
        }
    ]
};

Type dans la console:

var schemafy = function(schema, i, limit) {
    var i = (typeof i !== 'undefined') ? i : 1;
    var limit = (typeof limit !== 'undefined') ? limit : false;
    var type = '';
    var array = false;

    for (key in schema) {
        type = typeof schema[key];
        array = (schema[key] instanceof Array) ? true : false;

        if (type === 'object') {
            print(Array(i).join('    ') + key+' <'+((array) ? 'array' : type)+'>:');
            schemafy(schema[key], i+1, array);
        } else {
            print(Array(i).join('    ') + key+' <'+type+'>');
        }

        if (limit) {
            break;
        }
    }
}

Exécuter:

schemafy(db.collection.findOne());

Sortie

_id <number>
id <number>
t <string>
p <number>
ls <object>:
    0 <object>:
    l <string>
    p <object>:
        pp <number> 

J'ai 1 travail plus simple autour ...

Qu'est-ce que vous pouvez faire est tout en insérant des données / document dans votre collection principale « choses » vous devez insérer les attributs dans une collection séparée permet de dire « things_attributes ».

donc à chaque fois que vous insérez dans des « choses », vous ne recevez de « things_attributes » comparer les valeurs de ce document avec vos nouvelles clés de document si une nouvelle clé présente append dans ce document et encore re-insérer.

things_attributes auront seulement 1 documents de clés uniques que vous pouvez facilement obtenir à chaque fois que vous avez besoin en utilisant findOne ()

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top