На Хабре часто встречается комментарий о том, что документацию разработчики не дочитывают до конца. Столкнулся с этим сам, когда открыл для себя List-функции в CouchDB.
Мне показался вопрос достаточно сложным и не очень хорошо объясненным в документации, решил поделиться с уважаемым сообществом своим исследованием.
List-функции в design-документах CouchDB нужны для того, чтобы иметь возможность обработать всю базу данных одной функцией. Т.е. это некий аналог Full Table Scan в реляционных базах.
Рассмотрим design-документ из реально работающей инсталляции CouchDB.
CouchDB сильно не любит переводы каретки в функциях, поэтому все функции идут в одну строку. Для лучшей читаемости разверну функцию basicJSON в lists:
Что здесь интересного?
Ключевым звеном является цикл обработки:
Поскольку мы работаем с HTTP, когда обращаемся к CouchDB, то list-функция строит http-ответ. Т.е. по сути генерирует текст в какой-то кодировке. За это отвечает фукнция send(); Она возвращает текстовую строку в http-ответ сервера. Можно использовать конструкцию send(toJSON()); для возврата текстового представления JSON-объектов.
getRow() получает следующую запись в БД. Собственно, на её основе формируется цикл обработки.
Функция start отвечает за формирование заголовка http-ответа.
Вызывается приведённая list-функция так:
http://localhost:5984/requests-db/_design/complete/_list/basicJSON/freq?reduce=false
Указывается имя БД, design-документ, _list, название функции списка, представление. Затем идут параметры представления. Параметры можно указывать так же, как при обращении к любому представлению.
Таким образом, с помощью list-функций можно произвести нужную обработку данных, такую, которая не всегда доступна с помощью Map/Reduce или будет очень сложной на клиенте. С помощью условий в цикле можно часть записей отсеивать, а можно на одну запись делать несколько вызовов send(), что приведет к увеличению количества записей в ответе.
В итоге строится http-текст, содержащий все записи в БД, прошедшие через функциональную обработку.
Да, конечно, это не очень быстрая обработка, но ведь и в реляционных БД full table scan — это самая медленная операция.
List-функции, это очень удобный и полезный механизм, до которого многие не дочитывают в документации. По крайней мере я.
Хорошего вам кода!
PS. В CouchDB есть еще show-функции, но про это как-нибудь в другой раз.
Мне показался вопрос достаточно сложным и не очень хорошо объясненным в документации, решил поделиться с уважаемым сообществом своим исследованием.
List-функции в design-документах CouchDB нужны для того, чтобы иметь возможность обработать всю базу данных одной функцией. Т.е. это некий аналог Full Table Scan в реляционных базах.
Рассмотрим design-документ из реально работающей инсталляции CouchDB.
{ "_id": "_design/complete", "_rev": "2-45c7b0280b529d99b1d34f362e457860", "views": { "freq": { "map": "function(doc) { emit(doc.REQUEST, 1);}", "reduce": "function (key, values, rereduce){return sum(values);}" } }, "lists": { "basicJSON": "function(head, req) { start({headers :{'Content-Type' : 'text/plain;charset=utf-8'}}); send('{\"head\":'+toJSON(head)+', ');send('\"req\":'+toJSON(req)+', ');send('\"rows\":[');var row;var prev = null;while (row = getRow()){if (prev != null && prev.key == row.key) {} else {if (prev != null) { send(',');} send(toJSON({id: row.id, key: row.key}));} prev = row;} send(']}');}" } }
CouchDB сильно не любит переводы каретки в функциях, поэтому все функции идут в одну строку. Для лучшей читаемости разверну функцию basicJSON в lists:
function (head, req) { start({ headers: { 'Content-Type': 'text/plain;charset=utf-8' } }); send('{\"head\":' + toJSON(head) + ', '); send('\"req\":' + toJSON(req) + ', '); send('\"rows\":['); var row; var prev = null; while (row = getRow()) { if (prev != null && prev.key == row.key) {} else { if (prev != null) { send(','); } send(toJSON({ id: row.id, key: row.key })); } prev = row; } send(']}');
Что здесь интересного?
Ключевым звеном является цикл обработки:
var row; ... while (row = getRow()) { ... send(','); ... send(toJSON({ id: row.id, key: row.key })); ... }
Поскольку мы работаем с HTTP, когда обращаемся к CouchDB, то list-функция строит http-ответ. Т.е. по сути генерирует текст в какой-то кодировке. За это отвечает фукнция send(); Она возвращает текстовую строку в http-ответ сервера. Можно использовать конструкцию send(toJSON()); для возврата текстового представления JSON-объектов.
getRow() получает следующую запись в БД. Собственно, на её основе формируется цикл обработки.
Функция start отвечает за формирование заголовка http-ответа.
Вызывается приведённая list-функция так:
http://localhost:5984/requests-db/_design/complete/_list/basicJSON/freq?reduce=false
Указывается имя БД, design-документ, _list, название функции списка, представление. Затем идут параметры представления. Параметры можно указывать так же, как при обращении к любому представлению.
Таким образом, с помощью list-функций можно произвести нужную обработку данных, такую, которая не всегда доступна с помощью Map/Reduce или будет очень сложной на клиенте. С помощью условий в цикле можно часть записей отсеивать, а можно на одну запись делать несколько вызовов send(), что приведет к увеличению количества записей в ответе.
В итоге строится http-текст, содержащий все записи в БД, прошедшие через функциональную обработку.
Да, конечно, это не очень быстрая обработка, но ведь и в реляционных БД full table scan — это самая медленная операция.
List-функции, это очень удобный и полезный механизм, до которого многие не дочитывают в документации. По крайней мере я.
Хорошего вам кода!
PS. В CouchDB есть еще show-функции, но про это как-нибудь в другой раз.
