Usar map / reduce para mapear las propiedades en una colección

Actualización: seguimiento de MongoDB Obtenga los nombres de todas las claves en la colección .

Como señaló Kristina , se puede usar el mapa / reducir de Mongodb para enumerar las claves de una colección:

db.things.insert( { type : ['dog', 'cat'] } ); db.things.insert( { egg : ['cat'] } ); db.things.insert( { type : [] }); db.things.insert( { hello : [] } ); mr = db.runCommand({"mapreduce" : "things", "map" : function() { for (var key in this) { emit(key, null); } }, "reduce" : function(key, stuff) { return null; }}) db[mr.result].distinct("_id") //output: [ "_id", "egg", "hello", "type" ] 

Siempre que queramos obtener solo las claves ubicadas en el primer nivel de profundidad, esto funciona bien. Sin embargo, no podrá recuperar esas claves que se encuentran en niveles más profundos. Si agregamos un nuevo registro:

 db.things.insert({foo: {bar: {baaar: true}}}) 

Y ejecutamos nuevamente el fragmento map-reduce + distinct anterior, obtendremos:

 [ "_id", "egg", "foo", "hello", "type" ] 

Pero no obtendremos la barra y las teclas baaar , que están anidadas en la estructura de datos. La pregunta es: ¿cómo recupero todas las claves, sin importar su nivel de profundidad? Idealmente, me gustaría que el guión baje a todos los niveles de profundidad, produciendo un resultado como:

 ["_id","egg","foo","foo.bar","foo.bar.baaar","hello","type"] 

¡Gracias de antemano!

De acuerdo, esto es un poco más complejo porque necesitarás usar alguna recursión.

Para que la recursión ocurra, deberá poder almacenar algunas funciones en el servidor.

Paso 1: defina algunas funciones y póngalas en el lado del servidor

 isArray = function (v) { return v && typeof v === 'object' && typeof v.length === 'number' && !(v.propertyIsEnumerable('length')); } m_sub = function(base, value){ for(var key in value) { emit(base + "." + key, null); if( isArray(value[key]) || typeof value[key] == 'object'){ m_sub(base + "." + key, value[key]); } } } db.system.js.save( { _id : "isArray", value : isArray } ); db.system.js.save( { _id : "m_sub", value : m_sub } ); 

Paso 2: defina el mapa y reduzca las funciones

 map = function(){ for(var key in this) { emit(key, null); if( isArray(this[key]) || typeof this[key] == 'object'){ m_sub(key, this[key]); } } } reduce = function(key, stuff){ return null; } 

Paso 3: ejecuta el mapa reduce y mira los resultados

 mr = db.runCommand({"mapreduce" : "things", "map" : map, "reduce" : reduce,"out": "things" + "_keys"}); db[mr.result].distinct("_id"); 

Los resultados que obtendrá son:

 ["_id", "_id.isObjectId", "_id.str", "_id.tojson", "egg", "egg.0", "foo", "foo.bar", "foo.bar.baaaar", "hello", "type", "type.0", "type.1"] 

Hay un problema obvio aquí, estamos agregando algunos campos inesperados aquí: 1. los datos _id 2. el .0 (en el huevo y el tipo)

Paso 4: algunas soluciones posibles

Para el problema n. ° 1, la solución es relativamente fácil. Solo modifica la función de map . Cambia esto:

 emit(base + "." + key, null); if( isArray... 

a esto:

 if(key != "_id") { emit(base + "." + key, null); if( isArray... } 

El problema n. ° 2 es un poco más arriesgado. Querías todas las claves y técnicamente “egg.0” es una clave válida. Puede modificar m_sub para ignorar tales claves numéricas. Pero también es fácil ver una situación en la que esto fracasa. Digamos que tiene una matriz asociativa dentro de una matriz regular, entonces quiere que aparezca ese “0”. Dejaré el rest de esa solución a su criterio.

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

Espero que lo encuentres útil. Avíseme si tiene alguna pregunta o algún problema al usarla.

como una función simple;

 const getProps = (db, collection) => new Promise((resolve, reject) => { db .collection(collection) .mapReduce(function() { for (var key in this) { emit(key, null) } }, (prev, next) => null, { out: collection + '_keys' }, (err, collection_props) => { if (err) reject(err) collection_props .find() .toArray() .then( props => resolve(props.map(({_id}) => _id)) ) }) })