12.3.3 scaling issue of the status feed

4458 단어 status
the problem of the implementation of last chapter is:
 
1. it has a line of code:
 
following_ids = user.following_ids

this will fetch all the followed users of this user,
but what we need to just if user_id is included in this set.
SQL already optimized things like this, check inclusion in a set of another table.
 
2. the original way will pull out all the microposts and stick them into an array.
although they are paginated in the view, but in the memory, there is still an array holding all these data.
what we need  is it honestly pull out 30 elements at a time.
 
 
so we will solve the two problems in this chapter.
 
1. the solution to both problems involves converting the feed from a class method to a scope 
 
scope is a rails method for restricting database selects based on certain conditions.
 
for example, to arrange for a method to select all admins, we can add a scope:
 
 
scope :admin, where(:admin => true)
 
then
User.admin
will return all admin users.
 
2. I know you will ask what's the difference of using scope and class method:
 
when using independently, they are the same, both pull all admin users from database.
 
but when using in chains, it will be different:
 
User.admin.paginate(:page => 1)
this will only pull 30 admins from the database at a time.
 
so the database did some optimization here, the paginate will happen in the database level.
cool, isn't.
 
this will make a very bid differences for big site.
 
3. now it is time to change the from_users_followed_by method using scope:
  scope :from_users_followed_by, lambda { |user| followed_by(user) }

 note, this scope need to take an argument, so we have to use lambda, a anonymous function.
 
and the method is a little complicated, so we put it into a method, followed_by(user.)
  def self.followed_by(user)
    following_ids = user.following_ids
    where("user_id in (#{following_ids}) or user_id = :user_id", {:user => user})
  end

 
but this only make sure the database don't pull all micropost, but it still pull out all followed users.
 
we should think of a way to replace
following_ids = user.following_ids

we need to use sql text here:
following_ids = %(select followed_id from relationships where follower_id = :user_id)

  so the new method is:
def self.followed_by(user)
      following_ids = %(SELECT followed_id FROM relationships
                        WHERE follower_id = :user_id)
      where("user_id IN (#{following_ids}) OR user_id = :user_id",
            { :user_id => user })
    end

 so the final sql being passed to database is:
SELECT * FROM microposts
WHERE user_id IN (SELECT followed_id FROM relationships
                  WHERE follower_id = 1)
      OR user_id = 1

 the database will be more efficient when executing this.
 
when Micropost.from_users_followed_by(user), it will still return all micropost.
 
but when chained with paginate or other things, it will be much different.
 
Micropost.from_users_followed_by(user).paginate(:page => 1)
it will not get all followed users, it just get enough to get the first 30 micropost 
 
4. a way to construct a string:  %(.........)
 
%(fjdkfjdlsjlfdsfjdlsfjlds
fjdsfjdslfdjs)
this is a way to construct string, you can think of it the same as double quotes.
 
the use case of %() to construct a string is:
a. when you want to show a multiline string.
b. when you want to have ""or "#{jfldjs}"inside a string.
  >> puts %(The variable "foo"is equal to "#{foo}".)
all "in this string will be just display, no need to escape,
 
without %(), it will have to be:
  >> "The variable\"foo\"is equal to\"#{foo}\"."
this is not very readable.

좋은 웹페이지 즐겨찾기