Obtenez les noms de toutes les clés de la collection
-
21-09-2019 - |
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
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.
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
Après le fil de la réponse de Cropcho @ James, je suis tombé sur ce qui suit que je trouvais être super facile à utiliser. Il est un outil binaire, ce qui est exactement ce que je cherchais: mongoeye .
En utilisant cet outil, il a fallu 2 minutes pour obtenir mon schéma exporté de la ligne de commande.
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 ()