Rails Query based on hasManyBelongsToMany relation - ruby-on-rails

I have the following models
venues(id, name, ....)
categories(id, name, ....)
categories_venues(id, venue_id, category_id)
I want to query venues based which have the relation of Categories ID = (1)/(2) or (1 and 2)
How can i do a where query for the relation of a HABTM?
EDIT
Using the following query it solved the problem.
venues = Venue.joins(:categories_venues).where(:categories_venues => {:category_id => values})
I want to exclude repetitive records from the query and a particular ID from the results?
---
- !ruby/object:Venue
attributes:
id: 2
name: Ooty
slug: ooty
created_at: 2014-06-21 22:10:52.000000000 Z
updated_at: 2014-07-05 17:33:26.000000000 Z
- !ruby/object:Venue
attributes:
id: 4
name: Munnar
slug: munnar
created_at: 2014-08-25 03:42:13.000000000 Z
updated_at: 2014-08-25 03:42:13.000000000 Z
- !ruby/object:Venue
attributes:
id: 2
name: Ooty
slug: ooty
created_at: 2014-06-21 22:10:52.000000000 Z
updated_at: 2014-07-05 17:33:26.000000000 Z

Not sure if understanding you right. Plz provide sql code if you think this answer does not provide the right query
But i think, it should be just
Venue.joins(:categories).where(category_id: [1,2])

You can use below query to get venue details based on category
venues = Venue.joins(:categories_venues).where(:categories_venues => {:category_id => values})

Related

How to query the Noticed gem json field

I am using Noticed gem for my notifications and I am trying to query for the notifications related to the post.
The notification stores the post as an object inside params.
#<Notification id: 10, params: {:post=>#<Post id: 3}>
I can target like
notification.params[:post][:id]
=> 3
What I am trying to accomplish is something like this:
Notifications.where(params[:post][:id] => 3)
Is there a way to do that or my approach should be different?
Edited:
Here is an example of a notification in the database:
#<Notification id: 10, recipient_type: "User", recipient_id: 8, type: "MessageNotification", params: {:user=>#<User id: 3, email: "test2#test2.com", created_at: "2021-01-06 23:34:46", updated_at: "2021-04-15 17:47:54", admin: true>, :conversation=>#<Mailboxer::Conversation id: 6, subject: "Hello", created_at: "2021-05-14 00:14:41", updated_at: "2021-05-14 00:26:06">}, read_at: nil, created_at: "2021-05-14 00:26:06", updated_at: "2021-05-14 17:11:50">
squema.rb
t.jsonb "params"
A query suggested by #Joel_Blum and the slq call:
>> current_user.notifications.where('params #> ?', {conversation: {id: 6}}.to_json).to_sql
=> "SELECT \"notifications\".* FROM \"notifications\" WHERE \"notifications\".\"recipient_id\" = 8 AND \"notifications\".\"recipient_type\" = 'User' AND (params #> '{\"conversation\":{\"id\":6}}')"
Returns => #<ActiveRecord::AssociationRelation []>
It seems the gem creates either a jsonb or json column depending on your db.
So what want is perform a search on a json(b) column
For postgres for example it can be done like this
Notification.where('params #> ?', {post: {id: 3}}.to_json)
For whatever reason I am getting an empty array on a notification that I am sure exists.
query: Notification.where('params #> ?', {comment: {id: testc.id}}.to_ json)
SQL: "SELECT \"notifications\".* FROM \"notifications\" WHERE (params #> '{\"comment\":{\"id\":656}}')"
Noticed has a built in way of querying for objects you send in the params.
If you add has_noticed_notifications to the Post model, you should then be able to call #post.notifications_as_post
This is in the Noticed readme here. I definitely came here and found this question before I found the details in the readme!

Compare two records and get the names of the changed columns in rails active record

i need to compare two active records and get the name of the changed column
#<Evaluation id: 1, name: "Foo", status: "yes", comments: "can be better", created_at: "2017-05-09 12:00:00", updated_at: "2017-05-09 12:00:00">
#<Evaluation id: 2, name: "Foo", status: "yes", comments: "keep up the good work", created_at: "2017-05-09 12:05:00", updated_at: "2017-05-09 12:05:00">
I need to compare these two records and get the names of the changed columns. In this case :comments, :created_at, :updated_at
I have tried using methods like eql?, record1.attributes.except('id') == record2.attributes.except('id')
These only return either true or false I need to get the column names as well.
This can be done by comparing each column but i am looking for any other option.
You can use a small method to do this for you :
def diff_active_record(record_a, record_b)
(record_a.attributes.to_a - record_b.attributes.to_a).map(&:first)
end
This method will take the two active_record objects(in this case : record_a , record_b ) to compare and generate an array of attribute names that have changed.
You may use it like this:
diff_active_record(Evaluation.find(1), Evaluation.find(2))
The above would result in [:id, :comments, :created_at, :updated_at]

