问题
I have a specific example, but the general question is: whats the optimal way to retrieve records when you have an array of filter values to match?
Say I have User and Post records where User has_many :posts
and I have a Relationship model that looks like this:
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
I want to write a function that returns chronologically ordered posts from your followings' following - that is, all the posts from users that the users you follow are following, excluding duplicates and posts from anyone you follow.
My solution is to first compile an array of all the users of interest:
users = []
@user.following.each do |f|
f.following.each do |ff|
users << ff
end
end
# dedupe
users = users.uniq
#remove self and following
users.delete(@user)
@user.following.each do |f|
users.delete(f)
end
then compile their posts and order them:
posts = []
users.each do |u|
posts += u.posts
end
posts.sort_by!{|x| x[:created_at]}.reverse!
I think there's a better way to do this with Active Record functions, but I can't figure out how to make them work with an array. For instance, if I compile an array of User id values instead of the full models, and try to run this code to get the post array:
posts = Post.where(
user_id: user_ids
).order('created_at DESC').limit(21)
it returns an empty array. Is there a better way to search with an array of filter values than my current solution?
Update: additional modal code:
class Post < ActiveRecord::Base
belongs_to :user
...
User.rb
class User < ActiveRecord::Base
has_many :photos
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
...
回答1:
Your idea of using user_ids is a good one. If that query returned an empty array, then did you check to make sure that the user_ids were the ones that you expected?
As for the code, you need to look into Enumerable#map and #flat_map. They are built-in ruby methods to accomplish what you're trying to do with the #each loops. Your code might reduce to something like:
user_ids = user.followings.flat_map { |following| following.following_id }
user_ids.uniq!
user_ids -= [user.id, user.following_ids].flatten
Post.where(user_id: user_ids).order(id: :desc).limit(21)
Note: Since created_at should follow id creation order, I would consider searching based on id rather than created_at since it should have an index.
来源:https://stackoverflow.com/questions/36344662/ruby-on-rails-optimal-search-with-array-of-filter-values