Obtener nombres de todas las claves en la colección

Me gustaría obtener los nombres de todas las claves en una colección de MongoDB.

Por ejemplo, de esto:

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

Me gustaría obtener las claves únicas:

 type, egg, hello 

Puedes hacer esto con 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" }) 

A continuación, ejecute distinto en la colección resultante para encontrar todas las claves:

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

Con la respuesta de Kristina como inspiración, creé una herramienta de código abierto llamada Variety que hace exactamente esto: https://github.com/variety/variety

Puede usar la agregación con new $objectToArrray en la versión 3.4.4 para convertir todos los pares clave y valor superior en matrices de documentos seguidos de $unwind & $group con $addToSet para obtener claves distintas en toda la colección.

$$ROOT para hacer referencia al documento de nivel superior.

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

Puede usar la consulta a continuación para obtener claves en un solo documento.

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

Prueba esto:

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

Si su colección objective no es demasiado grande, puede probar esto en el cliente mongo shell:

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

Usando Python. Devuelve el conjunto de todas las claves de nivel superior en la colección:

 #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() ) 

Aquí está la muestra trabajada en Python: esta muestra devuelve los resultados en línea.

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

Esto funciona bien para mi:

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

Una solución limpia y reutilizable que utiliza 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') 

Uso:

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

Creo que la mejor manera de hacerlo, como se menciona aquí, es en mongod 3.4.4+ pero sin usar el operador $unwind y usar solo dos etapas en la tubería. En su lugar, podemos usar los $mergeObjects y $objectToArray .

En la etapa $group , usamos el operador $mergeObjects para devolver un único documento donde la clave / valor es de todos los documentos en la colección.

Luego viene el $project donde usamos $map y $objectToArray para devolver las claves.

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

Ahora bien, si tenemos documentos nesteds y también queremos obtener las claves, esto es factible. Para simplificar, consideremos un documento con un simple documento incrustado que se vea así:

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

La siguiente tubería proporciona todas las claves (campo1, campo2, campo3, campo4).

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

Con un poco de esfuerzo, podemos obtener la clave para todos los subdocumentos en un campo de matriz donde los elementos también son objetos.

Intenté escribir en nodejs y finalmente se me ocurrió esto:

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

Después de leer la colección recién creada “allFieldNames”, elimínela.

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

Para obtener una lista de todas las claves menos _id , considere ejecutar la siguiente canalización agregada:

 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"]; 

De acuerdo con la documentación de mongoldb, una combinación de distinct

Encuentra los valores distintos para un campo específico en una sola colección o vista y devuelve los resultados en una matriz.

y las operaciones de recostackción de índices son las que devolverían todos los valores posibles para una clave o índice dados:

Devuelve una matriz que contiene una lista de documentos que identifican y describen los índices existentes en la colección

Entonces, en un método dado uno podría usar un método como el siguiente, para consultar una colección de todos sus índices registrados, y devolver, digamos un objeto con los índices para las claves (este ejemplo usa async / await para NodeJS, pero obviamente, podrías usar cualquier otro enfoque asincrónico):

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

Por lo tanto, consultar una colección con el índice _id básico devolvería lo siguiente (la colección de prueba solo tiene un documento en el momento de la prueba):

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

Eso sí, utiliza métodos nativos del controlador NodeJS. Como algunas otras respuestas han sugerido, hay otros enfoques, como el marco agregado. Personalmente, considero que este enfoque es más flexible, ya que puede crear y ajustar fácilmente cómo devolver los resultados. Obviamente, esto solo aborda atributos de nivel superior, no nesteds. Además, para garantizar que todos los documentos estén representados en caso de que haya índices secundarios (que no sean el _id principal), dichos índices deben configurarse según sea required .

Amplié un poco la solución de Carlos LM para que sea más detallada.

Ejemplo de un esquema:

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

Escriba en la consola:

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

Correr:

 schemafy(db.collection.findOne()); 

Salida

 _id  id  t  p  ls : 0 : l  p : pp  

Tengo 1 trabajo más simple alrededor …

Lo que puede hacer es insertar datos / documentos en las “cosas” de su colección principal, debe insertar los atributos en 1 colección por separado, digamos “atributos_de_menos”.

así que cada vez que insertas en “cosas”, obtienes de “atributos_componentes” los valores de comparación de ese documento con las nuevas claves del documento si alguna clave nueva presente la agrega en ese documento y nuevamente la insertas.

Entonces, los atributos de las cosas solo tendrán 1 documento de claves únicas que podrá obtener fácilmente cuando lo necesite utilizando findOne ()

 var schematodo = db.[collection].findOne(); for (var key in schematodo) { print (key) ; }