sql >> Databasteknik >  >> NoSQL >> MongoDB

Fråga och filtrera nyckelnamn istället för värden i MongoDB

Du kan göra det med mapReduce :

För att få bara fältnamnen på rotnivå:

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

Detta kommer att mata ut något så här:

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

För att få fältnamn och alla eller alla (hela dokumentet) dess värden:

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

Detta kommer att mata ut något så här:

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

För att få fältnamn i underdokument (utan sökväg):

För att göra det måste du använda 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])
    });
}});

Och ändra din karta till:

function () {
    getFields(this);
}

Kör nu db.loadServerScripts() för att ladda dem.

För att få fältnamn i underdokument (med sökväg):

Den tidigare versionen kommer bara att returnera fältnamn, inte hela sökvägen för att få dem, vilket du behöver om det du vill göra är att byta namn på dessa nycklar. Så här får du sökvägen:

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

Och ändra din karta till:

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

Så här utesluter du överlappande sökvägsmatchningar:

Observera att om du har ett fält fkfoo.fkbar , returnerar den fkfoo och fkfoo.fkbar . Om du inte vill ha överlappande sökvägsmatchningar:

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

Återgår till din fråga och byter namn på dessa fält:

Med det här sista alternativet får du alla sökvägar som inkluderar nycklar som börjar med fk , så att du kan använda $rename för det.

Men $rename fungerar inte för de som innehåller arrayer, så för dem kan du använda forEach för att göra uppdateringen. Se MongoDB byt namn på databasfält inom array a>

Prestandanteckning:

MapReduce är inte speciellt snabbt tänkt, så du kanske vill ange { out: "fk_fields"} för att mata ut resultaten till en ny samling som heter fk_fields och fråga efter dessa resultat senare, men det beror på ditt användningsfall.

Möjliga optimeringar för specifika fall (konsekvent schema):

Observera också att om du vet att schemat för dina dokument alltid är detsamma behöver du bara kontrollera ett av dem för att få dess fält, så att du kan göra det genom att lägga till limit: 1 till optionsobjektet eller bara hämta ett dokument med findOne och läsa dess fält på applikationsnivå.



  1. StackExchange.Redis.RedisTimeoutException:Timeout väntar på svar

  2. konfigurera redis auth på sidekiq

  3. Räkna effektivt procent av förekomsten i MongoDB

  4. 3 sätt att sortera dokument i MongoDB