Koleksiyondaki tüm anahtarların adlarını alma


322

Bir MongoDB koleksiyonundaki tüm anahtarların adlarını almak istiyorum.

Örneğin, bundan:

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

Benzersiz anahtarları almak istiyorum:

type, egg, hello

Yanıtlar:


346

MapReduce ile bunu yapabilirsiniz:

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

Ardından, tüm anahtarları bulmak için ortaya çıkan koleksiyonda farklı çalışın:

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

2
Merhaba! Bu soru parçasını, veri yapısında daha derin seviyelerde bulunan tuşlarla bile nasıl çalıştıracağını soran bir soru gönderdim ( stackoverflow.com/questions/2997004/… ).
Andrea Fiore

1
@kristina: Bunu eşya koleksiyonunda kullanırken anahtarlarla listelenen tüm şeyleri almam nasıl mümkün olabilir . Tarih mekanizması ile ilgili görünüyor çünkü geçmişte değiştirdiğim şeyleri alıyorum ..
Shawn

3
Bu eski bir iplik olduğunu biliyorum, ama benzer bir ihtiyaç var gibi görünüyor. Nodejs mongodb yerli sürücüsünü kullanıyorum. Ortaya çıkan geçici koleksiyon her zaman boş görünüyor. Bunun için koleksiyon sınıfındaki mapreduce işlevini kullanıyorum. Bu mümkün değil mi?
Deepak

6
Bu açık olabilir, ancak bir alt for (var key in this.first_level.second_level.nth_level) { emit(key, null); }
belgedeki

3
Bir koleksiyona kaydetmek ve daha sonra bunun üzerinde farklı çalışmak yerine, map () kullanıyorum:db.runCommand({..., out: { "inline" : 1 }}).results.map(function(i) { return i._id; });
Ian Stanley


74

Yeni ile kümelenme kullanabilirsiniz $objectToArrrayiçinde 3.4.4izledi belge diziler içine tüm üst anahtarını & değer çiftini dönüştürmek için sürümüyle $unwind& $group ile $addToSetkoleksiyonunuzun tamamındaki farklı anahtarlarını almak için.

$$ROOT üst düzey belgeye gönderme için.

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

Anahtarları tek bir belgede almak için aşağıdaki sorguyu kullanabilirsiniz.

