mirror of
https://github.com/mastodon/mastodon.git
synced 2026-09-12 00:46:13 -05:00
Add server-side filtering to home feed endpoint
This commit is contained in:
@@ -3,9 +3,10 @@
|
||||
class Feed
|
||||
include Redisable
|
||||
|
||||
def initialize(type, id)
|
||||
def initialize(type, id, options = {})
|
||||
@type = type
|
||||
@id = id
|
||||
@id = id
|
||||
@options = options
|
||||
end
|
||||
|
||||
def get(limit, max_id = nil, since_id = nil, min_id = nil)
|
||||
@@ -20,15 +21,31 @@ class Feed
|
||||
protected
|
||||
|
||||
def from_redis(limit, max_id, since_id, min_id)
|
||||
scope = Status.all
|
||||
|
||||
# Apply specified filters
|
||||
scope.merge!(Status.where.not(visibility: :direct)) if @options[:exclude_direct]
|
||||
scope.merge!(Status.where(reblog_of_id: nil)) if @options[:exclude_reblogs]
|
||||
scope.merge!(Status.where(quote_id: nil)) if @options[:exclude_quotes]
|
||||
scope.merge!(Status.where(in_reply_to_id: nil).or(Status.where(@id))) if @options[:exclude_replies] # TODO: beware
|
||||
|
||||
# If we have no filter, rely on Redis to apply the limit, otherwise we will have to do a posteriori filtering
|
||||
limit_clause = [0, limit] if scope == Status.all
|
||||
|
||||
max_id = '+inf' if max_id.blank?
|
||||
if min_id.blank?
|
||||
since_id = '-inf' if since_id.blank?
|
||||
unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).map { |id| id.first.to_i }
|
||||
ids = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: limit_clause, with_scores: true).map { |id| id.first.to_i }
|
||||
else
|
||||
unhydrated = redis.zrangebyscore(key, "(#{min_id}", "(#{max_id}", limit: [0, limit], with_scores: true).map { |id| id.first.to_i }
|
||||
ids = redis.zrangebyscore(key, "(#{min_id}", "(#{max_id}", limit: limit_clause, with_scores: true).map { |id| id.first.to_i }
|
||||
end
|
||||
|
||||
Status.where(id: unhydrated)
|
||||
if min_id.blank? || limit_clause.present?
|
||||
scope.where(id: ids).limit(limit)
|
||||
else
|
||||
# We need to do some filtering *and* do it in the correct order
|
||||
Status.where(id: scope.reorder(id: :asc).where(id: ids).limit(limit))
|
||||
end
|
||||
end
|
||||
|
||||
def key
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class HomeFeed < Feed
|
||||
def initialize(account)
|
||||
def initialize(account, options = {})
|
||||
@account = account
|
||||
super(:home, account.id)
|
||||
super(:home, account.id, options)
|
||||
end
|
||||
|
||||
def async_refresh
|
||||
|
||||
@@ -10,8 +10,9 @@ RSpec.describe HomeFeed do
|
||||
describe '#get' do
|
||||
before do
|
||||
Fabricate(:status, account: account, id: 1)
|
||||
Fabricate(:status, account: account, id: 2)
|
||||
Fabricate(:status, account: account, id: 3)
|
||||
Fabricate(:status, account: account, id: 2, reblog: Fabricate(:status))
|
||||
Fabricate(:status, account: account, id: 3, visibility: :direct)
|
||||
Fabricate(:status, account: account, id: 4)
|
||||
Fabricate(:status, account: account, id: 10)
|
||||
end
|
||||
|
||||
@@ -23,10 +24,17 @@ RSpec.describe HomeFeed do
|
||||
)
|
||||
end
|
||||
|
||||
it 'gets statuses with ids in the range from redis' do
|
||||
results = subject.get(3)
|
||||
it 'gets statuses with ids in the range from redis according to the given parameters' do
|
||||
expect(described_class.new(account).get(3).map(&:id)).to eq [4, 3, 2]
|
||||
expect(described_class.new(account, { exclude_direct: true }).get(3).map(&:id)).to eq [4, 2, 1]
|
||||
expect(described_class.new(account, { exclude_reblogs: true }).get(3).map(&:id)).to eq [4, 3, 1]
|
||||
expect(described_class.new(account, { exclude_direct: true, exclude_reblogs: true }).get(3).map(&:id)).to eq [4, 1]
|
||||
|
||||
expect(results.map(&:id)).to eq [3, 2]
|
||||
expect(described_class.new(account).get(2, nil, nil, 0).map(&:id)).to eq [2, 1]
|
||||
expect(described_class.new(account, { exclude_direct: true }).get(2, nil, nil, 0).map(&:id)).to eq [2, 1]
|
||||
expect(described_class.new(account, { exclude_direct: true }).get(2, nil, nil, 1).map(&:id)).to eq [4, 2]
|
||||
expect(described_class.new(account, { exclude_reblogs: true }).get(2, nil, nil, 0).map(&:id)).to eq [3, 1]
|
||||
expect(described_class.new(account, { exclude_direct: true, exclude_reblogs: true }).get(2, nil, nil, 0).map(&:id)).to eq [4, 1]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user