I have a User model (has_many contacts) and a Contact model (belongs_to user). To send invites, I need to know if a user's contacts already exist in User table and update a status field in Contacts. My common field is phone number.
So oversimplifying I have:
User: id, phone
Contact: id, name, phone, status, user_id
How can I set status to "registered" for a user's contacts in an efficient manner?
I currently have this iteration, which works, but there has to be a better way:
all_contacts = Contact.where(user_id: user).where.not(phone: nil)
all_contacts.each do |contact|
a = User.find_by(phone: contact.phone)
if a
contact.status = 'registered'
contact.save
end
end
You can try following:
contacts = Contact.joins(:user).where("users.phone=contacts.phone and contacts.phone not NULL")
contact_ids = fetch ids from contacts array
Contact.update(your_array_of_ids, status: "registered")
If you want to update status of User to "registered" only if they have a phone number, then you could store their ids in an array(using where for example , and then you could use the update method like this:
User.update(your_array_of_ids, status: "registered")
Related
I am building a Rails 5.2 app.
In this app I got three objects:
User
Id
Firstname
Lastname
Conversation
Id
Entity_type
Entity_subtype
Assignment
Id
User_id
Assignable_id (ID of Conversation)
Assignable_type (Class name of Conversation)
One or more Users are connected to a Conversation through Assignments.
I am trying to create a query that checks if two Users are having a direct message Conversation or I need to create a new one automatically.
I have this code and it "works" (I get no error) but it returns a Conversation even though only one of the Users are involved in a Conversation. But I need it to only return a Conversation if Both Users share the Same Conversation, ie they are talking to each other.
Conversation.where(entity_type: "conversation", entity_subtype: "direct").joins(:assignments).where(:assignments => {:user_id => ["cdd3c6be-ac78-46f2-a7ae-7f2299b6fedb", "32117e53-9b2f-49c6-8cc8-3a9eb9003a2e"] })
One way to do this is by using GROUP and using HAVING to set a condition on the group:
class Conversion < ActiveRecord::Base
def self.between(*users)
joins(:assignments)
.group(:id)
.where(assignments: { user_id: users })
.having(Assignment.arel_table[:id].count.eq(users.length))
end
end
The advantage is that this approach can be used with any number of users.
If you change out .eq to .gteq you can get conversions that include the two users but isn't a private conversation. On Postgres you can use Assignment.arel_table[Arel.star] as an optimization.
Join assignments twice and in resulting rows, pick a row with assignment user ids match.
user_ids = ["cdd3c6be-ac78-46f2-a7ae-7f2299b6fedb", "32117e53-9b2f-49c6-8cc8-3a9eb9003a2e"]
Conversation
.where(entity_type: "conversation", entity_subtype: "direct")
.joins("LEFT JOIN assignments as1 ON assignments.assignable_id = conversations.id AND assignments.assignable_type = 'Conversation'")
.joins("LEFT JOIN assignments as2 ON assignments.assignable_id = conversations.id AND assignments.assignable_type = 'Conversation'")
.where('as1.user_id = as2.user_id')
.where('as1.user_id = ? AND as2.user_id = ?', user_ids[0], user_ids[1])
Will give you list of convos, both are involved
I have tables - users and articles. It is one to many relation or user can have many articles. What I want is to select all users from users table with LIMIT 40, but include count all articles for each user - add property for each user count_articles. I am trying in this way:
def self.get_users(limit, offset)
users = []
order('created_at').limit(limit).offset(offset).each do |user|
user.attributes[:count_articles] = user.articles.count
users << user
byebug
end
users
end
, but I am getting users without this attribute. If I use byebug and type in the
console - user.count_articles, I can see the result of count_articles for current user, but if I type only user, I see all the attributes without count_articles.
You could try with:
User.joins(:articles)
.select('users.id, users.name, COUNT(articles.id) AS count_articles')
.group('users.id')
.limit(40)
I'm doing s.th. similar with a sub-select. So no join is required.
Try putting this into your User Model:
def self.get_users(limit=40, offset=0)
order(:created_at)
.limit(limit)
.offset(offset)
.select('users.*, (SELECT COUNT(id) FROM articles WHERE articles.id = users.id) articles_count')
end
The resulting User instances in the returned Array will then have the attribute "articles_count".
I have two tables users and task_lists, users has_many task_lists.
task_lists belongs to users and has an attribute tasks_counter.
users
|id|
task_lists
|id|user_id|tasks_counter|
I would like to find all the users whose first (MIN(id)) tasks_list has a tasks_counter < 5.
How would I achieve this in PostGreSQL? I'm using Rails, if somebody knows a solution using ActiveRecords.
This will set users_ids variable with an Array containing all User id's whose first TaskList has a tasks_counter < 5:
user_ids = TaskList.select("MIN(id) AS id, user_id, tasks_counter")
.group(:user_id) # Get first TaskList for each user
.select { |t| t.tasks_counter < 5 } # Keep users tha meet criteria
.pluck(:user_id) # Return users' id in array
If you would like to get an ActiveRecord_Relation object with User objects you can use the result from the previous query and filter User.
users = User.where(id: user_ids)
Or, everything in one line:
users = User.where(id: TaskList.select("MIN(id) AS id, user_id, tasks_counter")
.group(:user_id)
.select { |t| t.tasks_counter < 5 }
.pluck(:user_id))
This is my problem. I am able to populate the list with data from a single table. But I need to read one table, and get data from another table based on those values.
there are 4 tables:
user (name and userid)
friends (usrid and friendid), friendid is the userid of the friend
accounts (acc_no and userid)
transactions (from, to and userid) from and to are account_nos and userid is the id of the "from" user
accounts calls the new transaction method. "from" is a parameter given to the new method here. I am able to find the userid using this account number like this:
#account=Account.find_by(:acc_no => params[:from])
#transaction.userid = #account.user_id
This works when I print it in the view.
Now, I need a drop down list for "to", which should be filled with all the account numbers of the friends that this user has. For this, I first found out the friends of this user like this:
#list1=Friend.where(:user_id => #transaction.userid)
To test this, I just used this list in my view in this way:
<%= f.select :to, #list1.friendid %>
This also worked. Now, using list1, i need to generate another list which consists of all the acc_no/accounts of the ids present in list1
This is where I got no output. I tried this:
#list2 = #list1.map{|i| Account.where(:user_id => i.friend_id)}
This is not working for me. basically I need to perform this query:
select * from accounts where user_id in (select friend_id from friends where user_id = (select user_id from account where acc_no = params[:from]))
I got the 2 nested queries, but am unable to get the equivalent of the outer query working
You can try passing in an array of IDs:
list1_ids = #list1.pluck(:id)
#list2 = Account.where(user_id: list1_ids)
I have a method on my User.rb model in my Rails app that returns a reputation score based on a user's various contributions to the site.
def total_votes
answerkarma = AnswerVote.joins(:answer).where(answers: {user_id: self.id}).sum('value')
contributionkarma = Contribution.where(user_id: self.id).sum('value')
answerkarma + contributionkarma
end
In the index action of my imaginary artists controller, sometimes I retrieve all artists, and sometimes I retrieve only those filtered by location
def index
if params[:province_id]
province = params[:province_id]
province = province.to_i
#artistsbyprovince = User.artists_by_province(province)
else
#artists = User.where(:onionpealer => true)
end
end
If I'm filtering by location, the User model calls this scope method
scope :artists_by_province, lambda {|province|
joins(:contact).
where( contacts: {province_id: province},
users: {onionpealer: true})
}
What I'd like to do, however, is, in both cases (whether filtering by location or retrieving all users) is to sort the retrieval of the artists by the results of total_votes method, so that artists with the highest number of points appear at the top.
Can you suggest how I might do that. Thank you in advance.
If do not pay attention on wrong query architecture, sorting can be done by ruby array#sort_by
#artistsbyprovince = User.artists_by_province(province).sort_by{ |u| -u.total_votes }