db.things.aggregate([
  {"$match":{_id: "5e8f968639bb8c67726686bc"}}, /* Replace with the document's ID */
  {"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
  {"$project":{"keys":"$arrayofkeyvalue.k"}}
])

20
Bu gerçekten en iyi cevap. Sorunu başka bir programlama dili veya paketi içermeden çözer ve toplam çerçeveyi (hatta Meteor!) Destekleyen tüm sürücülerle çalışır
Micah Henning

2
"Allkeys" tuşuyla tek bir harita girişi içeren bir imleç yerine bir dizi döndürmek istiyorsanız .next()["allkeys"], komuta ekleyebilirsiniz (koleksiyonda en az bir öğe olduğu varsayılarak).
M. Justin

19

Bunu dene:

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

49
yanlış yanıt, çünkü bu yalnızca bir koleksiyondaki tek bir belgenin alanlarını çıkarır - diğerlerinin tamamında farklı anahtarlar olabilir.
Asya Kamsky

15
Bu hala benim için en yararlı cevap, basit ve makul bir minimum.
Boris Burkov

11
Yararlı değil mi? Size yanlış cevap verirse nasıl faydalı olur?
Zlatko

4
Bağlam neyin yararlı olduğunu gösterir: eğer veri normalleştirilirse (örn. CSV dosyasından origen), faydalıdır ... SQL'den içe aktarılan veriler için kullanışlıdır.
Peter Krauss

5
iyi bir cevap değil, koleksiyondaki tüm anahtarların değil, koleksiyondaki bir öğenin anahtarlarının nasıl alınacağıyla ilgili bir cevaptır !
yonatan

16

Hedef koleksiyonunuz çok büyük değilse, bunu mongo kabuğu istemcisi altında deneyebilirsiniz:

var allKeys = {};

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

allKeys;

burada nasıl görmek isterseniz belirli anahtarlar için regExp verebilir?
TB.M

@ TB.M bunu deneyebilirsiniz: db.configs.find (). ForEach (function (doc) {Object.keys (doc) .forEach (function (key) {if (/YOURREGEXP/.test(key)) { AllKeys [anahtar] = 1}})});
Li Chunlin

test burada ne anlama geliyor? lütfen açıklayabilir misin
TB.M


14

Pymongo kullanarak temizlenmiş ve tekrar kullanılabilir bir çözelti:

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

Kullanımı:

get_keys('dbname', 'collection')
>> ['key1', 'key2', ... ]

1
Harika çalışıyor. Sonunda benim sorun çözüldü var .... Bu yığın taşması gördüm en basit çözüm ..
Smack Alpha

Ve türe göre filtrelemek için, örneğin if (typeof(this[key]) == 'number')önce ekleyin emit(key, null).
Skippy le Grand Gourou

10

Python kullanma. Koleksiyondaki tüm üst düzey anahtar kümesini döndürür:

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

1
Çalışmak için buldum ama ham mongod sorgu ile karşılaştırıldığında ne kadar verimli?
Jesus Gomez

1
Bunun doğrudan Mongodb'da yapmasına kıyasla son derece verimsiz olduğuna eminim
Ingo Fischer

9

İşte Python'da çalışan örnek: Bu örnek sonuçları satır içi döndürür.

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

9

Mongodb 3.4.4 ve üstünü kullanıyorsanız, aşağıdaki toplama $objectToArrayve $grouptoplama işlemlerini kullanabilirsiniz.

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

İşte çalışma örneği


Bu en iyi cevap. $matchToplama boru hattının başlangıcında, yalnızca bir koşulla eşleşen belgelerin anahtarlarını almak için de kullanabilirsiniz .
RonquilloAeon

5

Sürprizim, burada hiç kimse otomatik olarak aşağıdaki değerleri kopyalamak için basit javascriptve Setmantık kullanarak ans yoktur , aşağıdaki gibi mongo kabuğu üzerinde basit bir örnek :

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

Bu , toplama adındaki olası tüm benzersiz anahtarları yazdırır : collectionName .


3

Bu benim için iyi çalışıyor:

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

3

Burada belirtildiği gibi bunu yapmanın en iyi yolu mongod 3.4.4+ olduğunu düşünüyorum ama $unwindoperatörü kullanmadan ve boru hattında sadece iki aşama kullanmadan. Bunun yerine $mergeObjectsve $objectToArrayoperatörlerini kullanabiliriz .

In $groupaşamada, kullandığımız $mergeObjectsanahtar / değer koleksiyonundaki tüm belgeler nereli tek belge dönmek operatörü.

Sonra $projectkullandığımız yer $mapve $objectToArrayanahtarları iade etmek.

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

Eğer iç içe geçmiş bir dokümanımız varsa ve anahtarları da almak istiyorsak, bu yapılabilir. Basit olması için, aşağıdaki gibi basit gömülü belgeye sahip bir belgeyi ele alalım:

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

Aşağıdaki boru hattı tüm anahtarları verir (alan1, alan2, alan3, alan4).

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

Biraz çaba sarf ederek, öğelerin de nesne olduğu bir dizi alanındaki tüm alt belgelerin anahtarını alabiliriz.


Evet $unwind(alanların docs Kataloğundaki * Kataloğundaki), biz kullanarak bu önleyebilirsiniz koleksiyonunu patlayacak $mergeObjectstüm sürümlerinde> 3.6.. Aynı, Meli hayatım daha kolay olurdu, bu cevap daha önce gördüğüm mü bu şekilde ( -_-)
whoami

3

Biraz konu dışı olabilir, ancak bir nesnenin tüm anahtarlarını / alanlarını tekrar tekrar güzel bir şekilde yazdırabilirsiniz:

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

Bir koleksiyondaki tüm nesneler aynı yapıya sahip olduğunda kullanışlıdır.


1

Tüm anahtarların eksi bir listesini almak _idiçin aşağıdaki toplu ardışık düzeni çalıştırmayı düşünün:

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

0

Düğümlerde yazmaya çalışıyordum ve sonunda bununla geldim:

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

Yeni oluşturulan "allFieldNames" koleksiyonunu okuduktan sonra silin.

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

0

Mongoldb belgelerine göre ,distinct

Tek bir koleksiyon veya görünümdeki belirli bir alan için ayrı değerleri bulur ve sonuçları bir dizide döndürür.

ve dizin toplama işlemleri, belirli bir anahtar veya dizin için tüm olası değerleri döndürecek işlemlerdir:

Koleksiyondaki mevcut dizinleri tanımlayan ve tanımlayan belgelerin listesini içeren bir dizi döndürür

Bu nedenle, belirli bir yöntemde, bir koleksiyonu kayıtlı tüm dizinleri için sorgulamak ve geri dönmek için anahtar dizinleri olan bir nesne söyleyin (bu örnek NodeJS için async / await kullanır, ancak Açıkçası başka bir eşzamansız yaklaşım kullanabilirsiniz):

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

Bu nedenle, bir koleksiyonu temel _iddizinle sorgulamak aşağıdakileri döndürür (test koleksiyonunda test sırasında yalnızca bir belge bulunur):

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

Dikkat edin, bu NodeJS Sürücüsüne özgü yöntemleri kullanır. Diğer bazı cevapların da belirttiği gibi, toplam çerçeve gibi başka yaklaşımlar da vardır. Sonuçların nasıl iade edileceğini kolayca oluşturabileceğiniz ve ince ayar yapabileceğiniz için bu yaklaşımı daha esnek buluyorum. Açıkçası, bu yalnızca üst düzey niteliklere yöneliktir, iç içe olanlara değil. Ayrıca, tüm belgelerin ikincil dizinler (ana _id dışındakiler) olması durumunda temsil edilmesini sağlamak için, bu dizinler olarak ayarlanmalıdır required.


0

Bunu mongo js dosyasını kullanarak başarabiliriz. GetCollectionName.js dosyanıza aşağıdaki kodu ekleyin ve aşağıdaki gibi Linux konsolunda js dosyasını çalıştırı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();

Teşekkürler @ackuser


0

@James Cropcho'nun cevabından sonra, kullanımı çok kolay bulduğum aşağıdakilere indi. Tam aradığım şey olan ikili bir araçtır: mongoeye .

Bu aracı kullanarak şemanın komut satırından dışa aktarılması yaklaşık 2 dakika sürdü.


0

Bu sorunun 10 yaşında olduğunu biliyorum ama C # çözümü yok ve bu beni anlamaya saatler sürdü. .NET sürücüsünü kullanıyorum System.Linqve anahtarların bir listesini döndürmek için.

var map = new BsonJavaScript("function() { for (var key in this) { emit(key, null); } }");
var reduce = new BsonJavaScript("function(key, stuff) { return null; }");
var options = new MapReduceOptions<BsonDocument, BsonDocument>();
var result = await collection.MapReduceAsync(map, reduce, options);
var list = result.ToEnumerable().Select(item => item["_id"].ToString());

-1

Carlos LM'nin çözümünü biraz daha genişlettim, böylece daha ayrıntılı.

Şema örneği:

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

Konsola yazın:

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

Çalıştırmak:

schemafy(db.collection.findOne());

Çıktı

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

3
onun yanıtı yanlış ve bunun üzerine inşa ettiniz. bütün mesele çıkışına olduğu tüm alanları tüm belgeler değil, her bir sonraki olandan farklı alanlara sahip olabilir ilk belge.
Asya Kamsky

-3

1 basit işim var ...

Yapabileceğiniz şey veri / belge eklerken ana şeyler "şeyler" eklemek gerekir özellikleri 1 ayrı bir koleksiyon "things_attributes" diyelim.

"şeyleri" her eklediğinizde, "things_attributes" ifadesinden alırsanız, yeni bir anahtar varsa o belgeye ekleyip tekrar eklerseniz, o belgenin değerlerini yeni belge anahtarlarınızla karşılaştırırsınız.

Böylece things_attributes, findOne () kullanarak istediğiniz zaman kolayca alabileceğiniz yalnızca 1 benzersiz anahtar belgesine sahip olacaktır.


Tüm anahtarlar için sorguların sık ve eklerin sık olmadığı birçok girdiye sahip veritabanları için, "tüm anahtarları al" sorgusunun sonucunun önbelleğe alınması mantıklı olacaktır. Bunu yapmanın bir yolu budur.
Scott
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.