CouchDB-Map уменьшить аналогично SQL Group by
рассмотрим следующие примеры документов, хранящихся в CouchDB
 {
"_id":....,
"rev":....,
"type":"orders",
"Period":"2013-01",
"Region":"East",
"Category":"Stationary",
"Product":"Pen",
"Rate":1,
"Qty":10,
"Amount":10
}
{
"_id":....,
"rev":....,
"type":"orders",
"Period":"2013-02",
"Region":"South",
"Category":"Food",
"Product":"Biscuit",
"Rate":7,
"Qty":5,
"Amount":35
}
рассмотрим следующий SQL-запрос
SELECT Period, Region,Category, Product, Min(Rate),Max(Rate),Count(Rate), Sum(Qty),Sum(Amount)
FROM Sales
GROUP BY Period,Region,Category, Product;
можно ли создать карту/уменьшить вид в CouchDB эквивалентный запрос SQL и для производства продукции как
[
    {
        "Period":"2013-01",
        "Region":"East",
        "Category":"Stationary",
        "Product":"Pen",
        "MinRate":1,
        "MaxRate":2,
        "OrdersCount":20,
        "TotQty":1000,
        "Amount":1750
    },
    {
    ... 
    }
]
            2 ответов
впереди, я считаю, что ответ @benedolph-лучшая практика и лучший сценарий. В идеале каждое сокращение должно возвращать 1 скалярное значение, чтобы код был максимально простым.
однако, это правда, вам нужно будет выполнить несколько запросов, чтобы получить полный набор результатов, описанный вашим вопросом. Если у вас нет возможности запускать запросы параллельно, или это действительно важно, чтобы сохранить количество запросов вниз можно сделать все это сразу.
Ваша Карта функция останется довольно простой:
function (doc) {
    emit([ doc.Period, doc.Region, doc.Category, doc.Product ], doc);
}
функция reduce будет длительным:
function (key, values, rereduce) {
    // helper function to sum all the values of a specified field in an array of objects
    function sumField(arr, field) {
        return arr.reduce(function (prev, cur) {
            return prev + cur[field];
        }, 0);
    }
    // helper function to create an array of just a single property from an array of objects
    // (this function came from underscore.js, at least it's name and concept)
    function pluck(arr, field) {
        return arr.map(function (item) {
            return item[field];
        });
    }
    // rereduce made this more challenging, and I could not thoroughly test this right now
    // see the CouchDB wiki for more information
    if (rereduce) {
        // a rereduce handles transitionary values
        // (so the "values" below are the results of previous reduce functions, not the map function)
        return {
            OrdersCount: sumField(values, "OrdersCount"),
            MinRate: Math.min.apply(Math, pluck(values, "MinRate")),
            MaxRate: Math.max.apply(Math, pluck(values, "MaxRate")),
            TotQty: sumField(values, "TotQty"),
            Amount: sumField(values, "Amount")
        };
    } else {
        var rates = pluck(values, "Rate");
        // This takes a group of documents and gives you the stats you were asking for
        return {
            OrdersCount: values.length,
            MinRate: Math.min.apply(Math, rates),
            MaxRate: Math.max.apply(Math, rates),
            TotQty: sumField(values, "Qty"),
            Amount: sumField(values, "Amount")
        };
    }
}
Я не смог проверить ветвь "rereduce" этого кода вообще, вам придется сделать это с вашей стороны. (но это должно работать) см.Вики для получения информации о reduce vs rereduce.
вспомогательные функции, которые я добавил вверху, фактически сделали код в целом намного короче и проще для чтения, они в значительной степени зависят от моего опыта с подчеркивания.js. Однако вы не можете включать модули CommonJS в функции уменьшения, поэтому они должны быть написаны вручную.
опять же, в лучшем случае, каждое агрегированное поле должно получить собственный индекс карты/уменьшения, но если это не опция для вас, приведенный выше код должен получить то, что вы описали здесь в вопросе.
Я предложу очень простое решение, которое требует одного представления на переменную, которую вы хотите объединить в своем предложении "select". Хотя, конечно, можно агрегировать все переменные один view, функция уменьшения будет намного сложнее.
проектный документ выглядит следующим образом:
{
    "_id": "_design/ddoc",
    "_rev": "...",
    "language": "javascript",
    "views": {
        "rates": {
            "map": "function(doc) {\n  emit([doc.Period, doc.Region, doc.Category, doc.Product], doc.Rate);\n}",
            "reduce": "_stats"
        },
        "qty": {
            "map": "function(doc) {\n  emit([doc.Period, doc.Region, doc.Category, doc.Product], doc.Qty);\n}",
            "reduce": "_stats"
        }
    }
}
теперь, вы можете сделать запрос <couchdb>/<database>/_design/ddoc/_view/rates?group_level=4 чтобы получить статистику о переменной "Rate". Результат должен выглядеть так это:
{"rows":[
{"key":["2013-01","East","Stationary","Pen"],"value":{"sum":4,"count":3,"min":1,"max":2,"sumsqr":6}},
{"key":["2013-01","North","Stationary","Pen"],"value":{"sum":1,"count":1,"min":1,"max":1,"sumsqr":1}},
{"key":["2013-01","South","Stationary","Pen"],"value":{"sum":0.5,"count":1,"min":0.5,"max":0.5,"sumsqr":0.25}},
{"key":["2013-02","South","Food","Biscuit"],"value":{"sum":7,"count":1,"min":7,"max":7,"sumsqr":49}}
]}
для переменной " Qty " запрос будет <couchdb>/<database>/_design/ddoc/_view/qty?group_level=4.
С group_level свойство вы можете контролировать, на каких уровнях должна выполняться агрегация. Например, запрос с помощью group_level=2 будет агрегироваться до "период " и"регион".