Ottieni i nomi di tutte le chiavi nella raccolta

Mi piacerebbe avere i nomi di tutte le chiavi in ​​una collezione MongoDB.

Ad esempio, da questo:

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

Mi piacerebbe avere le chiavi uniche:

 type, egg, hello 

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

Quindi eseguire distinti sulla raccolta risultante in modo da trovare tutte le chiavi:

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

Con la risposta di Kristina come ispirazione, ho creato uno strumento open source chiamato Variety che fa esattamente questo: https://github.com/variety/variety

È ansible utilizzare l’aggregazione con il nuovo $objectToArrray nella versione 3.4.4 per convertire tutte le coppie chiave e valore in array di documenti seguiti da $unwind e $group con $addToSet per ottenere chiavi distinte nell’intera raccolta.

$$ROOT per referenziare il documento di livello superiore.

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

È ansible utilizzare la query seguente per ottenere le chiavi in ​​un singolo documento.

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

Prova questo:

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

Se la tua collezione di destinazione non è troppo grande, puoi provarla con il client mongo shell:

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

Utilizzando Python. Restituisce l’insieme di tutte le chiavi di livello superiore nella raccolta:

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

Ecco il campione lavorato in Python: questo esempio restituisce i risultati in linea.

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

Questo funziona bene per me:

 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 soluzione ripulita e riutilizzabile 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', ... ] 

Penso che il modo migliore di farlo come menzionato qui sia in mongod 3.4.4+ ma senza usare l’operatore $unwind e usando solo due fasi nella pipeline. Invece possiamo usare gli $mergeObjects e $objectToArray .

Nella fase $group , usiamo l’operatore $mergeObjects per restituire un singolo documento dove chiave / valore provengono da tutti i documenti nella raccolta.

Poi arriva il $project dove usiamo $map e $objectToArray per restituire le chiavi.

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

Ora se abbiamo un documento annidato e vogliamo ottenere anche le chiavi, questo è fattibile. Per semplicità, prendi in considerazione un documento con un semplice documento incorporato simile a questo:

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

La seguente pipeline produce tutte le chiavi (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 piccolo sforzo, possiamo ottenere la chiave per tutti i documenti secondari in un campo array in cui anche gli elementi sono object.

Stavo cercando di scrivere in nodejs e alla fine ho trovato questo:

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

Dopo aver letto la nuova raccolta “allFieldNames”, cancellala.

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

Per ottenere un elenco di tutte le chiavi meno _id , considera l’esecuzione della seguente pipeline aggregata:

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

Secondo la documentazione di mongoldb, una combinazione di distinct

Trova i valori distinti per un campo specificato in una singola raccolta o vista e restituisce i risultati in una matrice.

e le operazioni di raccolta degli indici sono ciò che restituirebbe tutti i valori possibili per una determinata chiave o indice:

Restituisce una matrice che contiene un elenco di documenti che identificano e descrivono gli indici esistenti nella raccolta

Quindi in un dato metodo si può usare un metodo come il seguente, per interrogare una collezione per tutti gli indici registrati, e restituire, diciamo un object con gli indici per le chiavi (questo esempio usa async / attende per NodeJS, ma ovviamente potresti usare qualsiasi altro approccio asincrono):

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

Quindi, interrogando una raccolta con l’indice _id base, si restituisce quanto segue (la raccolta di test ha un solo documento al momento del 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.. }) }); 

Intendiamoci, questo utilizza metodi nativi per il driver NodeJS. Come hanno suggerito altre risposte, ci sono altri approcci, come il quadro generale. Personalmente trovo questo approccio più flessibile, poiché puoi facilmente creare e perfezionare il modo in cui restituire i risultati. Ovviamente, questo riguarda solo gli attributi di livello superiore, non quelli nidificati. Inoltre, per garantire che tutti i documenti siano rappresentati in presenza di indici secondari (diversi da quello _id principale), tali indici devono essere impostati come required .

Ho esteso la soluzione di Carlos LM un po ‘in modo che sia più dettagliata.

Esempio di schema:

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

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

Correre:

 schemafy(db.collection.findOne()); 

Produzione

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

Ho 1 lavoro più semplice in giro …

Quello che puoi fare è inserire dati / documenti nella “collezione” della tua collezione principale, devi inserire gli attributi in 1 collezione separata, diciamo “cose_attributi”.

quindi ogni volta che inserisci “cose”, ottieni da “items_attributes” i valori di confronto di quel documento con le nuove chiavi del documento se una nuova chiave presente lo aggiunge e lo reinserisce di nuovo in quel documento.

Quindi gli attributi_attributi avranno solo 1 documento di chiavi univoche che puoi facilmente ottenere quando ti serve usando findOne ()

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