Rails join single record to scope - ruby-on-rails

Lets say there is a model called Comments. No we do #comments = Comment.where(name: 'John'). Next we do comment = Comment.find_or_create_by(...). Is there a way in Rails to join comment to #comments? For arrays I can do #comments << comment but I want activeRecords...
#comments.inspect returns "#<ActiveRecord::Relation []>"
I'm using rails 5.1.3
Thanks, Andreas

#comments will be an array of instances of Comment. comment will be an instance of Comment. Therefore, there is nothing wrong with doing #comments << comment and it will achieve the result you desire.

You should try before asking!
Active Record Relations have every method that arrays do available to them, so all you need to do is:
#comments << comment
or
#comments.push(comment)
EDIT: Have tried the above and seems to work for me however another solution:
ids = #comments.collect(&:id)
#comments = Comment.where(id: (ids + comment.id))
This will take the ID's from every record in #comments, then add them to the other comment's ID, then return all comments that have an ID in that range

Yes you can do something like this
If you want data in the active record format.
Comment.where(conditions).or(Comment.where(id: 1))
The rest of all the solution are complex or provides the data in the array form.

Related

Ruby Gem ActiveRecord find method with multiple conditions

I'm building a Sinatra application using three database's tables: user, post and like.
I'd want to run a query that will find an entry in the like table like so:
FIND in like WHERE user_id == params[:user_id] AND post_id == params[:post_id]
(for one condition I'll be using: Like.find_by_user_id(params[:user_id]))
My question is:
How to run a find query with multiple conditions using the ActiveRecord Gem?
Use where:
Like.where('user_id = ? AND post_id = ?', params[:user_id], params[:post_id])
or
Like.where('user_id = :user_id AND post_id = :post_id', params)
Is important to keep in mind that the paremeters of the where need to be converted to the expected type for example params[:post_id].to_i
Similar to find_by_user_id for user_id column you can combine multiple column names and get a dynamic finder find_by_user_id_and_post_id:
Like.find_by_user_id_and_post_id(params[:user_id], params[:post_id])
When there are more than "bearable" columns in the find_by_ finder, you could use where and supply the condition as follows:
Like.where(user_id: params[:user_id], post_id: params[:post_id])
Like.find_by_user_id(params[:user_id]) - this syntax is deprecated in ActiveRecord 4.
Instead try using where method of ActiveRecord query interface, to pass array conditions. Example:
Like.where("user_id = ? AND post_id = ?", params[:user_id], params[:post_id])
If you are expecting one record to be the result:
To replace find_by_whatever you can use find_by(whatever) for example User.find_by(username:"UsernameIsMyUsername",password:"Mypassword"). You should use find_by if there is only one record that you expect to match your search.
If you are expecting more than one record to be the result:
If you expect more than one you should use where with where(username:"MyUsername",password:"Password"). This will return all the resulting records in an array.

Rails 3 Joins -- Select only certain columns

Below is a relationship between Comments and a user. Each comment has one user so I'm building out a join in the code below.
I was wondering how to build this code to only include specific columns in the join. I don't need all of the user information. Just the first_name. Any suggestions.
Current Code:
#comments = Comment.where(:study_id => #study.id).joins(:user)
You could use something like this:
#comments = Comment.joins(:user)
.select("comments.*, users.first_name")
.where(study_id: #study.id)
Extending Aldo's answer to show a way to retrieve the resulting foreign column value.
#comments = \
Comment\
.joins(:user)
.select("comments.*, users.first_name as users_first_name")
.where(study_id: #study.id)
# Now it's stored on each comment as an attribute, e.g.:
puts #comments.first.read_attribute(:users_first_name)
puts #comments.first.attributes['users_first_name']
# Note that inspecting the comment won't show the foreign data
puts #comments.first.inspect # you won't see user's first name output
You could also declare users_first_name as an attrib on the comment with attr_accessible. I don't think there's any magic way to automatically set it, but you could easily do so yourself in a post-select loop.

Paginate data from two models into one newsfeed: Ruby on Rails 3 // Will_paginate

I'd like to make a newsfeed for the homepage of a site i'm playing around with. There are two models: Articles, and Posts. If I wanted just one in the newsfeed it would be easy:
#newsfeed_items = Article.paginate(:page => params[:page])
But I would like for the two to be both paginated into the same feed, in reverse chronological order. The default scope for the article and post model are already in that order.
How do I get the articles and posts to be combined in to the newsfeed as such?
Thanks!
EDIT: What about using SQL in the users model?
Just wondering: maybe would it be possible define in User.rb:
def feed
#some sql like (SELECT * FROM articles....)
end
Would this work at all?
in my last project i stuck into a problem, i had to paginate multiple models with single pagination in my search functionality. it should work in a way that the first model should appear first when the results of the first model a second model should continue the results and the third and so on as one single search feed, just like facebook feeds. this is the function i created to do this functionality
def multi_paginate(models, page, per_page)
WillPaginate::Collection.create(page, per_page) do |pager|
# set total entries
pager.total_entries = 0
counts = [0]
offsets = []
for model in models
pager.total_entries += model.count
counts << model.count
offset = pager.offset-(offsets[-1] || 0)
offset = offset>model.count ? model.count : offset
offsets << (offset<0 ? 0 : offset)
end
result = []
for i in 0...models.count
result += models[i].limit(pager.per_page-result.length).offset(offsets[i]).to_a
end
pager.replace(result)
end
end
try it and let me know if you have any problem with it, i also posted it as an issue to will_paginate repository, if everyone confirmed that it works correctly i'll fork and commit it to the library. https://github.com/mislav/will_paginate/issues/351
for those interested, please check this question: Creating a "feed" from multiple rails models, efficiently?
Here, Victor Piousbox provides a good, efficient solution.
Look at paginate_by_sql method. You can write unione query to fetch both articles and posts:
select 'article' as type, id from articles
union
select 'post' as type, id from posts
You can paginate both if you use AJAX. Here is well explained how to paginate using AJAX with WillPaginate.
You can paginate an array using WillPaginate::Collection.create. So you'd need to use ActiveRecord to find both sets of data and then combine them in a single array.
Then take a look at https://github.com/mislav/will_paginate/blob/master/lib/will_paginate/collection.rb for documentation on how to use the Collection to paginate any array.

rails where() sql query on array

I'll explain this as best as possible. I have a query on user posts:
#selected_posts = Posts.where(:category => "Baseball")
I would like to write the following statement. Here it is in pseudo terms:
User.where(user has a post in #selected_posts)
Keep in mind that I have a many to many relationship setup so post.user is usable.
Any ideas?
/EDIT
#posts_matches = User.includes(#selected_posts).map{ |user|
[user.company_name, user.posts.count, user.username]
}.sort
Basically, I need the above to work so that it uses the users that HAVE posts in selected_posts and not EVERY user we have in our database.
Try this:
user.posts.where("posts.category = ?", "Baseball")
Edit 1:
user.posts.where("posts.id IN (?)", #selected_posts)
Edit 2:
User.select("users.company_name, count(posts.id) userpost_count, user.username").
joins(:posts).
where("posts.id IN (?)", #selected_posts).
order("users.company_name, userpost_count, user.username")
Just use the following:
User.find(#selected_posts.map(&:user_id).uniq)
This takes the user ids from all the selected posts, turns them into an array, and removes any duplicates. Passing an array to user will just find all the users with matching ids. Problem solved.
To combine this with what you showed in your question, you could write:
#posts_matches = User.find(#selected_posts.map(&:user_id).uniq).map{ |user|
[user.company_name, user.posts.size, user.username]
}
Use size to count a relation instead of count because Rails caches the size method and automatically won't look it up more than once. This is better for performance.
Not sure what you were trying to accomplish with Array#sort at the end of your query, but you could always do something like:
#users_with_posts_in_selected = User.find(#selected_posts.map(&:user_id).uniq).order('username DESC')
I don't understand your question but you can pass an array to the where method like this:
where(:id => #selected_posts.map(&:id))
and it will create a SQL query like WHERE id IN (1,2,3,4)
By virtue of your associations your selected posts already have the users:
#selected_posts = Posts.where("posts.category =?", "Baseball")
#users = #selected_posts.collect(&:user);
You'll probably want to remove duplicate users from #users.

A clean way to update attributes across objects?

I want to do something like this:
Given:
#user1
#user2
Where the Post model has (id, user_id)
#posts = #user1.posts
I want to take all of the posts of user1, and set the user_id to #user2
Is there an easy one liner way to do this? Or do I have to loop through all the #posts and update attributes and save the record?
Thanks
You can use the class method update_all on your Post class to accomplish this. The first string passed is essentially the SET clause of an SQL update statement, the second string is the WHERE clause.
Post.update_all("user_id = #{#user2.id}", "user_id = #{#user1.id}")
I use the _ids attribute for this.
#user2.update_attribute(:post_ids, #user1.post_ids)
More explanation
If you type
#user1.post_ids # --> [1,2,3,4]
You will get all the ids for all the posts the user is connected to in an array.
You can also set the relationship in this way too
#user2.post_ids = [1,2]
#user2.save
So, to solve what you are asking about I simply assigned the array of post ids of #user1 to the post ids for #user2.

Resources