闭社主体 forked from https://github.com/tootsuite/mastodon
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
2.1 KiB

  1. # frozen_string_literal: true
  2. module StatusThreadingConcern
  3. extend ActiveSupport::Concern
  4. def ancestors(account = nil)
  5. find_statuses_from_tree_path(ancestor_ids, account)
  6. end
  7. def descendants(account = nil)
  8. find_statuses_from_tree_path(descendant_ids, account)
  9. end
  10. private
  11. def ancestor_ids
  12. Rails.cache.fetch("ancestors:#{id}") do
  13. ancestors_without_self.pluck(:id)
  14. end
  15. end
  16. def ancestors_without_self
  17. ancestor_statuses - [self]
  18. end
  19. def ancestor_statuses
  20. Status.find_by_sql([<<-SQL.squish, id: id])
  21. WITH RECURSIVE search_tree(id, in_reply_to_id, path)
  22. AS (
  23. SELECT id, in_reply_to_id, ARRAY[id]
  24. FROM statuses
  25. WHERE id = :id
  26. UNION ALL
  27. SELECT statuses.id, statuses.in_reply_to_id, path || statuses.id
  28. FROM search_tree
  29. JOIN statuses ON statuses.id = search_tree.in_reply_to_id
  30. WHERE NOT statuses.id = ANY(path)
  31. )
  32. SELECT id
  33. FROM search_tree
  34. ORDER BY path DESC
  35. SQL
  36. end
  37. def descendant_ids
  38. descendants_without_self.pluck(:id)
  39. end
  40. def descendants_without_self
  41. descendant_statuses - [self]
  42. end
  43. def descendant_statuses
  44. Status.find_by_sql([<<-SQL.squish, id: id])
  45. WITH RECURSIVE search_tree(id, path)
  46. AS (
  47. SELECT id, ARRAY[id]
  48. FROM statuses
  49. WHERE id = :id
  50. UNION ALL
  51. SELECT statuses.id, path || statuses.id
  52. FROM search_tree
  53. JOIN statuses ON statuses.in_reply_to_id = search_tree.id
  54. WHERE NOT statuses.id = ANY(path)
  55. )
  56. SELECT id
  57. FROM search_tree
  58. ORDER BY path
  59. SQL
  60. end
  61. def find_statuses_from_tree_path(ids, account)
  62. statuses = statuses_with_accounts(ids).to_a
  63. # FIXME: n+1 bonanza
  64. statuses.reject! { |status| filter_from_context?(status, account) }
  65. # Order ancestors/descendants by tree path
  66. statuses.sort_by! { |status| ids.index(status.id) }
  67. end
  68. def statuses_with_accounts(ids)
  69. Status.where(id: ids).includes(:account)
  70. end
  71. def filter_from_context?(status, account)
  72. StatusFilter.new(status, account).filtered?
  73. end
  74. end