Reputation: 847
Models
Users have clients, which have projects which have tasks.
I'm trying to build an array with [Name, Count] to send to a Google Chart - I have all of them but one resolved:
def task_by_project(user)
task_arr = user.projects.joins(:tasks).where("tasks.completed = ?", "0").uniq.collect { |d| [d.name, d.tasks.count] }.to_a
task_arr.unshift(['Projects', 'Uncompleted Tasks'])
return task_arr
end
This query is returning 11 entries. Which is actually -all- of the entries for this user. It's bypassing the where
completely. There is 7 uncompleted 4 completed.
Within the view I am rendering it slightly different with a loop:
<% current_user.projects.uniq.each do |project| %>
<%= link_to project.name + " || " + project.tasks.where(completed: 0).count.to_s + " tasks remaining currently", project, :class => "collection-item" %>
<% end %>
</div>
I'm fairly confident it has to do with my misunderstanding of how to query an association this deep.
The END RESULT is I need Project's Name, and the Count of how many Tasks it has.
I do this via query by joining Task
back to Project
on Project.ID
- But I'm not sure how to do that with an entire collection.
(The purpose of the 'unshift' is because other-wise I end up with a result format of [[['Projects', 'Tasks'], [['First Record, 2] ... ]]
which isn't GCharts format. Flatter killed it off -too- much)
Upvotes: 0
Views: 1083
Reputation: 1263
Maybe something like this?
projects = user.projects
.select("projects.name, COUNT(uncompleted_tasks.id) AS uncompleted_tasks_count")
.joins("LEFT OUTER JOIN tasks AS uncompleted_tasks ON uncompleted_tasks.project_id = projects.id AND uncompleted_tasks.completed = 0")
.group("projects.id")
.map { |project| [project.name, project.uncompleted_tasks_count] }
Upvotes: 1
Reputation: 102045
Use .pluck
to get the column values instead
user.projects.left_joins(:tasks).where("tasks.completed = 0")
.distinct
.pluck('users.id, users.name, tasks.count')
.map { |a| a.pop(2) }
.pluck
is far more efficient as it just queries the columns passed to pluck and returns an array of arrays instead of loading a bunch of models into memory.
# => [[1, 'Frank', 0], [2, 'Muhammad', 5], [3, 'Jane', 3]]
However unless your name column is unique you should select the user id to weed out duplicates. .map { |a| a.pop(2) }
gets rid of the id in the resulting arrays.
Pulling records out of the database and calling .uniq
on them is a huge antipattern. You're pulling tons of data out of the database and then sorting out the duplicates in Ruby. This can lead to severe performance issues.
Also a better way to handle states is by using a ActiveRecord::Enum:
class Task
enum :status, [:incomplete, :complete, :deleted]
end
Which will let you query:
Task.incomplete
Task.completed
And gives you interogration and mutation methods:
task.complete?
task.complete!
You can use it like so:
user.projects.left_joins(:tasks).where(tasks: Task.incomplete)
.distinct
.pluck('users.id, users.name, tasks.count')
.map { |a| a.pop(2) }
Upvotes: 0