after much sleuthing, this is an extremely hacky way to limit the amount of public post _ids we are pulling back with every query. previously, for unknown reason, we were pulling back all of them from people you are connected to, and doing giant in queries which were causing bad things to happen. As far as I can tell, we are in fact keeping track of the page( i.e. max time and offset) so there was no need to pull all of them back. This whole file is a huge clusterf*ck, so it really should just get totally refactored, as the stuff that is happening is not really as scary as it looks :(
164 lines
5.5 KiB
Ruby
164 lines
5.5 KiB
Ruby
# Copyright (c) 2010-2011, Diaspora Inc. This file is
|
|
# licensed under the Affero General Public License version 3 or later. See
|
|
# the COPYRIGHT file.
|
|
|
|
require File.join(Rails.root, 'lib', 'evil_query')
|
|
|
|
|
|
#TODO: THIS FILE SHOULD NOT EXIST, EVIL SQL SHOULD BE ENCAPSULATED IN EvilQueries,
|
|
#throwing all of this stuff in user violates demeter like WHOA
|
|
module User::Querying
|
|
def find_visible_shareable_by_id(klass, id, opts={} )
|
|
key = (opts.delete(:key) || :id)
|
|
::EvilQuery::VisibleShareableById.new(self, klass, key, id, opts).post!
|
|
end
|
|
|
|
def visible_shareables(klass, opts={})
|
|
opts = prep_opts(klass, opts)
|
|
shareable_ids = visible_shareable_ids(klass, opts)
|
|
klass.where(:id => shareable_ids).select('DISTINCT '+klass.to_s.tableize+'.*').limit(opts[:limit]).order(opts[:order_with_table])
|
|
end
|
|
|
|
def visible_shareable_ids(klass, opts={})
|
|
opts = prep_opts(klass, opts)
|
|
visible_ids_from_sql(klass, opts)
|
|
end
|
|
|
|
# @return [Array<Integer>]
|
|
def visible_ids_from_sql(klass, opts={})
|
|
opts = prep_opts(klass, opts)
|
|
opts[:klass] = klass
|
|
opts[:by_members_of] ||= self.aspect_ids
|
|
|
|
post_ids = klass.connection.select_values(visible_shareable_sql(klass, opts)).map { |id| id.to_i }
|
|
post_ids += klass.connection.select_values("#{construct_public_followings_sql(opts).to_sql} LIMIT #{opts[:limit]}").map {|id| id.to_i }
|
|
end
|
|
|
|
def visible_shareable_sql(klass, opts={})
|
|
table = klass.table_name
|
|
opts = prep_opts(klass, opts)
|
|
opts[:klass] = klass
|
|
|
|
shareable_from_others = construct_shareable_from_others_query(opts)
|
|
shareable_from_self = construct_shareable_from_self_query(opts)
|
|
|
|
"(#{shareable_from_others.to_sql} LIMIT #{opts[:limit]}) UNION ALL (#{shareable_from_self.to_sql} LIMIT #{opts[:limit]}) ORDER BY #{opts[:order]} LIMIT #{opts[:limit]}"
|
|
end
|
|
|
|
def ugly_select_clause(query, opts)
|
|
klass = opts[:klass]
|
|
select_clause ='DISTINCT %s.id, %s.updated_at AS updated_at, %s.created_at AS created_at' % [klass.table_name, klass.table_name, klass.table_name]
|
|
query.select(select_clause).order(opts[:order_with_table]).where(klass.arel_table[opts[:order_field]].lt(opts[:max_time]))
|
|
end
|
|
|
|
def construct_shareable_from_others_query(opts)
|
|
conditions = {
|
|
:pending => false,
|
|
:share_visibilities => {:hidden => opts[:hidden]},
|
|
:contacts => {:user_id => self.id, :receiving => true}
|
|
}
|
|
|
|
conditions[:type] = opts[:type] if opts.has_key?(:type)
|
|
|
|
query = opts[:klass].joins(:contacts).where(conditions)
|
|
|
|
if opts[:by_members_of]
|
|
query = query.joins(:contacts => :aspect_memberships).where(
|
|
:aspect_memberships => {:aspect_id => opts[:by_members_of]})
|
|
end
|
|
|
|
ugly_select_clause(query, opts)
|
|
end
|
|
|
|
def construct_public_followings_sql(opts)
|
|
aspects = Aspect.where(:id => opts[:by_members_of])
|
|
person_ids = Person.connection.select_values(people_in_aspects(aspects).select("people.id").to_sql)
|
|
|
|
query = opts[:klass].where(:author_id => person_ids, :public => true, :pending => false)
|
|
|
|
unless(opts[:klass] == Photo)
|
|
query = query.where(:type => opts[:type])
|
|
end
|
|
|
|
ugly_select_clause(query, opts)
|
|
end
|
|
|
|
def construct_shareable_from_self_query(opts)
|
|
conditions = {:pending => false }
|
|
conditions[:type] = opts[:type] if opts.has_key?(:type)
|
|
query = self.person.send(opts[:klass].to_s.tableize).where(conditions)
|
|
|
|
if opts[:by_members_of]
|
|
query = query.joins(:aspect_visibilities).where(:aspect_visibilities => {:aspect_id => opts[:by_members_of]})
|
|
end
|
|
|
|
ugly_select_clause(query, opts)
|
|
end
|
|
|
|
def contact_for(person)
|
|
return nil unless person
|
|
contact_for_person_id(person.id)
|
|
end
|
|
|
|
def aspects_with_shareable(base_class_name_or_class, shareable_id)
|
|
base_class_name = base_class_name_or_class
|
|
base_class_name = base_class_name_or_class.base_class.to_s if base_class_name_or_class.is_a?(Class)
|
|
self.aspects.joins(:aspect_visibilities).where(:aspect_visibilities => {:shareable_id => shareable_id, :shareable_type => base_class_name})
|
|
end
|
|
|
|
def contact_for_person_id(person_id)
|
|
Contact.where(:user_id => self.id, :person_id => person_id).includes(:person => :profile).first
|
|
end
|
|
|
|
# @param [Person] person
|
|
# @return [Boolean] whether person is a contact of this user
|
|
def has_contact_for?(person)
|
|
Contact.exists?(:user_id => self.id, :person_id => person.id)
|
|
end
|
|
|
|
def people_in_aspects(requested_aspects, opts={})
|
|
allowed_aspects = self.aspects & requested_aspects
|
|
aspect_ids = allowed_aspects.map(&:id)
|
|
|
|
people = Person.in_aspects(aspect_ids)
|
|
|
|
if opts[:type] == 'remote'
|
|
people = people.where(:owner_id => nil)
|
|
elsif opts[:type] == 'local'
|
|
people = people.where('people.owner_id IS NOT NULL')
|
|
end
|
|
people
|
|
end
|
|
|
|
def aspects_with_person person
|
|
contact_for(person).aspects
|
|
end
|
|
|
|
def posts_from(person)
|
|
::EvilQuery::ShareablesFromPerson.new(self, Post, person).make_relation!
|
|
end
|
|
|
|
def photos_from(person)
|
|
::EvilQuery::ShareablesFromPerson.new(self, Photo, person).make_relation!
|
|
end
|
|
|
|
protected
|
|
|
|
# @return [Hash]
|
|
def prep_opts(klass, opts)
|
|
defaults = {
|
|
:order => 'created_at DESC',
|
|
:limit => 15,
|
|
:hidden => false
|
|
}
|
|
defaults[:type] = Stream::Base::TYPES_OF_POST_IN_STREAM if klass == Post
|
|
opts = defaults.merge(opts)
|
|
|
|
opts[:order_field] = opts[:order].split.first.to_sym
|
|
opts[:order_with_table] = klass.table_name + '.' + opts[:order]
|
|
|
|
opts[:max_time] = Time.at(opts[:max_time]) if opts[:max_time].is_a?(Integer)
|
|
opts[:max_time] ||= Time.now + 1
|
|
opts
|
|
end
|
|
end
|