CouchDB: группировка данных по родительскому элементу

При разработке своего одного приложения (скоро анонсирую!) я использовал в качестве БД Apache CouchDB, точнее использовал его чуть более продвинутую версию от коммерческой организации – CouchBase. В этом посте я привожу решение нередкой, но нетривиально решаемой задачи (нетривиально для людей, не знакомых до этого с NoSQL).

Задача

Возврат сгруппированных значений по родительскому элементу.

В БД обычно данные хранятся в примерно таком формате:

{
   "_id": "256b9dd4492210351c9db34ee500d817",
   "_rev": "1-94366c1d2b7a075f33ebbd7a96565f4b",
   "url": "http://surdoserver.ru",
   "status": 200,
   "timestamp": 1969072012,
   "ping": 332
}
{
   "_id": "256b9dd4492210351c9db34ee500f288",
   "_rev": "1-1770b12a395ca76887c792e5c1d99f8f",
   "url": "http://surdoserver.ru",
   "status": 200,
   "timestamp": 1969073012,
   "ping": 220
}
и т.д.

Нам же необходимо получить статистику по каждому сайту без дублирования информации, то есть что-то типа такого:

{
   "url": "http://surdoserver.ru",
   "data":
   [
      "status": 200,
      "timestamp": 1969072012,
      "ping": 332
   ],
   [
     "status": 200,
     "timestamp": 1969073352,
     "ping": 220
   ]
}

Решение

Для этого необходимо использовать list-функции, обрабатывающие результаты работы view-функций. View-функция для выборки всех сайтов с их данными выглядит просто:

function(doc) {
  if (doc.url)
  	emit(doc.url, doc);
}

List-функция по-сложнее, но не сильно:

function(head, req) {
  var row, site, url = "", stats = [], sendData = [];
  site = getRow();
  while (row = getRow()) {
	  if (row.value.url != site.value.url) {
		 //last stat of current site
		  sendData.push({"url" : site.value.url, "data" :stats});
		  site = row;
		  stats = [];
	  } else {
		  stats.push({
			"timestamp": row.value.timestamp,
			"status": row.value.status,
			"ping":	row.value.ping
		  });
	  }
  }
  sendData.push({"url" : site.value.url, "data" :stats});
  send(JSON.stringify(sendData));
}

За основу решения я взял главу из книги «CouchDB: The Devinitive Guide».