ActiveRecord querying through multiple joins - sql

So below are my models:
post.rb
class Post < ActiveRecord::Base
has_many :taggings
has_many :tags, through: :taggings
has_many :collectables
has_many :collections, through: :collectables
end
tagging.rb
class Tagging < ActiveRecord::Base
belongs_to :post
belongs_to :tag, counter_cache: :posts_count
end
tag.rb
class Tag < ActiveRecord::Base
has_many :taggings
has_many :posts, through: :taggings
end
collectable.rb
class Collectable < ActiveRecord::Base
belongs_to :post
belongs_to :collection, counter_cache: :posts_count
end
collection.rb
class Collection < ActiveRecord::Base
has_many :collectables
has_many :posts
end
A Collection has many Posts, and a Post has many Tags. Now I'm trying to create a search bar that'll search for Collections by Posts that have particular Tags (tag.name is the search term). For instance, Collection 1 has a post that has the tag #cat. Now if the user searches for "cat", Collection 1 will show up in the results. I'm not sure how the query should look like for making this happen.
def self.search(search)
Collection.joins(:posts) ... ?
end

try this:
def self.search(search)
Collection.includes(posts: :tags).where(tags: {name: search})
end

Try This
def self.search(search)
Collection.joins(posts: :tags).where({tags: {name: search}})
end
You can use includes, eager_load and joins, but when you use joins then only it will take those collections who have the posts in all other cases, it will take all collections who have the posts and who don't have the posts.

Related

N + 1 Queries even with Eager loading in Rails 3.2

I have a has_many through, through relationship that needs to be eagerly loaded. Is this possible in Rails 3.2? I've tried several ways to include the association yet calling categories on the facebook_user object always makes another query to Category. Here's a simplified version of my activerecord associations.
class FacebookUser < ActiveRecord::Base
belongs_to :user, touch: true
has_many :user_categories, through: :user
has_many :categories, through: :user_categories
end
class User < ActiveRecord::Base
has_many :user_categories
has_many :categories, through: :user_categories
has_one :facebook_user
end
class UserCategory < ActiveRecord::Base
belongs_to :user
belongs_to :category
end
#single query
FacebookUser.includes( user: :categories).joins(user: :categories).each do |f|
## N+1 query on f.categories
f.categories.first
end
#multi-part query
facebook_user_ids = FacebookUser.where(user_id: [1,2,3]).joins(user: :categories).pluck('facebook_users.id')
FacebookUser.where( id: facebook_user_ids ).includes( user: :categories)
With
FacebookUser.includes( user: :categories)
you have eager loaded the user and his categories instead of the categories on facebook_user. So in order to avoid n+1 query you call then
f.user.categories
So why u just don't eager facebook_user's categories?
FacebookUser.includes(:categories)

Rails ActiveRecord query on existing collection

Suppose I have a result from a query:
allCourses = Course.all
Then I also have another set:
myCourses = current_user.courses.all
How can I get a set of items that are in allCourses and NOT in myCourses?
Here are the models:
class Student < ActiveRecord::Base
has_many :student_enrollments, dependent: :destroy
has_many :courses, through: :student_enrollments
end
class Course < ActiveRecord::Base
has_many :student_enrollments, dependent: :destroy
has_many :students, through: :student_enrollments
end
class StudentEnrollment < ActiveRecord::Base
belongs_to :student
belongs_to :course
end
I can always write raw SQL script to achieve the result, but I prefer to find a Rails way to do it.
Thanks
Assume your fk is user_id.
ohterCourses = Course.where('user_id != ?', current_user.id)

Rails: Fetch model across many associations

