Am'd place să ia numele de la toate cheile într-o colecție MongoDB.
De exemplu, de la asta:
db.things.insert( { type : ['dog', 'cat'] } );
db.things.insert( { egg : ['cat'] } );
db.things.insert( { type : [] } );
db.things.insert( { hello : [] } );
Am'd dori să obțină chei unice:
type, egg, hello
Ai putea face acest lucru cu 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"
})
Apoi rulați distincte privind colectarea rezultat, astfel încât pentru a găsi toate cheile:
db[mr.result].distinct("_id")
["foo", "bar", "baz", "_id", ...]
Cu Kristina's a răspunde ca sursă de inspirație, am creat un instrument open source numit Varietate care face exact acest lucru: https://github.com/variety/variety
Puteți utiliza agregare cu noi $objectToArrray
3.4.4
version pentru a converti toate top-cheie & valoarea pereche în document tablouri urmat de $relaxa
& $grup
cu $addToSet
pentru a obține chei distincte în întreaga colecție.
$$ROOT
pentru corelarea la nivel de top document.
db.things.aggregate([
{"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
{"$unwind":"$arrayofkeyvalue"},
{"$group":{"_id":null,"allkeys":{"$addToSet":"$arrayofkeyvalue.k"}}}
])
Puteți utiliza de mai jos interogare pentru a obține cheile într-un singur document.
db.things.aggregate([
{"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
{"$project":{"keys":"$arrayofkeyvalue.k"}}
])
Dacă ținta de colectare nu este prea mare, puteți încerca acest lucru în mongo shell client:
var allKeys = {};
db.YOURCOLLECTION.find().forEach(function(doc){Object.keys(doc).forEach(function(key){allKeys[key]=1})});
allKeys;
Aici este o mostră a lucrat în Python: Această probă se întoarce rezultatele inline.
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']
Un curățate și reutilizabile soluție folosind 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')
Utilizare:
get_keys('dbname', 'collection')
>> ['key1', 'key2', ... ]
Dacă utilizați mongodb 3.4.4 și mai sus, atunci puteți folosi de mai jos agregare folosind $objectToArray
și $grup
agregare
db.collection.aggregate([
{ "$project": {
"data": { "$objectToArray": "$$ROOT" }
}},
{ "$project": { "data": "$data.k" }},
{ "$unwind": "$data" },
{ "$group": {
"_id": null,
"keys": { "$addToSet": "$data" }
}}
])
Aici este de lucru exemplu
Eu sunt surpriza, nimeni de aici nu are ans folosind simplu "javascript" și " Set " logica pentru a filtra în mod automat duplicatele valori, simplu exemplu pe mongo shellca mai jos:
var allKeys = new Set()
db.collectionName.find().forEach( function (o) {for (key in o ) allKeys.add(key)})
for(let key of allKeys) print(key)
Acest lucru se va imprima toate posibile unic taste în numele colecției: collectionName.
Aceasta funcționează bine pentru mine:
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);
}
Pentru a obține o listă cu toate cheile minus băiete
, ia în considerare execută următoarele agregate de conducte:
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"];
Cred că cel mai bun mod de a face acest lucru așa cum am menționat aici este în mongod 3.4.4+ dar fără a utiliza $relaxa
operator și folosind doar două etape în conducte. În schimb, putem folosi $mergeObjects
și $objectToArray
operatorii.
În $grupul de etapă, vom folosi
$mergeObjects` să returneze un singur document unde-cheie/valoare la toate documentele din colecție.
Apoi vine $proiect în care vom folosi
$hartă " și " $objectToArray` pentru a reveni la chei.
let allTopLevelKeys = [
{
"$group": {
"_id": null,
"array": {
"$mergeObjects": "$$ROOT"
}
}
},
{
"$project": {
"keys": {
"$map": {
"input": { "$objectToArray": "$array" },
"in": "$$this.k"
}
}
}
}
];
Acum, dacă avem o imbricate documente și doriți să obțineți cheile la fel de bine, acest lucru este greu de realizat. Pentru simplitate, să ia în considerare un document cu simplu încorporat document care arata ca aceasta:
{field1: {field2: "abc"}, field3: "def"}
{field1: {field3: "abc"}, field4: "def"}
Următoarele conducte randamentul toate cheile (field1, domeniu2, 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"
}
}
]
}
}
}
]
Cu un pic de efort, putem obține cheia pentru toate subdocument într-o matrice de teren în cazul în care elementele sunt obiect la fel de bine.
În urma firul de la @James Cropcho's a răspunde, am aterizat pe următoarele care am găsit a fi foarte ușor de utilizat. Este un binar instrument, care este exact ceea ce am fost în căutarea pentru: mongoeye.
Folosind acest instrument a fost nevoie de aproximativ 2 minute pentru a obține schema mea exportate din linia de comandă.
Am încercat să scriu în nodejs și în cele din urmă a venit cu asta:
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');
});
});
După ce a citit nou creat colectia "allFieldNames", ștergeți-l.
db.collection("allFieldNames").remove({}, function (err,result) {
db.close();
return;
});
Ca pe mongoldb documentația, o combinație de distinct
Constată valori distincte pentru un anumit domeniu într-o singură colecție sau pentru a vizualiza și returnează rezultatele într-o matrice.
și indici operațiunile de colectare sunt ceea ce va returna toate valorile posibile pentru o anumită cheie, sau index:
Returnează o matrice care deține o listă de documente care să identifice și să descrie existente indicii cu privire la colectarea
Deci, într-o anumită metodă s-ar putea face uz de o metodă cum ar fi următoarele, în scopul de a interoga o colecție pentru toate l's a înregistrat indici, și să se întoarcă, să zicem un obiect cu indici pentru chei (acest exemplu utilizează asincron/așteaptă pentru NodeJS, dar, evident, ai putea să utilizați orice alte asincron abordare):
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;
}
}
}
Deci interogarea o colectie cu cele de bază băiete
index, ar reveni următoarele (test de colectare are un singur document la momentul testului):
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..
})
});
Tine minte, acesta utilizează metode native pentru NodeJS Driver. Ca și alte răspunsuri au sugerat, există și alte abordări, cum ar fi agregate cadru. Personal, consider această abordare mai flexibilă, după cum puteți crea cu ușurință și de a ajusta cum să se întoarcă rezultatele. Evident, asta doar adrese de nivel superior atribute, nu imbricate altele. De asemenea, pentru a garanta că toate documentele sunt reprezentate ar trebui să existe indici secundari (altul decât cel principal _id unul), aceste indicii ar trebui să fie setat ca "necesară".
Poate putin off-topic, dar puteți recursiv pretty-print toate cheile/fields a unui obiect:
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)
}
Util atunci când toate obiectele într-o colecție are aceeași structură.
Putem realiza acest lucru prin Utilizarea mongo js fișiere. Adauga codul de mai jos în getCollectionName.js dosar și a alerga js fișiere în consola de Linux ca mai jos :
mongo, gazda 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();
Multumesc @ackuser
Am extins Carlos LM's soluție puțin, ca's mai detaliate.
Exemplu de 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
}
}
]
};
Tastați în consolă:
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;
}
}
}
Run:
schemafy(db.collection.findOne());
Ieșire
_id <number>
id <number>
t <string>
p <number>
ls <object>:
0 <object>:
l <string>
p <object>:
pp <number>
Am 1 mai simplă lucra în jurul valorii de...
Ce puteți face este în timp ce introducerea de date/documente în colecția principală "lucrurile" trebuie să introduceți atributele în 1 colectarea separată vă permite să spun "things_attributes".
deci, de fiecare dată când introduceți în "lucrurile", puteți obține de la "things_attributes" a compara valorile de acel document cu document nou cheile cazul în care orice nou-cheie prezente adăugați-l în acest document și din nou re-introduceți-l.
Deci things_attributes va avea doar 1 document de chei unice care puteți obține cu ușurință atunci când vreodată aveți nevoie de ajutorul findOne()