MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Interroga e filtra i nomi delle chiavi anziché i valori in MongoDB

Puoi farlo usando mapReduce :

Per ottenere solo i nomi dei campi a livello radice:

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

Questo produrrà qualcosa del genere:

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

Per ottenere i nomi dei campi e alcuni o tutti (intero documento) i suoi valori:

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

Questo produrrà qualcosa del genere:

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

Per ottenere i nomi dei campi nei documenti secondari (senza percorso):

Per farlo dovrai usare 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])
    });
}});

E cambia la tua mappa in:

function () {
    getFields(this);
}

Ora esegui db.loadServerScripts() per caricarli.

Per ottenere i nomi dei campi nei documenti secondari (con percorso):

La versione precedente restituirà solo i nomi dei campi, non l'intero percorso per ottenerli, di cui avrai bisogno se quello che vuoi fare è rinominare quelle chiavi. Per ottenere il percorso:

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

E cambia la tua mappa in:

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

Per escludere corrispondenze di percorsi sovrapposte:

Nota che se hai un campo fkfoo.fkbar , restituirà fkfoo e fkfoo.fkbar . Se non vuoi che le corrispondenze di percorso si sovrappongano, allora:

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

Tornando alla tua domanda, rinominando quei campi:

Con quest'ultima opzione, ottieni tutti i percorsi che includono chiavi che iniziano con fk , quindi puoi utilizzare $rename per quello.

Tuttavia, $rename non funziona per quelli che contengono array, quindi per quelli puoi usare forEach per fare l'aggiornamento. Vedere Campo del database di ridenominazione di MongoDB all'interno dell'array

Nota sul rendimento:

MapReduce non è particolarmente veloce, quindi potresti voler specificare { out: "fk_fields"} per generare i risultati in una nuova raccolta chiamata fk_fields e interroga questi risultati in un secondo momento, ma ciò dipenderà dal tuo caso d'uso.

Possibili ottimizzazioni per casi specifici (schema coerente):

Inoltre, nota che se sai che lo schema dei tuoi documenti è sempre lo stesso, devi solo selezionarne uno per ottenere i suoi campi, quindi puoi farlo aggiungendo limit: 1 all'oggetto options o semplicemente recuperando un documento con findOne e leggerne i campi a livello di applicazione.