Pregunta

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

Por ejemplo, de esta:

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

Me gustaría conseguir las claves únicas:

type, egg, hello
¿Fue útil?

Solución

Se puede 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 distinta en la colección resultante con el fin de encontrar todas las teclas:

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

Otros consejos

respuesta de Kristina como fuente de inspiración, he creado una herramienta de código abierto llamado Variedad que hace exactamente esto: https://github.com/variety/variety

Se puede utilizar la agregación con el nuevo $objectToArrray en 3.4.4 versión para convertir todo par de claves superior y valor en matrices de documentos seguido de $unwind & $group con $addToSet para obtener claves distintas a través de 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"}}}
])

Se puede utilizar a continuación la consulta para conseguir las llaves 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 de destino no es demasiado grande, se puede probar esto en virtud de cliente de consola mongo:

var allKeys = {};

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

allKeys;

Uso de pitón. Devuelve el conjunto de todas las claves de nivel superior de 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()
)

Esto es el ejemplo trabajado en Python: Este ejemplo 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']

A limpiado y solución reutilizable usando 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', ... ]

Si está utilizando mongodb 3.4.4 y superiores, puede utilizar debajo de la agregación usando $objectToArray y href="https://docs.mongodb.com/manual/reference/operator/aggregation/group/" rel="nofollow noreferrer"> < strong> $group agregación

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

Aquí está el trabajo ejemplo

Esta bien que funciona para mí:

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

Soy sorpresa, nadie aquí tiene ans mediante el uso de javascript simple y lógica Set para automáticamente filtrar los valores duplicados, ejemplo simple en consola mongo como a continuación:

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

Esto imprimirá todos los posibles únicas teclas en el nombre de la colección:. collectionName

Para obtener una lista de todas las teclas Menos _id, considere la ejecución del siguiente tubería 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"];

Creo que la mejor manera de hacer esto como se mencionó aquí está en mongod 3.4.4+ pero sin utilizar el operador $unwind y el uso de sólo dos etapas en la tubería. En su lugar se puede utilizar la $mergeObjects y operadores $objectToArray .

En la etapa $group, utilizamos el operador $mergeObjects para devolver un solo documento en clave / valor son de todos los documentos de la colección.

Luego viene la $project donde utilizamos $map y $objectToArray a devolver las llaves.

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

Ahora bien, si tenemos unos documentos anidados y desea obtener las claves, así, esto es factible. Por simplicidad, vamos a considerar un documento con una simple incrustado documento que tener este aspecto:

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

La siguiente rendimiento tubería todas las teclas (Campo1 Campo2 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"
                        }
                    }
                ]
            }
        }
    }
]

Con un poco de esfuerzo, podemos obtener la clave para todo el subdocumento en un campo con elementos que son objeto también.

Yo estaba tratando de escribir en nodejs y finalmente se le 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 "allFieldNames" de nueva creación, borrarlo.

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

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

  

encuentra los valores distintos para un campo especificado a través de una sola colección o vista y devuelve los resultados en una matriz.

índices operaciones de recogida son lo devolvería todos los valores posibles para una clave dada, o índice:

  

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

Así que en cierto un método dado podría hacer uso de un método como el siguiente, con el fin de consultar una recopilación de todo lo que está índices registrados, y de retorno, por ejemplo un objeto con los índices de claves (este ejemplo utiliza asíncrono / esperan para NodeJS, pero obviamente se podría utilizar cualquier otro método asíncrono):

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

Así se consulta una colección con el índice _id básica, devolvería el siguiente (colección prueba sólo 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í, este utiliza métodos propios de la NodeJS conductor. Como han sugerido algunas otras respuestas, hay otros enfoques, tales como el marco global. Personalmente considero que este enfoque más flexible, como se puede crear fácilmente y ajustar la forma de devolver los resultados. Obviamente, lo atribuye sólo las direcciones de nivel superior, no los anidados. Además, para garantizar que todos los documentos están representados debería haber índices secundarios (excepto la principal _id), esos índices se deben establecer como required.

Tal vez un poco fuera de tema, pero se puede imprimir de forma recursiva bastante-todas las teclas / campos de un objeto:

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

útil cuando todos los objetos en una colección tiene la misma estructura.

puede lograr esto mediante el uso de archivos js mongo. Añadir a continuación el código en el getCollectionName.js archivo y ejecutar js archivo en la consola de Linux como figura a continuación:

  

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

Gracias @ackuser

Siguiendo el hilo de la respuesta de @ James Cropcho, que aterrizó en la continuación de lo cual me pareció ser muy fácil de usar. Es una herramienta binario, que es exactamente lo que estaba buscando: mongoeye .

El uso de esta herramienta que tomó cerca de 2 minutos para conseguir mi esquema exportado desde la línea de comandos.

extendí solución de Carlos LM un poco por lo que es 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
           }
        }
    ]
};

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

Ejecutar:

schemafy(db.collection.findOne());

salida

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

tengo 1 trabajo más sencillo todo ...

Lo que puede hacer es, mientras que la inserción de datos / documento en su colección principal "cosas" debe insertar los atributos de la recogida selectiva 1 digamos "things_attributes".

por lo que cada vez que se inserta en "cosas", recibes a razón de "things_attributes" comparar los valores de ese documento con sus nuevas claves de documentos si cualquier tecla nueva presentes en prueba de que en ese documento y de nuevo volver a insertarlo.

Así things_attributes tendrán solamente 1 documento de claves únicas que se pueden conseguir fácilmente cuando cada vez que necesita mediante el uso de findOne ()

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top