MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Abfragen und Filtern von Schlüsselnamen anstelle von Werten in MongoDB

Sie können dies mit mapReduce tun :

So erhalten Sie nur die Feldnamen auf Stammebene:

db.collection.mapReduce(function () {
    Object.keys(this).map(function(key) {
        if (key.match(/^fk/)) emit(key, null);

        // OR: key.indexOf("fk") === 0
    });
}, function(/* key, values */) {
    // No need for params or to return anything in the 
    // reduce, just pass an empty function.
}, { out: { inline: 1 }});

Dies wird etwa Folgendes ausgeben:

{
    "results": [{
        "_id": "fkKey1",
        "value": null
    }, {
        "_id": "fkKey2",
        "value": null
    }, {
        "_id": "fkKey3",
        "value": null
    }],
    "timeMillis": W,
    "counts": {
        "input": X,
        "emit": Y,
        "reduce": Z,
        "output": 3
    },
    "ok" : 1
}

So erhalten Sie Feldnamen und einige oder alle (gesamtes Dokument) seiner Werte:

db.test.mapReduce(function () {
    var obj = this;

    Object.keys(this).map(function(key) {
        // With `obj[key]` you will get the value of the field as well.
        // You can change `obj[key]` for:
        //  - `obj` to return the whole document.
        //  - `obj._id` (or any other field) to return its value.

        if (key.match(/^fk/)) emit(key, obj[key]);
    });
}, function(key, values) {
    // We can't return values or an array directly yet:

    return { values: values };
}, { out: { inline: 1 }});

Dies wird etwa Folgendes ausgeben:

{
    "results": [{
        "_id": "fkKey1",
        "value": {
            "values": [1, 4, 6]
        }
    }, {
        "_id": "fkKey2",
        "value": {
            "values": ["foo", "bar"]
        }
    }],
    "timeMillis": W,
    "counts": {
        "input": X,
        "emit": Y,
        "reduce": Z,
        "output": 2
    },
    "ok" : 1
}

Um Feldnamen in Unterdokumenten zu erhalten (ohne Pfad):

Dazu müssen Sie store JavaScript functions on the Server :

db.system.js.save({ _id: "hasChildren", value: function(obj) {
    return typeof obj === "object";
}});

db.system.js.save({ _id: "getFields", value: function(doc) {
    Object.keys(doc).map(function(key) {
        if (key.match(/^fk/)) emit(key, null);

        if (hasChildren(doc[key])) getFields(doc[key])
    });
}});

Und ändern Sie Ihre Karte in:

function () {
    getFields(this);
}

Führen Sie nun db.loadServerScripts() aus um sie zu laden.

So erhalten Sie Feldnamen in Unterdokumenten (mit Pfad):

Die vorherige Version gibt nur Feldnamen zurück, nicht den gesamten Pfad, um sie abzurufen, was Sie benötigen, wenn Sie diese Schlüssel umbenennen möchten. So erhalten Sie den Pfad:

db.system.js.save({ _id: "getFields", value: function(doc, prefix) {
    Object.keys(doc).map(function(key) {
        if (key.match(/^fk/)) emit(prefix + key, null);

        if (hasChildren(doc[key]))
            getFields(doc[key], prefix + key + '.')
    });
}});

Und ändern Sie Ihre Karte in:

function () {
    getFields(this, '');
}

So schließen Sie überlappende Pfadübereinstimmungen aus:

Beachten Sie, dass wenn Sie ein Feld fkfoo.fkbar haben , wird fkfoo zurückgegeben und fkfoo.fkbar . Wenn Sie keine überlappenden Pfadübereinstimmungen wünschen, dann:

db.system.js.save({ _id: "getFields", value: function(doc, prefix) {
    Object.keys(doc).map(function(key) {
        if (hasChildren(doc[key]))
            getFields(doc[key], prefix + key + '.')
        else if (key.match(/^fk/)) emit(prefix + key, null);
    });
}});

Um auf Ihre Frage zurückzukommen und diese Felder umzubenennen:

Mit dieser letzten Option erhalten Sie alle Pfade, die Schlüssel enthalten, die mit fk beginnen , sodass Sie $rename verwenden können dafür.

Allerdings $rename funktioniert nicht für diejenigen, die Arrays enthalten, also könnten Sie für diese verwenden forEach um das Update zu machen. Siehe MongoDB-Umbenennungsdatenbankfeld innerhalb des Arrays

Leistungshinweis:

MapReduce ist nicht besonders schnell gedacht, daher sollten Sie { out: "fk_fields"} angeben um die Ergebnisse in eine neue Sammlung namens fk_fields auszugeben und diese Ergebnisse später abfragen, aber das hängt von Ihrem Anwendungsfall ab.

Mögliche Optimierungen für bestimmte Fälle (konsistentes Schema):

Beachten Sie auch, dass Sie, wenn Sie wissen, dass das Schema Ihrer Dokumente immer dasselbe ist, nur eines von ihnen überprüfen müssen, um seine Felder zu erhalten, sodass Sie dies tun können, indem Sie limit: 1 hinzufügen zum options-Objekt oder einfach ein Dokument mit findOne und Lesen seiner Felder in der Anwendungsebene.