Как эффективно хранить и извлекать вложенную структуру произвольной глубины в postgres? - PullRequest
2 голосов
/ 06 января 2012

В моем приложении ruby-on-rails я вложил комментарии, которые могут быть произвольной длины.

Я пробовал разные способы хранения этого:

Использование самостоятельных объединений:

belongs_to :parent, :class_name => 'Comment', :foreign_key => 'parent_id'
has_many :children, :class_name => 'Comment', :foreign_key => "parent_id"

Использование драгоценного камня предков

и т.д.

Проблема, однако, в том, что независимо от того, что я использую, всегда будет линейное число операторов SQL. (1 оператор для получения всех корневых комментариев, затем 1 оператор для дочерних элементов каждого корня, а затем 1 оператор для всех дочерних элементов этого и т. Д.)

Есть ли более эффективный способ сделать это?

Postgres 9.1, но, надеюсь, предпочтительны решения, обратно совместимые.

Ответы [ 2 ]

3 голосов
/ 06 января 2012

Вы можете придерживаться указательного столбца parent_id и использовать find_by_sql и запрос WITH RECURSIVE и позволить базе данных выполнять всю работу за один раз. Примерно так:

comments = Comment.find_by_sql(%Q{
    with recursive tree(id) as (
        select c.id, c.column1, ...
        from comments c
        where c.id in (#{roots.join(',')})
        union all
        select c.id, c.column1, ...
        from comments c
        join tree on c.parent_id = tree.id
    )
    select id, column1, ...
    from tree
})

где roots будет массивом Ruby, содержащим id s корневых узлов, которые вас интересуют. Это даст вам все узлы в представляющих интерес поддеревьях в качестве экземпляров Comment. Я использовал подобные запросы в прошлом, и WITH RECURSIVE был более чем в два раза быстрее, чем ваш итеративный метод, даже с мелкими деревьями, я бы предположил, что более глубокие деревья будут видеть еще лучшие ускорения.

Используемая вами структура parent_id очень удобна для большинства вещей и вполне соответствует тому, как ActiveRecord хочет работать. Кроме того, сохранение вашей текущей структуры означает, что вы можете оставить остальную часть своего приложения в покое.

WITH RECURSIVE доступно в PostgreSQL 8.4 и выше.

1 голос
/ 06 января 2012

Взгляните на awesome_nested_set, думаю, вам понравится.

https://github.com/collectiveidea/awesome_nested_set

...