سؤال

أرغب في الحصول على أسماء جميع المفاتيح في مجموعة MongoDB.

على سبيل المثال ، من هذا:

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

أود الحصول على المفاتيح الفريدة:

type, egg, hello
هل كانت مفيدة؟

المحلول

يمكنك القيام بذلك مع 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"
})

ثم قم بتشغيل متميز على المجموعة الناتجة للعثور على جميع المفاتيح:

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

نصائح أخرى

مع إجابة كريستينا كمصدر إلهام ، قمت بإنشاء أداة مفتوحة المصدر تسمى Variety والتي تفعل هذا بالضبط: https://github.com/variety/variety

يمكنك استخدام التجميع مع جديد $objectToArrray في 3.4.4 الإصدار لتحويل جميع مفاتيح TOP وزوج القيمة إلى صفائف المستندات تليها $unwind & $group مع $addToSet للحصول على مفاتيح مميزة عبر مجموعة كاملة.

$$ROOT للإشارة إلى المستند الأعلى.

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

يمكنك استخدام الاستعلام أدناه للحصول على مفاتيح في مستند واحد.

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

جرب هذا:

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

إذا لم تكن المجموعة المستهدفة كبيرة جدًا ، فيمكنك تجربة ذلك تحت عميل Mongo Shell:

var allKeys = {};

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

allKeys;

باستخدام بيثون. إرجاع مجموعة من جميع المفاتيح ذات المستوى الأعلى في المجموعة:

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

فيما يلي العينة التي تم عملها في بيثون: هذه العينة تُرجع النتائج.

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

حل تم تنظيفه وقابل لإعادة الاستخدام باستخدام 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')

الاستخدام:

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

إذا كنت تستخدم MongoDB 3.4.4 وما فوق ، يمكنك استخدام التجميع أدناه باستخدام $objectToArray و $group تجميع

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

هنا هو العمل مثال

هذا يفي بالغرض بالنسبة لي:

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

أنا مفاجأة ، لا أحد هنا لديه ANS باستخدام بسيط javascript و Set المنطق لتصفية القيم التكرارات تلقائيًا ، مثال بسيط على مونغو قذيفةعلى النحو التالي:

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

هذا سيطبع كل فريد من نوعه مفاتيح في اسم المجموعة: CollectionName.

للحصول على قائمة بجميع المفاتيح ناقص _id, ، فكر في تشغيل خط الأنابيب الإجمالي التالي:

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

أعتقد أن أفضل طريقة تفعل ذلك كما ذكر هنا في Mongod 3.4.4+ ولكن دون استخدام $unwind المشغل واستخدام مرحلتين فقط في خط الأنابيب. بدلا من ذلك يمكننا استخدام $mergeObjects و $objectToArray العاملين.

في ال $group المرحلة ، نستخدم $mergeObjects المشغل لإرجاع مستند واحد يكون فيه المفتاح/القيمة من جميع المستندات في المجموعة.

ثم يأتي $project حيث نستخدم $map و $objectToArray لإرجاع المفاتيح.

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

الآن إذا كان لدينا مستندات متداخلة ونريد الحصول على المفاتيح أيضًا ، فهذا أمر قابل للتنفيذ. من أجل البساطة ، دعنا في الاعتبار وثيقة مع وثيقة مضمنة بسيطة تبدو مثل هذا:

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

خط الأنابيب التالي يعطي جميع المفاتيح (Field1 ، Field2 ، 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"
                        }
                    }
                ]
            }
        }
    }
]

مع القليل من الجهد ، يمكننا الحصول على المفتاح لجميع الحجة الفرعية في حقل صفيف حيث تكون العناصر كائنًا أيضًا.

كنت أحاول الكتابة في Nodejs وأخيراً توصلت إلى هذا:

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

بعد قراءة المجموعة التي تم إنشاؤها حديثًا "AllfieldNames" ، قم بحذفها.

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

حسب مونغولد توثيق, ، مزيج من distinct

يجد القيم المميزة لحقل محدد عبر مجموعة أو عرض واحد ويعيد النتائج في صفيف.

و الفهارس عمليات التجميع هي ما يمكن أن يعيد جميع القيم الممكنة لمفتاح معين ، أو فهرس:

إرجاع مجموعة تحمل قائمة من المستندات التي تحدد وتحدد الفهارس الموجودة على المجموعة

لذلك ، في طريقة معينة ، يمكن للمرء استخدام طريقة مثل الطريقة التالية ، من أجل الاستعلام عن مجموعة لجميع الفهارس المسجلة ، والعودة ، على سبيل المثال كائن مع فهارس المفاتيح (يستخدم هذا المثال Async/في انتظار NodeJs ، ولكن من الواضح أنه يمكنك استخدام أي نهج غير متزامن آخر):

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

حتى الاستعلام عن مجموعة مع الأساسي _id الفهرس ، سيعود ما يلي (مجموعة الاختبار فقط مستند واحد في وقت الاختبار):

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

ضع في اعتبارك أن هذا يستخدم طرقًا محليًا لسائق NodeJS. كما اقترحت بعض الإجابات الأخرى ، هناك مناهج أخرى ، مثل الإطار الكلي. أنا شخصياً أجد هذا النهج أكثر مرونة ، حيث يمكنك بسهولة إنشاء وضبط كيفية إعادة النتائج. من الواضح أن هذا يعالج فقط سمات المستوى الأعلى ، وليس تلك المتداخلة. أيضًا ، لضمان تمثيل جميع المستندات إذا كانت هناك فهارس ثانوية (بخلاف الواحد الرئيسي) ، يجب تعيين تلك الفهارس على أنها required.

ربما خارج عن الموضوع قليلاً ، ولكن يمكنك طباعة جميع المفاتيح/الحقول بشكل متكرر بشكل متكرر:

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

مفيد عندما يكون لجميع الكائنات في المجموعة نفس الهيكل.

يمكننا تحقيق ذلك باستخدام ملف Mongo JS. أضف كود أدناه في الخاص بك getCollectionName.js الملف وتشغيل ملف JS في وحدة التحكم في Linux كما هو موضح أدناه:

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

شكرا @ackuser

باتباع الموضوع من إجابة @James Cropcho ، هبطت على ما يلي التي وجدت أنها سهلة الاستخدام للغاية. إنها أداة ثنائية ، وهو بالضبط ما كنت أبحث عنه:mongoeye.

باستخدام هذه الأداة ، استغرق الأمر حوالي دقيقتين للحصول على مخططتي من سطر الأوامر.

لقد قمت بتوسيع حل Carlos LM قليلاً ، لذا فهو أكثر تفصيلًا.

مثال على المخطط:

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

اكتب في وحدة التحكم:

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

يركض:

schemafy(db.collection.findOne());

انتاج |

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

لدي عمل أبسط حول ...

ما يمكنك القيام به هو أثناء إدخال البيانات/المستند في مجموعتك الرئيسية "الأشياء" يجب عليك إدراج السمات في مجموعة منفصلة واحدة يتيح "الأشياء _attributes".

لذلك في كل مرة تقوم فيها بإدراج في "الأشياء" ، يمكنك الحصول عليها من "Things_attributes" قارن قيم هذا المستند باستخدام مفاتيح المستندات الجديدة إذا كان أي مفتاح جديد يقدمه في هذا المستند وإعادة إدراجه مرة أخرى.

لذا ، سيكون لدى Things_attributes مستند واحد فقط من المفاتيح الفريدة التي يمكنك الحصول عليها بسهولة عندما تحتاج إلى استخدام Findone ()

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top