MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Interroger et filtrer les noms de clé au lieu des valeurs dans MongoDB

Vous pouvez le faire en utilisant mapReduce :

Pour obtenir uniquement les noms de champs au niveau racine :

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

Cela affichera quelque chose comme ceci :

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

Pour obtenir les noms de champ et tout ou partie (tout le document) de ses valeurs :

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

Cela affichera quelque chose comme ceci :

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

Pour obtenir les noms de champs dans les sous-documents (sans chemin) :

Pour ce faire, vous devrez utiliser 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])
    });
}});

Et changez votre carte en :

function () {
    getFields(this);
}

Exécutez maintenant db.loadServerScripts() pour les charger.

Pour obtenir les noms de champs dans les sous-documents (avec chemin) :

La version précédente renverra simplement les noms de champs, pas le chemin complet pour les obtenir, dont vous aurez besoin si vous voulez renommer ces clés. Pour obtenir le chemin :

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

Et changez votre carte en :

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

Pour exclure les correspondances de chemin qui se chevauchent :

Notez que si vous avez un champ fkfoo.fkbar , il retournera fkfoo et fkfoo.fkbar . Si vous ne voulez pas que les correspondances de chemin se chevauchent, alors :

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

Pour en revenir à votre question, renommer ces champs :

Avec cette dernière option, vous obtenez tous les chemins qui incluent des clés commençant par fk , vous pouvez donc utiliser $rename pour ça.

Cependant, $rename ne fonctionne pas pour ceux qui contiennent des tableaux, donc pour ceux que vous pouvez utiliser forEach pour faire la mise à jour. Voir Champ de renommage de la base de données MongoDB dans le tableau

Remarque sur les performances :

MapReduce n'est pas particulièrement rapide, vous pouvez donc spécifier { out: "fk_fields"} pour afficher les résultats dans une nouvelle collection appelée fk_fields et interrogez ces résultats plus tard, mais cela dépendra de votre cas d'utilisation.

Optimisations possibles pour des cas particuliers (schéma cohérent) :

Aussi, notez que si vous savez que le schéma de vos documents est toujours le même, alors il vous suffit de cocher l'un d'eux pour obtenir ses champs, vous pouvez donc le faire en ajoutant limit: 1 à l'objet options ou simplement récupérer un document avec findOne et la lecture de ses champs au niveau de l'application.