Получение списка документов с максимальным значением поля из представления CouchDB

#couchdb #mapreduce

#couchdb #mapreduce

Вопрос:

Допустим, у меня есть записи в блоге, подобные этим, в моей базе данных CouchDB:

{"имя": "Мэри", "дата публикации": "20110412", "тема": "это", "сообщение": "бла"}
{"имя": "Джо", "дата публикации": "20110411", "тема": "это", "сообщение": "блядь"}
{"имя": "Мэри", "дата публикации": "20110411", "тема": "и это", "сообщение": "бла-бла"}
{"имя": "Джо", "дата публикации": "20110410", "тема": "И другое", "сообщение": "бла-бла-бла"}
{"имя": "Джейн", "дата публикации": "20110409", "тема": "Серьезный материал", "сообщение": "Не совсем"}

Получить список всех записей довольно просто. Но как мне получить список последних сообщений от всех пользователей?

Вот так:

{"имя": "Мэри", "дата публикации": "20110412", "тема": "это", "сообщение": "бла"}
{"имя": "Джо", "дата публикации": "20110411", "тема": "это", "сообщение": "блядь"}
{"имя": "Джейн", "дата публикации": "20110409", "тема": "Серьезный материал", "сообщение": "Не совсем"}

Ответ №1:

Попробуйте использовать эту функцию map:

 function(doc) {
  if (doc.postdate amp;amp; doc.name) {
    emit([doc.name, doc.postdate], 1);
  }
}
  

и следующая функция уменьшения:

 function(keys, values, rereduce) {
  var max = 0,
      ks = rereduce ? values : keys;

  for (var i = 1, l = ks.length; i < l;   i) {
    if (ks[max][0][1] < ks[i][0][1]) max = i;
  }
  return ks[max];
}
  

и запрашиваем его с помощью group_level=1 . Он выдает вам _id количество записей, затем вы можете получить их все с помощью одного запроса с keys параметром или с помощью POST.

Я не уверен, что это лучший подход, но, похоже, он работает.

ОБНОВЛЕНИЕ: исправлено, что карта неправильно обрабатывала повторное получение.

Комментарии:

1. Я пробовал что-то подобное. Это то, что я получаю взамен при доступе к представлению: «ошибка»:»reduce_overflow_error»,»причина»: «Уменьшенный вывод должен сокращаться быстрее: текущий вывод:… Может быть, потому, что мои документы на самом деле довольно большие, около 7-10 тыс.

2. Я только что попробовал загрузить 30000 документов, и у меня это работает. Но я использовал только четыре разных doc.name файла. Может быть, у вас много пользователей с несколькими публикациями на пользователя? Вы пробовали обновленную версию?

3. Пожалуйста, обратите внимание, что я также обновил функцию map до emit([doc.name, doc.postdate], 1) , а не emit([doc.name, doc.postdate], doc) .

Ответ №2:

Вы собираетесь использовать postdate в качестве ключа, потому что ключи отсортированы. Например, вот как будет выглядеть ваша функция map…

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

Это даст вам все документы, отсортированные по возрастанию по дате публикации. Если вы хотите по убыванию, то выполните запрос с ?descending=true

Приветствия.

Комментарии:

1. К сожалению, это включало бы все сообщения, скажем, от Мэри, в то время как я ищу только последнее. Не требует ли он слишком многого от CouchDB?

2. Вы можете сделать это без проблем! Просто убедитесь, что вы переходите по убыванию, и вызовите URL-адрес, используя параметр limit, следующим образом: ?descending=trueamp;limit=1 . Приветствия.

3. А, я понимаю: вам нужны последние данные для каждого пользователя, а не последние в целом. Это возможно только при выполнении нескольких вызовов и использовании сложных ключей. Например, ваша ключевая структура была бы такой, [doc.name, doc.postdate] и затем вы могли бы запросить ?startkey=["Mary"]amp;endkey=["Mary",{}]amp;descending=trueamp;limit=1 , чтобы получить последнюю запись Мэри.

4. Спасибо, Сэм. Я думаю, это отвечает на мой вопрос. К сожалению, мне приходится использовать несколько вызовов. Это невозможно, когда база данных насчитывает около миллиона пользователей.

5. Если это так, то вы могли бы самостоятельно поддерживать список в дополнительном документе. Дайте ему _id «latestPosts» или что-то в этом роде. Также взгляните на couchdb-lucene для получения дополнительной, более традиционной функциональности запросов. Приветствия.