Add optimizations to reduce the number of relationships queries
This commit is contained in:
parent
83392afa63
commit
788289011f
15 changed files with 159 additions and 263 deletions
|
@ -19,6 +19,6 @@ class Api::V1::Accounts::RelationshipsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_ids
|
def account_ids
|
||||||
Array(params[:id]).map(&:to_i)
|
Array(params[:id]).uniq.map(&:to_i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,7 +39,13 @@ class Api::V1::AccountsController < Api::BaseController
|
||||||
|
|
||||||
def follow
|
def follow
|
||||||
follow = FollowService.new.call(current_user.account, @account, reblogs: params.key?(:reblogs) ? truthy_param?(:reblogs) : nil, notify: params.key?(:notify) ? truthy_param?(:notify) : nil, delivery: params.key?(:delivery) ? truthy_param?(:delivery) : nil, with_rate_limit: true)
|
follow = FollowService.new.call(current_user.account, @account, reblogs: params.key?(:reblogs) ? truthy_param?(:reblogs) : nil, notify: params.key?(:notify) ? truthy_param?(:notify) : nil, delivery: params.key?(:delivery) ? truthy_param?(:delivery) : nil, with_rate_limit: true)
|
||||||
options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: follow.show_reblogs?, notify: follow.notify?, delivery: follow.delivery? } }, requested_map: { @account.id => false } }
|
options = @account.locked? || current_user.account.silenced? ? {} : {
|
||||||
|
following_map: { @account.id => true },
|
||||||
|
showing_reblogs_map: { @account.id => follow.show_reblogs? },
|
||||||
|
notifying_map: { @account.id => follow.notify? },
|
||||||
|
delivery_following_map: { @account.id => follow.delivery? },
|
||||||
|
requested_map: { @account.id => false }
|
||||||
|
}
|
||||||
|
|
||||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(**options)
|
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(**options)
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,8 +28,8 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
||||||
Account.find(params[:id])
|
Account.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def relationships(**options)
|
def relationships
|
||||||
AccountRelationshipsPresenter.new([params[:id]], current_user.account_id, **options)
|
AccountRelationshipsPresenter.new([params[:id]], current_user.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_accounts
|
def load_accounts
|
||||||
|
|
|
@ -684,7 +684,7 @@ export function expandSubscribeFail(id, error) {
|
||||||
export function fetchRelationships(accountIds) {
|
export function fetchRelationships(accountIds) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const loadedRelationships = getState().get('relationships');
|
const loadedRelationships = getState().get('relationships');
|
||||||
const newAccountIds = accountIds.filter(id => loadedRelationships.get(id, null) === null);
|
const newAccountIds = Array.from(new Set(accountIds)).filter(id => loadedRelationships.get(id, null) === null);
|
||||||
|
|
||||||
if (newAccountIds.length === 0) {
|
if (newAccountIds.length === 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -3,91 +3,6 @@
|
||||||
module AccountInteractions
|
module AccountInteractions
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
class_methods do
|
|
||||||
def following_map(target_account_ids, account_id)
|
|
||||||
Follow.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |follow, mapping|
|
|
||||||
mapping[follow.target_account_id] = {
|
|
||||||
reblogs: follow.show_reblogs?,
|
|
||||||
notify: follow.notify?,
|
|
||||||
delivery: follow.delivery?,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def followed_by_map(target_account_ids, account_id)
|
|
||||||
Follow.where(account_id: target_account_ids, target_account_id: account_id).each_with_object({}) do |follow, mapping|
|
|
||||||
mapping[follow.account_id] = {
|
|
||||||
delivery: follow.delivery?,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
follow_mapping(Follow.where(account_id: target_account_ids, target_account_id: account_id), :account_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def subscribing_map(target_account_ids, account_id)
|
|
||||||
AccountSubscribe.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |subscribe, mapping|
|
|
||||||
mapping[subscribe.target_account_id] = (mapping[subscribe.target_account_id] || {}).merge({
|
|
||||||
subscribe.list_id || -1 => {
|
|
||||||
reblogs: subscribe.show_reblogs?,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def blocking_map(target_account_ids, account_id)
|
|
||||||
follow_mapping(Block.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def blocked_by_map(target_account_ids, account_id)
|
|
||||||
follow_mapping(Block.where(account_id: target_account_ids, target_account_id: account_id), :account_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def muting_map(target_account_ids, account_id)
|
|
||||||
Mute.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |mute, mapping|
|
|
||||||
mapping[mute.target_account_id] = {
|
|
||||||
notifications: mute.hide_notifications?,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def requested_map(target_account_ids, account_id)
|
|
||||||
FollowRequest.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |follow_request, mapping|
|
|
||||||
mapping[follow_request.target_account_id] = {
|
|
||||||
reblogs: follow_request.show_reblogs?,
|
|
||||||
notify: follow_request.notify?,
|
|
||||||
delivery: follow_request.delivery?,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def endorsed_map(target_account_ids, account_id)
|
|
||||||
follow_mapping(AccountPin.where(account_id: account_id, target_account_id: target_account_ids), :target_account_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_note_map(target_account_ids, account_id)
|
|
||||||
AccountNote.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |note, mapping|
|
|
||||||
mapping[note.target_account_id] = {
|
|
||||||
comment: note.comment,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def domain_blocking_map(target_account_ids, account_id)
|
|
||||||
accounts_map = Account.where(id: target_account_ids).select('id, domain').each_with_object({}) { |a, h| h[a.id] = a.domain }
|
|
||||||
blocked_domains = domain_blocking_map_by_domain(accounts_map.values.compact, account_id)
|
|
||||||
accounts_map.reduce({}) { |h, (id, domain)| h.merge(id => blocked_domains[domain]) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def domain_blocking_map_by_domain(target_domains, account_id)
|
|
||||||
follow_mapping(AccountDomainBlock.where(account_id: account_id, domain: target_domains), :domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def follow_mapping(query, field)
|
|
||||||
query.pluck(field).index_with(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
included do
|
included do
|
||||||
# Follow relations
|
# Follow relations
|
||||||
has_many :follow_requests, dependent: :destroy
|
has_many :follow_requests, dependent: :destroy
|
||||||
|
|
|
@ -117,13 +117,14 @@ module StatusThreadingConcern
|
||||||
def relations_map_for_account(account, account_ids, domains)
|
def relations_map_for_account(account, account_ids, domains)
|
||||||
return {} if account.nil?
|
return {} if account.nil?
|
||||||
|
|
||||||
|
presenter = AccountRelationshipsPresenter.new(account_ids, account)
|
||||||
{
|
{
|
||||||
blocking: Account.blocking_map(account_ids, account.id),
|
blocking: presenter.blocking,
|
||||||
blocked_by: Account.blocked_by_map(account_ids, account.id),
|
blocked_by: presenter.blocked_by,
|
||||||
muting: Account.muting_map(account_ids, account.id),
|
muting: presenter.muting,
|
||||||
following: Account.following_map(account_ids, account.id),
|
following: presenter.following,
|
||||||
subscribing: Account.subscribing_map(account_ids, account.id),
|
subscribing: presenter.subscribing,
|
||||||
domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
|
domain_blocking_by_domain: presenter.domain_blocking,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -129,7 +129,6 @@ class Status < ApplicationRecord
|
||||||
|
|
||||||
cache_associated :application,
|
cache_associated :application,
|
||||||
:media_attachments,
|
:media_attachments,
|
||||||
:emoji_reactions,
|
|
||||||
:conversation,
|
:conversation,
|
||||||
:status_stat,
|
:status_stat,
|
||||||
:tags,
|
:tags,
|
||||||
|
@ -142,7 +141,6 @@ class Status < ApplicationRecord
|
||||||
:tags,
|
:tags,
|
||||||
:preview_cards,
|
:preview_cards,
|
||||||
:media_attachments,
|
:media_attachments,
|
||||||
:emoji_reactions,
|
|
||||||
:conversation,
|
:conversation,
|
||||||
:status_stat,
|
:status_stat,
|
||||||
:preloadable_poll,
|
:preloadable_poll,
|
||||||
|
|
|
@ -1,28 +1,77 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AccountRelationshipsPresenter
|
class AccountRelationshipsPresenter
|
||||||
attr_reader :following, :followed_by, :subscribing, :blocking, :blocked_by,
|
attr_reader :following, :showing_reblogs, :notifying, :delivery_following, :followed_by, :subscribing, :blocking, :blocked_by,
|
||||||
:muting, :requested, :domain_blocking,
|
:muting, :muting_notifications, :requested, :domain_blocking,
|
||||||
:endorsed, :account_note
|
:endorsed, :account_note
|
||||||
|
|
||||||
def initialize(account_ids, current_account_id, **options)
|
def initialize(account_ids, current_account_id, **options)
|
||||||
@account_ids = account_ids.map { |a| a.is_a?(Account) ? a.id : a.to_i }
|
@account_ids = account_ids.map { |a| a.is_a?(Account) ? a.id : a.to_i }
|
||||||
@current_account_id = current_account_id
|
@current_account_id = current_account_id
|
||||||
|
|
||||||
@following = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id))
|
@following = cached[:following]
|
||||||
@followed_by = cached[:followed_by].merge(Account.followed_by_map(@uncached_account_ids, @current_account_id))
|
@showing_reblogs = cached[:showing_reblogs]
|
||||||
@subscribing = cached[:subscribing].merge(Account.subscribing_map(@uncached_account_ids, @current_account_id))
|
@notifying = cached[:notifying]
|
||||||
@blocking = cached[:blocking].merge(Account.blocking_map(@uncached_account_ids, @current_account_id))
|
@delivery_following = cached[:delivery_following]
|
||||||
@blocked_by = cached[:blocked_by].merge(Account.blocked_by_map(@uncached_account_ids, @current_account_id))
|
@followed_by = cached[:followed_by]
|
||||||
@muting = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id))
|
@subscribing = cached[:subscribing]
|
||||||
@requested = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id))
|
@blocking = cached[:blocking]
|
||||||
@domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id))
|
@blocked_by = cached[:blocked_by]
|
||||||
@endorsed = cached[:endorsed].merge(Account.endorsed_map(@uncached_account_ids, @current_account_id))
|
@muting = cached[:muting]
|
||||||
@account_note = cached[:account_note].merge(Account.account_note_map(@uncached_account_ids, @current_account_id))
|
@muting_notifications = cached[:muting_notifications]
|
||||||
|
@requested = cached[:requested]
|
||||||
|
@domain_blocking = cached[:domain_blocking]
|
||||||
|
@endorsed = cached[:endorsed]
|
||||||
|
@account_note = cached[:account_note]
|
||||||
|
|
||||||
|
if current_account_id.present? && !account_ids.empty?
|
||||||
|
result = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.sanitize_sql_array([<<-SQL.squish, account_ids: @uncached_account_ids, current_account_id: @current_account_id])).to_a.first
|
||||||
|
with
|
||||||
|
followings as (select * from follows where account_id = :current_account_id and target_account_id in (:account_ids)),
|
||||||
|
follow_requesteds as (select * from follow_requests where account_id = :current_account_id and target_account_id in (:account_ids)),
|
||||||
|
filter_mutes as (select * from mutes where account_id = :current_account_id and target_account_id in (:account_ids)),
|
||||||
|
subscribe_lists as (select target_account_id, coalesce(list_id, -1) as id, json_object_agg('reblogs', show_reblogs) as reblogs from account_subscribes where account_id = :current_account_id and target_account_id in (:account_ids) group by target_account_id, id)
|
||||||
|
select
|
||||||
|
(select string_agg(target_account_id::text, ',') from followings) as following,
|
||||||
|
(select string_agg(target_account_id::text, ',') from (select target_account_id from followings where show_reblogs union all select target_account_id from follow_requesteds where show_reblogs) a) as showing_reblogs,
|
||||||
|
(select string_agg(target_account_id::text, ',') from (select target_account_id from followings where notify union all select target_account_id from follow_requesteds where notify) a) as notifying,
|
||||||
|
(select string_agg(target_account_id::text, ',') from (select target_account_id from followings where delivery union all select target_account_id from follow_requesteds where delivery) a) as delivery_following,
|
||||||
|
(select string_agg(target_account_id::text, ',') from follow_requesteds) as requested,
|
||||||
|
(select string_agg(account_id::text, ',') from follows where target_account_id = :current_account_id and account_id in (:account_ids)) as followed_by,
|
||||||
|
(select json_object_agg(list.target_account_id, list.val)
|
||||||
|
from (select target_account_id, json_object_agg(lists.id, lists.reblogs) as val from subscribe_lists as lists group by target_account_id) as list) as subscribing,
|
||||||
|
(select string_agg(target_account_id::text, ',') from blocks where account_id = :current_account_id and target_account_id in (:account_ids)) as blocking,
|
||||||
|
(select string_agg(account_id::text, ',') from blocks where target_account_id = :current_account_id and account_id in (:account_ids)) as blocked_by,
|
||||||
|
(select string_agg(target_account_id::text, ',') from filter_mutes) as muting,
|
||||||
|
(select string_agg(target_account_id::text, ',') from filter_mutes where hide_notifications) as muting_notifications,
|
||||||
|
(select string_agg(adb.account_id::text, ',') from accounts a join account_domain_blocks adb on a.domain = adb.domain where adb.account_id = :current_account_id and a.id in (:account_ids)) as domain_blocking,
|
||||||
|
(select string_agg(target_account_id::text, ',') from account_pins where account_id = :current_account_id and target_account_id in (:account_ids)) as endorsed,
|
||||||
|
(select json_object_agg(n.target_account_id, n.val)
|
||||||
|
from (select target_account_id, json_object_agg('comment', comment) as val from account_notes where account_id = :current_account_id and target_account_id in (:account_ids) group by target_account_id) as n) as account_note
|
||||||
|
SQL
|
||||||
|
|
||||||
|
@following.merge!(mapping_from_string(result['following']))
|
||||||
|
@showing_reblogs.merge!(mapping_from_string(result['showing_reblogs']))
|
||||||
|
@notifying.merge!(mapping_from_string(result['notifying']))
|
||||||
|
@delivery_following.merge!(mapping_from_string(result['delivery_following']))
|
||||||
|
@followed_by.merge!(mapping_from_string(result['followed_by']))
|
||||||
|
@subscribing.merge!(mapping_from_json(result['subscribing']))
|
||||||
|
@blocking.merge!(mapping_from_string(result['blocking']))
|
||||||
|
@blocked_by.merge!(mapping_from_string(result['blocked_by']))
|
||||||
|
@muting.merge!(mapping_from_string(result['muting']))
|
||||||
|
@muting_notifications.merge!(mapping_from_string(result['muting_notifications']))
|
||||||
|
@requested.merge!(mapping_from_string(result['requested']))
|
||||||
|
@domain_blocking.merge!(mapping_from_string(result['domain_blocking']))
|
||||||
|
@endorsed.merge!(mapping_from_string(result['endorsed']))
|
||||||
|
@account_note.merge!(mapping_from_json(result['account_note']))
|
||||||
|
|
||||||
cache_uncached!
|
cache_uncached!
|
||||||
|
end
|
||||||
|
|
||||||
@following.merge!(options[:following_map] || {})
|
@following.merge!(options[:following_map] || {})
|
||||||
|
@showing_reblogs.merge!(options[:showing_reblogs_map] || {})
|
||||||
|
@notifying.merge!(options[:notifying_map] || {})
|
||||||
|
@delivery_following.merge!(options[:delivery_following_map] || {})
|
||||||
@followed_by.merge!(options[:followed_by_map] || {})
|
@followed_by.merge!(options[:followed_by_map] || {})
|
||||||
@subscribing.merge!(options[:subscribing_map] || {})
|
@subscribing.merge!(options[:subscribing_map] || {})
|
||||||
@blocking.merge!(options[:blocking_map] || {})
|
@blocking.merge!(options[:blocking_map] || {})
|
||||||
|
@ -36,16 +85,36 @@ class AccountRelationshipsPresenter
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def mapping_from_string(string)
|
||||||
|
return {} if string.blank?
|
||||||
|
|
||||||
|
string&.split(',')&.map(&:to_i)&.index_with(true) || {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def mapping_from_json(json)
|
||||||
|
return {} if json.blank?
|
||||||
|
|
||||||
|
(Oj.load(json, mode: :strict, symbol_keys: true) || {}).tap do |json_data|
|
||||||
|
json_data.keys.each do |key|
|
||||||
|
json_data[(Integer(key.to_s) rescue key) || key] = json_data.delete(key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def cached
|
def cached
|
||||||
return @cached if defined?(@cached)
|
return @cached if defined?(@cached)
|
||||||
|
|
||||||
@cached = {
|
@cached = {
|
||||||
following: {},
|
following: {},
|
||||||
|
showing_reblogs: {},
|
||||||
|
notifying: {},
|
||||||
|
delivery_following: {},
|
||||||
followed_by: {},
|
followed_by: {},
|
||||||
subscribing: {},
|
subscribing: {},
|
||||||
blocking: {},
|
blocking: {},
|
||||||
blocked_by: {},
|
blocked_by: {},
|
||||||
muting: {},
|
muting: {},
|
||||||
|
muting_notifications: {},
|
||||||
requested: {},
|
requested: {},
|
||||||
domain_blocking: {},
|
domain_blocking: {},
|
||||||
endorsed: {},
|
endorsed: {},
|
||||||
|
@ -71,11 +140,15 @@ class AccountRelationshipsPresenter
|
||||||
@uncached_account_ids.each do |account_id|
|
@uncached_account_ids.each do |account_id|
|
||||||
maps_for_account = {
|
maps_for_account = {
|
||||||
following: { account_id => following[account_id] },
|
following: { account_id => following[account_id] },
|
||||||
|
showing_reblogs: { account_id => showing_reblogs[account_id] },
|
||||||
|
notifying: { account_id => notifying[account_id] },
|
||||||
|
delivery_following: { account_id => delivery_following[account_id] },
|
||||||
followed_by: { account_id => followed_by[account_id] },
|
followed_by: { account_id => followed_by[account_id] },
|
||||||
subscribing: { account_id => subscribing[account_id] },
|
subscribing: { account_id => subscribing[account_id] },
|
||||||
blocking: { account_id => blocking[account_id] },
|
blocking: { account_id => blocking[account_id] },
|
||||||
blocked_by: { account_id => blocked_by[account_id] },
|
blocked_by: { account_id => blocked_by[account_id] },
|
||||||
muting: { account_id => muting[account_id] },
|
muting: { account_id => muting[account_id] },
|
||||||
|
muting_notifications: { account_id => muting_notifications[account_id] },
|
||||||
requested: { account_id => requested[account_id] },
|
requested: { account_id => requested[account_id] },
|
||||||
domain_blocking: { account_id => domain_blocking[account_id] },
|
domain_blocking: { account_id => domain_blocking[account_id] },
|
||||||
endorsed: { account_id => endorsed[account_id] },
|
endorsed: { account_id => endorsed[account_id] },
|
||||||
|
|
|
@ -18,12 +18,28 @@ class StatusRelationshipsPresenter
|
||||||
conversation_ids = statuses.filter_map(&:conversation_id).uniq
|
conversation_ids = statuses.filter_map(&:conversation_id).uniq
|
||||||
pinnable_status_ids = statuses.map(&:proper).filter_map { |s| s.id if s.account_id == current_account_id && %w(public unlisted).include?(s.visibility) }
|
pinnable_status_ids = statuses.map(&:proper).filter_map { |s| s.id if s.account_id == current_account_id && %w(public unlisted).include?(s.visibility) }
|
||||||
|
|
||||||
@reblogs_map = Status.reblogs_map(status_ids, current_account_id).merge(options[:reblogs_map] || {})
|
result = ActiveRecord::Base.connection.select_all(ActiveRecord::Base.sanitize_sql_array([<<-SQL.squish, account_id: current_account_id, status_ids: status_ids, conversation_ids: conversation_ids, pinnable_status_ids: pinnable_status_ids])).to_a.first
|
||||||
@favourites_map = Status.favourites_map(status_ids, current_account_id).merge(options[:favourites_map] || {})
|
select
|
||||||
@bookmarks_map = Status.bookmarks_map(status_ids, current_account_id).merge(options[:bookmarks_map] || {})
|
(select string_agg(reblog_of_id::text, ',') from statuses where account_id = :account_id and reblog_of_id in (:status_ids)) as reblogs,
|
||||||
@emoji_reactions_map = Status.emoji_reactions_map(status_ids, current_account_id).merge(options[:emoji_reactions_map] || {})
|
(select string_agg(status_id::text, ',') from favourites where account_id = :account_id and status_id IN (:status_ids)) as favourites,
|
||||||
@mutes_map = Status.mutes_map(conversation_ids, current_account_id).merge(options[:mutes_map] || {})
|
(select string_agg(status_id::text, ',') from bookmarks where account_id = :account_id and status_id in (:status_ids)) as bookmarks,
|
||||||
@pins_map = Status.pins_map(pinnable_status_ids, current_account_id).merge(options[:pins_map] || {})
|
(select string_agg(status_id::text, ',') from emoji_reactions where account_id = :account_id and status_id in (:status_ids)) as emoji_reactions,
|
||||||
|
(select string_agg(conversation_id::text, ',') from conversation_mutes where account_id = :account_id and conversation_id in (:conversation_ids)) as mutes,
|
||||||
|
(select string_agg(status_id::text, ',') from status_pins where account_id = :account_id and status_id in (:pinnable_status_ids)) as pins
|
||||||
|
SQL
|
||||||
|
|
||||||
|
@reblogs_map = mapping(result['reblogs'], options[:reblogs_map])
|
||||||
|
@favourites_map = mapping(result['favourites'], options[:favourites_map])
|
||||||
|
@bookmarks_map = mapping(result['bookmarks'], options[:bookmarks_map])
|
||||||
|
@emoji_reactions_map = mapping(result['emoji_reactions'], options[:emoji_reactions_map])
|
||||||
|
@mutes_map = mapping(result['mutes'], options[:mutes_map])
|
||||||
|
@pins_map = mapping(result['pins'], options[:pins_map])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def mapping(result, additional)
|
||||||
|
(result&.split(',')&.map(&:to_i)&.index_with(true) || {}).merge(additional || {})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,21 +14,15 @@ class REST::RelationshipSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def delivery_following
|
def delivery_following
|
||||||
(instance_options[:relationships].following[object.id] || {})[:delivery] ||
|
instance_options[:relationships].delivery_following[object.id] ? true : false
|
||||||
(instance_options[:relationships].requested[object.id] || {})[:delivery] ||
|
|
||||||
false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def showing_reblogs
|
def showing_reblogs
|
||||||
(instance_options[:relationships].following[object.id] || {})[:reblogs] ||
|
instance_options[:relationships].showing_reblogs[object.id] ? true : false
|
||||||
(instance_options[:relationships].requested[object.id] || {})[:reblogs] ||
|
|
||||||
false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def notifying
|
def notifying
|
||||||
(instance_options[:relationships].following[object.id] || {})[:notify] ||
|
instance_options[:relationships].notifying[object.id] ? true : false
|
||||||
(instance_options[:relationships].requested[object.id] || {})[:notify] ||
|
|
||||||
false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def followed_by
|
def followed_by
|
||||||
|
@ -52,7 +46,7 @@ class REST::RelationshipSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def muting_notifications
|
def muting_notifications
|
||||||
(instance_options[:relationships].muting[object.id] || {})[:notifications] || false
|
instance_options[:relationships].muting_notifications[object.id] ? true : false
|
||||||
end
|
end
|
||||||
|
|
||||||
def requested
|
def requested
|
||||||
|
|
|
@ -20,21 +20,21 @@ class AccountFullTextSearchService < BaseService
|
||||||
|
|
||||||
results = definition.limit(@limit).offset(@offset).objects.compact
|
results = definition.limit(@limit).offset(@offset).objects.compact
|
||||||
account_ids = results.map(&:id)
|
account_ids = results.map(&:id)
|
||||||
account_domains = results.map(&:domain).uniq.compact
|
preloaded_relations = relations_map_for_account(@account, account_ids)
|
||||||
preloaded_relations = relations_map_for_account(@account, account_ids, account_domains)
|
|
||||||
|
|
||||||
results.reject { |target_account| AccountSearchFilter.new(target_account, @account, preloaded_relations).filtered? }
|
results.reject { |target_account| AccountSearchFilter.new(target_account, @account, preloaded_relations).filtered? }
|
||||||
rescue Faraday::ConnectionFailed, Parslet::ParseFailed
|
rescue Faraday::ConnectionFailed, Parslet::ParseFailed
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
def relations_map_for_account(account, account_ids, domains)
|
def relations_map_for_account(account, account_ids)
|
||||||
|
presenter = AccountRelationshipsPresenter.new(account_ids, account)
|
||||||
{
|
{
|
||||||
blocking: Account.blocking_map(account_ids, account.id),
|
blocking: presenter.blocking,
|
||||||
blocked_by: Account.blocked_by_map(account_ids, account.id),
|
blocked_by: presenter.blocked_by,
|
||||||
muting: Account.muting_map(account_ids, account.id),
|
muting: presenter.muting,
|
||||||
following: Account.following_map(account_ids, account.id),
|
following: presenter.following,
|
||||||
domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
|
domain_blocking_by_domain: presenter.domain_blocking,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -135,11 +135,12 @@ class ImportService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def relations_map_for_account(account, account_ids)
|
def relations_map_for_account(account, account_ids)
|
||||||
|
presenter = AccountRelationshipsPresenter.new(account_ids, account)
|
||||||
{
|
{
|
||||||
blocking: {},
|
blocking: {},
|
||||||
blocked_by: Account.blocked_by_map(account_ids, account.id),
|
blocked_by: presenter.blocked_by,
|
||||||
muting: {},
|
muting: {},
|
||||||
following: Account.following_map(account_ids, account.id),
|
following: presenter.following,
|
||||||
domain_blocking_by_domain: {},
|
domain_blocking_by_domain: {},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,8 +50,7 @@ class SearchService < BaseService
|
||||||
|
|
||||||
results = definition.limit(@limit).offset(@offset).objects.compact
|
results = definition.limit(@limit).offset(@offset).objects.compact
|
||||||
account_ids = results.map(&:account_id)
|
account_ids = results.map(&:account_id)
|
||||||
account_domains = results.map(&:account_domain)
|
preloaded_relations = relations_map_for_account(@account, account_ids)
|
||||||
preloaded_relations = relations_map_for_account(@account, account_ids, account_domains)
|
|
||||||
|
|
||||||
results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? }
|
results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? }
|
||||||
rescue Faraday::ConnectionFailed, Parslet::ParseFailed
|
rescue Faraday::ConnectionFailed, Parslet::ParseFailed
|
||||||
|
@ -113,14 +112,14 @@ class SearchService < BaseService
|
||||||
@options[:type].blank? || @options[:type] == 'statuses'
|
@options[:type].blank? || @options[:type] == 'statuses'
|
||||||
end
|
end
|
||||||
|
|
||||||
def relations_map_for_account(account, account_ids, domains)
|
def relations_map_for_account(account, account_ids)
|
||||||
|
presenter = AccountRelationshipsPresenter.new(account_ids, account)
|
||||||
{
|
{
|
||||||
blocking: Account.blocking_map(account_ids, account.id),
|
blocking: presenter.blocking,
|
||||||
blocked_by: Account.blocked_by_map(account_ids, account.id),
|
blocked_by: presenter.blocked_by,
|
||||||
muting: Account.muting_map(account_ids, account.id),
|
muting: presenter.muting,
|
||||||
following: Account.following_map(account_ids, account.id),
|
following: presenter.following,
|
||||||
subscribing: Account.subscribing_map(account_ids, account.id),
|
domain_blocking_by_domain: presenter.domain_blocking,
|
||||||
domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -459,30 +459,6 @@ RSpec.describe Account, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.following_map' do
|
|
||||||
it 'returns an hash' do
|
|
||||||
expect(Account.following_map([], 1)).to be_a Hash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.followed_by_map' do
|
|
||||||
it 'returns an hash' do
|
|
||||||
expect(Account.followed_by_map([], 1)).to be_a Hash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.blocking_map' do
|
|
||||||
it 'returns an hash' do
|
|
||||||
expect(Account.blocking_map([], 1)).to be_a Hash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.requested_map' do
|
|
||||||
it 'returns an hash' do
|
|
||||||
expect(Account.requested_map([], 1)).to be_a Hash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'MENTION_RE' do
|
describe 'MENTION_RE' do
|
||||||
subject { Account::MENTION_RE }
|
subject { Account::MENTION_RE }
|
||||||
|
|
||||||
|
|
|
@ -8,89 +8,6 @@ describe AccountInteractions do
|
||||||
let(:target_account_id) { target_account.id }
|
let(:target_account_id) { target_account.id }
|
||||||
let(:target_account_ids) { [target_account_id] }
|
let(:target_account_ids) { [target_account_id] }
|
||||||
|
|
||||||
describe '.following_map' do
|
|
||||||
subject { Account.following_map(target_account_ids, account_id) }
|
|
||||||
|
|
||||||
context 'account with Follow' do
|
|
||||||
it 'returns { target_account_id => true }' do
|
|
||||||
Fabricate(:follow, account: account, target_account: target_account)
|
|
||||||
is_expected.to eq(target_account_id => { reblogs: true, notify: false })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'account without Follow' do
|
|
||||||
it 'returns {}' do
|
|
||||||
is_expected.to eq({})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.followed_by_map' do
|
|
||||||
subject { Account.followed_by_map(target_account_ids, account_id) }
|
|
||||||
|
|
||||||
context 'account with Follow' do
|
|
||||||
it 'returns { target_account_id => true }' do
|
|
||||||
Fabricate(:follow, account: target_account, target_account: account)
|
|
||||||
is_expected.to eq(target_account_id => true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'account without Follow' do
|
|
||||||
it 'returns {}' do
|
|
||||||
is_expected.to eq({})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.blocking_map' do
|
|
||||||
subject { Account.blocking_map(target_account_ids, account_id) }
|
|
||||||
|
|
||||||
context 'account with Block' do
|
|
||||||
it 'returns { target_account_id => true }' do
|
|
||||||
Fabricate(:block, account: account, target_account: target_account)
|
|
||||||
is_expected.to eq(target_account_id => true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'account without Block' do
|
|
||||||
it 'returns {}' do
|
|
||||||
is_expected.to eq({})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.muting_map' do
|
|
||||||
subject { Account.muting_map(target_account_ids, account_id) }
|
|
||||||
|
|
||||||
context 'account with Mute' do
|
|
||||||
before do
|
|
||||||
Fabricate(:mute, target_account: target_account, account: account, hide_notifications: hide)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'if Mute#hide_notifications?' do
|
|
||||||
let(:hide) { true }
|
|
||||||
|
|
||||||
it 'returns { target_account_id => { notifications: true } }' do
|
|
||||||
is_expected.to eq(target_account_id => { notifications: true })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'unless Mute#hide_notifications?' do
|
|
||||||
let(:hide) { false }
|
|
||||||
|
|
||||||
it 'returns { target_account_id => { notifications: false } }' do
|
|
||||||
is_expected.to eq(target_account_id => { notifications: false })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'account without Mute' do
|
|
||||||
it 'returns {}' do
|
|
||||||
is_expected.to eq({})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#follow!' do
|
describe '#follow!' do
|
||||||
it 'creates and returns Follow' do
|
it 'creates and returns Follow' do
|
||||||
expect do
|
expect do
|
||||||
|
|
Loading…
Reference in a new issue