группировка комментариев по родительскому объекту, упорядочение родительского объекта по самому старому комментарию - PullRequest
2 голосов
/ 04 июня 2010

У меня есть объекты, у которых есть комментарии.Как часть периодической сводки по электронной почте, я хочу определить комментарии для периода и сначала представить объекты в порядке самого старого закомментированного объекта.

Данные:

object_id  comment_id
30         40
40         42
32         41
30         43
32         44

Вывод:

Объект # 30

  • комментарий 40
  • комментарий 43

Объект #40

  • комментарий 42

Объект # 32

  • комментарий 41
  • комментарий 44

Я использую этот код для передачи данных в промежуточный массив - я пытался получить все это одним махом, используя .group_by (&: commentable_id), но данные вышли не в правильном порядке.

comments = account.comments.all( 
  :conditions => ["comments.created_at > ?", 8.hours.ago],
  :order => "comments.created_at asc" ).map { |c| [c.commentable_id,c.id] }

=>  [ [30,40], [40,42], [32,41], [30,43], [32,44] ]

Если бы я смог преобразовать эти данные в следующую форму, я мог бы просто перебрать массив для создания содержимого электронной почты ...

[ [30,[40,43]], [40,[42]], [32,[41,44]] ]

Но мне интересно, смогу ли яЯ делаю это сложнее, чем мне нужно ... Любой совет?

(я использую Rails 2.3 и Ruby ree-1.8.7)

Ответы [ 3 ]

1 голос
/ 04 июня 2010

Вы можете использовать группу с агрегатом массива, чтобы добраться до нужной формы массива.

Агрегаты массивов в значительной степени зависят от базы данных. MySQL это GROUP_CONCAT. Postgres 'это ARRAY_AGG. В Sqlite его нет, но я знаю, что вы можете определять пользовательские агрегатные функции, так что это не невозможно.

На самом деле не пытался запустить этот код, но вот что-то, что должно указать вам правильное направление:

result = Object.all(
  :select => 'objects.id, GROUP_CONCAT(comment_id) AS comment_array',
  :group => 'comments.id'
).map { |c| [c.id, c.comment_array] }

Я использовал наименование из первого примера, поэтому вам нужно изменить «объект» на имя вашей таблицы. Надеюсь, это имеет смысл. Rails, вероятно, не имеет встроенной поддержки для разбора массива, поэтому он, вероятно, вернет строку для comment_array, и вам, возможно, придется ее проанализировать.

1 голос
/ 04 июня 2010

Наличие всех комментариев для одного объекта в одном блоке / элементе определенно облегчит жизнь при выполнении любой операции над ними. Однако я не буду превращать их в массив массивов массивов, потому что это уже массив массивов. Я бы предпочел создать хеш так:

comments_array = [ [30,40], [32,41], [40,42], [30,43], [32,44] ]
obj_with_comments = {}
comments_array.each do |x|
  obj_with_comments[x.first] ||= []
  obj_with_comments[x.first] << x.last
end

obj_with_comments #=> {40=>[42], 30=>[40, 43], 32=>[41, 44]}

Но это создает еще одну проблему, которая заключается в том, что хэши не упорядочены, поэтому вы теряете свой порядок случайным образом, если просто перебираете хеш. Однако вы можете создать массив объектов, а затем выполнить итерации по хешу следующим образом:

objects = comments_array.collect{|x| x.first}.uniq
objects #=> [30, 32, 40]

# now get the hash value for each object key in order
objects.each { |obj| puts obj_with_comments[obj].inspect }

Надеюсь, что это имеет смысл.

0 голосов
/ 04 июня 2010

Попробуйте это:

comments = account.comments.all(
  :conditions => ["comments.created_at > ?", 8.hours.ago],
  :order => "comments.commentable_id ASC, comments.id ASC" 
).map { |c| [c.commentable_id,c.id] }

Это вернет следующий набор результатов:

=>  [ [30,40], [30,43], [32,41], [32,44], [40,42] ]

В приведенном выше запросе я использую id для сортировки вместо create_at. Если вы используете MySQL и идентификаторы создаются автоматически, эта логика будет работать, так как id нового объекта будет выше, чем id более старого объекта. Если вы не разрешите редактирование комментариев, тогда эта логика будет работать.

Если вы хотите явно отсортировать по датам, используйте следующий синтаксис:

comments = account.comments.all(
  :joins => "accounts AS accounts ON comments.commentable_type = 'Account' AND
                                     comments.commentable_id  = accounts.id", 
  :conditions => ["comments.created_at > ?", 8.hours.ago],
  :order => "accounts.id ASC, comments.id ASC" 
).map { |c| [c.commentable_id,c.id] }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...