I've tried wrapping my mind around how to navigate the associations I want, but I can't seem to figure it out. I'm trying to get all the Posts given a Tag. Each Post currently has a title and body text, both of which are represented as TaggedText. Each TaggedText can have many unique tags — like tagging multiple people/pages in a Facebook post (uniqueness is enforced in the model when saving an instance).
class Tag < ActiveRecord::Base
has_many :tagged_texts, through: :tag_ranges
end
class Post < ActiveRecord::Base
has_many :tagged_texts
end
class TaggedText < ActiveRecord::Base
# Each TaggedText cannot have more than one of each tag
has_many :tags, through: :tag_ranges
belongs_to :post
end
class TagRange < ActiveRecord::Base
# TaggedText cannot have more than one of each tag
belongs_to :tagged_text
belongs_to :tag
end
I tried joining the tables, but I get the error Association named 'tag_ranges' was not found on Post:
def get_posts_by_tag(tag, page, posts_per_page)
Post
.joins(:tagged_texts)
.joins(:tag_ranges)
.joins(:tags)
.where('tag.id = ?', tag.id)
.uniq
.limit(posts_per_page)
.offset(page - 1)
.to_a
end
What am I missing to get the query to work — or should I restructure my models and associations somehow?
As you error states, you need to add a tag_ranges association to your Post model. I've also added a few associations that you may or may not find useful, and one that will simplify your query greatly. Not that your TagRange class's associations are fine as is.
class Tag < ActiveRecord::Base
has_many :tag_ranges # need this association in order to get tagged_texts
has_many :tagged_texts, through: :tag_ranges
has_many :posts, -> { uniq }, through: :tagged_texts # posts with the given tag
end
class Post < ActiveRecord::Base
has_many :tagged_texts
has_many :tag_ranges, through: :tagged_texts # Post now has association named 'tagged_ranges'
has_many :tags, -> { uniq }, through: :tag_ranges # tags that given post has
end
class TaggedText < ActiveRecord::Base
has_many :tag_ranges # all tag ranges for a tag text
has_many :tags, through: :tag_range
belongs_to :post
end
And now, your query to get all the posts for a tag:
def get_posts_by_tag(tag, page, posts_per_page)
tag.posts.limit(posts_per_page).offset(page - 1).to_a
end
Hopefully this helps!

Rails sql complex query for public_activity

I'm using the public_activity gem with rails 4 to generate an activity feed. The models are as follows:
Class Post
has_many :comments
has_many :taggings
has_many :tags, through: :taggings
Class Comment
belongs_to :post
Class Tagging
belongs_to :post
belongs_to :tag
Class Tag
has_many :posts, through: :taggings
has_many :taggings
On the Tags#show action, I'd like to show a feed of all activities of posts and comments belonging to posts belonging to that particular tag. How do I write the query?
class TagsController
activities = PublicActivity::Activity.order("created_at desc").where('trackable_type =? OR trackable_type = ?', 'Post', 'Comment')
#activities =.....
SOLUTION.
Ok managed to get it working.
TagsController
def show
post_ids = #tag.posts.map(&:id)
comment_ids = Comment.where('post_id IN (?)', post_ids).map(&:id)
#activities = PublicActivity::Activity.
where("(trackable_id IN (?) AND trackable_type = 'Post')
or (trackable_id IN (?) AND trackable_type = 'Comment')", post_ids, comment_ids).
order("created_at desc")
end
It's ugly and unsatisfying but it gets the job done. Bonus points for whoever can optimize this query!

Rails includes nested relations

I need to query all posts from a specific user and include all comments and the user who belongs to the comment.
class User < ...
has_many :posts
has_many :comments
end
class Post < ...
belongs_to :user
has_many :comments
end
class Comment < ...
belongs_to :user
belongs_to :post
end
#posts = current_user.posts.include(:comments)
Is is possible to also get the comment user? I list a lot of posts and comments. I do not want to query each comment user.
Thx / Tobias
Try
#posts = current_user.posts.includes( :comments => :user)
Read more about it here
How about include at the relation definition statement?
:include
Specify second-order associations that should be eager loaded when this object is loaded.
class Post <
belongs_to :user
has_many :comments, :include => [:user], :limit => 5
end