n+1 for counting items in a has_many association - ruby-on-rails

(Using rails 4)
I have an Event model that has_many subscriptions. In the list of events, I display the number of subscriptions. To do this in my view for every event I have:
event.subscriptions.count
However, this causes a performance hit because for every event a subsequent query is performed to obtain the subscriptions count.
To fix this I thought to get all the subscriptions, something along these lines:
subscription_count = Event.all.map { |e| {e.id => e.subscriptions.count} }
Then instead of performing a query for every event, I would simply look up the event id in the subscription_count hash. However, this obviously still performs all the queries separately. Is there a way to perform some aggregate query to do the job in one go?
Thanks!

You can set a counter_cache column in your event table for the subscription. another way you can fetch events like Event.includes(:subscription) and in the view you can use e.subscription.size instead of count also you can check this link also
if you only need the count of the associated model
http://railscasts.com/episodes/23-counter-cache-column

You can do sth like:
#events = Event.joins('LEFT JOIN subscriptions ON subscription.event_id = event.id').group('events.id').select('events.*, COUNT(subscriptions.id) AS number_of_subscriptions')
And then inside the view use:
event.number_of_subscriptions

Related

Rails - finding an object in an active record association by an attribute without issuing more queries

Very short question, I feel like the answer must be already on StackOverflow but I couldn't find it.
I have some incoming parameters. They each have a unique ID.
I query the database and get an active record association using something like:
existing_things = current_user.things.where(uuid: [param_uuids])
This gives me an Active Record Association of those objects.
However, what I later do is something like:
existing_things.where(uuid: some_specific_uuid)
Of course, when I run the above query, it issues an SQL statement.
What I would love to do is find an object in a pre-loaded Active Record array of objects, and return that object without issuing another query.
How should I do that?
The same way you would if it was an ordinary array: Enumerable#find.
existing_things.find { |t| t.uuid == some_specific_uuid }
(or select if you're expecting more than one match)
If you're doing a lot of lookups like that, you might want to get a Hash involved:
things_by_uuid = existing_things.index_by(&:uuid)
things_by_uuid[some_specific_uuid]
(again, if you're expecting more than one match per value, there's group_by)

Graph Model: How to mark items in Neo4J as Read/Viewed

[EDITED]
I am trying to build an application that allows users to receive messages. The model looks something like this:
(user:Person)-[:HAS_MESSAGE]->(message:Message)
Multiple users can have the same message.
I am attempting to show this on the UI with the following query:
MATCH (user:Person)-[:HAS_MESSAGE]->(message:Message) WHERE user.EmployeeId = 'XYZ123' RETURN message
But on the UI, I want to indicate the messages that they have not yet seen.
What would be the best way to model this in the Neo4J? Should I use a Label or a property? Also, how do I update their read state in the same query?
Regards
Kiran
[EDITED]
Here is one approach.
When creating new Message nodes, assign false to a wasRead property on the HAS_MESSAGE relationship. Then, when you want to get unread messages (and, at the same time, mark them as having been read):
MATCH (user:Person)-[r:HAS_MESSAGE]->(message:Message)
WHERE user.EmployeeId = 'XYZ123' AND r.wasRead = false
SET r.wasRead = true
RETURN message
For better performance, you should consider creating an index on the Person/EmployeeId combination, like so:
CREATE INDEX ON :Person(EmployeeId)
Unfortunately, you cannot set indexes on relationships.

find_by_sql which model should I use?

There are three tables:
users
schedules
schedules_users
The user-model and the schedules-model each have the has_and_belongs_to_many-relationship.
Now I simply want to do this:
user_id_binded = Schedule/User/Object/#I dont know!#.find_by_sql ["SELECT schedules_users.user_id FROM schedules_users WHERE schedules_users.schedule_id = ?", schedule.id]
#user_schedules_binded = User.find(user_id_binded)
BUT the return-value of the first find_by_sql must be a model, as I understood the Rails.Api correctly.
It's neither a user-model-return-value or a schedule-model-return-value.
In the schedules_users-table are all relationships between users and schedules.
So I want to get all users which are binded to a specific schedule.
First I thought this should be the right way to solve it, but at that moment I didn't know that the return-value must be a model.
How could I solve this problem?
It appears you have a schedule ID and want the users in the end - that can be done easier by join statement like #user_schedules_binded = User.joins(:schedules).where(schedules: { id: schedule_id })
Or, if you have the schedule object, schedule.users will do the same, both going through schedules_users table.

How to iterate over a list in groovy and add property for each object

I have a list of events like so:
events = Events.all
Events have a many-to-many relationship with Users via class UserEvents modeled after the approach from spring-security-plugin:
I would like to find out whether a User is attending an Event and I can do that by running this:
UserEvent.get(currentUserId, eventId)
Question
How can I do this over all the elements of my list events so that in my view layer I can easily find out whether currentUserId is going to the event?
You can query for all of the associated Users for an Event like this:
def user = ...
def events = UserEvent.findAllByUser(user).event
This is a fairly efficient query since it executes SQL similar to
select * from user_event where user_id=?
and then loads each Event. This is N+1 though since it loads each Event individually, so you can do it more efficiently with this:
def eventIds = UserEvent.findAllByUser(user).eventId
def events = Event.getAll(eventIds)
The first line just gets the event ids using the same SQL as above, then the second line runs SQL like
select * from event where id in (?, ?, ?, ...)
If you only need a subset of your events which the user attends, then go with what Burt Beckwith sugeested.
But, if you need the whole set of events and just add an attribute you should use metaClass.
event.metaClass.userAttends= ...
you can see explanations about metaClass here
Specifically in your situation i would get all ids as Burt Beckwith said and the check for each event if its id is in the list:
def eventIds = UserEvent.findAllByUser(user).eventId
expandedEvents = events.collect {event->
event.metaClass.usetAttends = eventIds.contains(event.id)
return event}
You could use grep() to filter the list of events by whether UserEvent.get() returns an item.
events.grep { UserEvent.get(currentUserId, it.id) }

rails scope returning one record

I have a Product object that has many InventoryCurrent objects. Once a month all inventory transactions will be summed and a new InventoryCurrent record will be created for each product in each warehouse. This is done so that the system does not have to do hundreds of calculations every time current inventory is requested.
I have created a from_warehouse scope as follows:
scope :from_warehouse, lambda {|warehouse| where(:warehouse_id =>warehouse) }
I would like to make a call to get the current inventory as follows:
product.current_inventories.from_warehouse(2).recent
This should return either the most current InventoryCurrent, or if one hasn't been created yet (because the product was just added) then it should return a new InventoryCurrent object with the proper warehouse_id and product_id set.
The problem is that scope returns a recordset, not a single object. I can't put this scope in a method and call .first because that might generate an error if the recordset is empty.
Any suggestions?
use find_or_create_by_warehouse_id_and_product_id, or find_or_initialize_by_warehouse_id_and_product_id. see this for more information.

Resources