Deleting records from HABTM association

I'm trying to do something fairly simple. I have two models, User and Group. For simplicity's sake, let's say they look like this:
class User < ActiveRecord::Base
has_and_belongs_to_many :groups
end
and
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
end
Now, for some reason, I have a user that has the same group twice. In the Rails Console:
user = User.find(1000)
=> #<User id: 1000, first_name: "John", last_name: "Doe", active: true, created_at:
"2013-01-02 16:52:36", updated_at: "2013-06-17 16:21:09">
groups = user.groups
=> [#<Group id: 1, name: "student", is_active: true, created_at: "2012-12-24 15:08:59",
updated_at: "2012-12-24 15:08:59">, #<Group id: 1, name: "student", is_active: true,
created_at: "2012-12-24 15:08:59", updated_at: "2012-12-24 15:08:59">]
user.groups = groups.uniq
=> [#<Group id: 1, name: "student", is_active: true, created_at: "2012-12-24 15:08:59",
updated_at: "2012-12-24 15:08:59">]
user.save
=> true
And there is some SQL output that I've silenced. I would think that everything should be all set, but it's not. The groups aren't updated, and that user still has both. I could go into the join table and manually remove the duplicates, but that seems cludgy and gross and unnecessary. What am I doing wrong here?
I'm running Rails 3.2.11 and Ruby 1.9.3p392
Additional note: I've tried this many different ways, including using user.update_attributes, and using group_ids instead of the groups themselves, to no avail.
The reason this doesn't work is because ActiveRecord isn't handling the invalid state of duplicates in the habtm association (or any CollectionAssociation for that matter). Any ids not included in the newly assigned array are deleted - but there aren't any in this case. The relevant code:
# From lib/active_record/associations/collection_association.rb
def replace_records(new_target, original_target)
delete(target - new_target)
unless concat(new_target - target)
#target = original_target
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
"new records could not be saved."
end
target
end
The 'targets' being passed around are Arrays of assigned records. Note the call to delete(target - new_target) is equivalent in your case to delete(user.groups - user.groups.uniq) which results in an empty Array passed (since comparison is based on the id attribute of each record).
Instead, you'll need to clear out the association and then reassign the single group again:
group = user.groups.first
user.groups.clear
user.groups << group
This might be a way to cleanup those duplicates (it handles any number of groups of duplicate associations):
user = User.find(1000)
user.groups << user.groups.group_by(&:id).values.find_all {|v| v.size > 1}.each {|duplicates| duplicates.uniq_by! {|obj| obj.id}}.flatten.each {|duplicate| user.groups.delete(duplicate)}

Rails active record query, serialized array

Suppose I have Users data that store array of pet in String datatype
[
#<User id: 1, name: "John", pets: "---\n- cat\n- dog\n- bunny\n- ''\n">,
#<User id: 2, name: "Pete", pets: "---\n- dog\n- bunny\n- ''\n">,
#<User id: 3, name: "Jack", pets: "---\n- cat\n- ''\n">,
#<User id: 4, name: "Kurt", pets: "---\n- cat\n- bunny\n- ''\n">
]
Can i get all users that has a cat? Maybe something like User.find_all_by... or User.where(....) or anything that return as a relation? So i can order with active record query.
I know i can get all users that has a cat with
User.all.select{|s| YAML.load(s.pets).include?'cat'}
, but it convert to array that cannot be ordered with active record query.
thx for helping.
You could use simple SQL to see if 'cat' shows up in the serialized column.
User.where('pets LIKE "%cat%"').all
You need to normalize your data, add Pet model and set has_and_belongs_to_many association between theese models.

How do I delete all instances of an object that has an association that has already been deleted at the command-line in Rails 3?

I have a model Comment
Comment(id: integer, body: string, notified: boolean, user_id: integer, stage_id: integer, created_at: datetime, updated_at: datetime)
A comment belongs_to : stage.
So when I assign a comment, say with ID12 to the local variable comment12 in my Rails console, I get something like this:
comment12.stage => #<Stage id: 19, project_id: 8, user_id: 1, name: "Wedding", created_at: "2011-02-25 09:05:35", updated_at: "2011-02-25 09:05:35">
However, before I setup the dependency on the associations to destroy all comments when a stage was deleted, I have some errant comments left in the db.
So how do I delete all comments, from the Rails console, that have a stage_id that no longer exists ?
Is there a quick command I can run that makes it easy to do ?
Thanks.
This should work:
Comment.joins("LEFT OUTER JOIN stages ON comments.stage_id = stages.id").where("stages.id IS NULL").each do |comment|
comment.destroy
end

Resources