Exists in nested collection cannot find without ID? - ruby-on-rails

Hey guys another rails issue,
Currently have a collection that is line items for an invoicing system. I want to increment the count of the line items if I add in an item that already exists. At the moment I'm using an exists? query on the collection but it seems to return regardless of the key.
The foreign key I'm using is item_id, so I try to do invoice_items.exists?(:item_id => item.id)
This wasn't returning, so I changed it to invoice_items.find(:conditions => ["item_id == ?", item.id) and I get a return that I cannot search without invoiceItem ID.
Ideas?

conditions => ["item_id == ?", item.id
should be
conditions => ["item_id = ?", item.id]
So your query look like this
invoice_items.find(:all).conditions => ["item_id = ?", item.id]

you should just need to do either
invoice_items.all(:conditions => ["item_id == ?", item.id])
OR
invoice_items.first(:conditions => ["item_id == ?", item.id])
and you can use the syntax
invoice_items.all(:conditions => {:item_id => item.id})
if you are going to use the model.find command the first parameter needs to be :all, :first, :last or the primary key you are searching for. that is why I generally prefer to use Model.find only if I am searching for an id, otherwise I use Model.first, Model.last, Model.all. That way you know what you are going to get.

Related

Filter activerecord by nested ID and value combination in Ruby on Rails

Lets say I have two models with a many to many relationship: Item and Property
Now I have an array of properties and I want to filter all items which properties match a given value (lets say a boolean value: property.value = true)
When I try
#items = Item.includes(:properties).where(:properties => {:id => [1,2,3].to_a, :value => true})
I would like to get all items where property(1) is true AND property(2) is true and so on. But with the code above I get all items related to the property id's and where any property is true. How should I change my code?
I would appreciate not to use a gem for this.
Looks like you are almost there:
property_ids = [1,2,3]
Item.joins(:properties).
where(:properties => { :id => property_ids, :value => true }).
group('items.id').
having('COUNT(properties.id) >= ?', property_ids.size)
joins does an INNER JOIN and is preferred over includes when you really need to join tables.
where is basically the conditions you already had, the only change is that there is not need to call to_a on the array.
Than you have to group to make that COUNT in SQL work.
having extracts the lines that have at least the expected number of property lines matching the condition.

find all :conditions id found in an array of values

I have 2 models (player and team linked through the model lnkteamplayer)
Team has_many players through lnkteamplayer
Player has_many teams through lnkteamplayer
I need to retrieve all players not belonging to a specific team.
<% #players = Player.find(:all, :conditions => ["id != ?",#team.lnkteamplayers.player_id ]) %>
I am getting an error with above line of code. My question is how do i pass an array of values in the above condition.
Thanks for any suggestion provided.
You've got a couple of problems there:
1) the first part of conditions, "id != ?", is a fragment of sql, and in sql you do "not equals" as <> not !=. Eg "id <> ?"
2) To use an array, the sql syntax is id in (1,2,3) or id not in (1,2,3). In your conditions you can do this like :conditions => ["id not in (?)", array_of_ids]
So, you could get players not on a team like this:
#team = Team.find(params[:team_id])
#not_on_team = Player.find(:all, :conditions => ["id not in (?)", #team.player_ids])
Since you haven't provided an error message, I am kind of guessing here. However, I don't think != is a valid syntax in many SQL dialects. You are probably looking for something like NOT IN () instead.
Also, #team.lnkteamplayers.player_id probably doesn't work since the value returned from #team.lnkteamplayers likely doesn't have a player_id method; you might want the ids of the actual players instead.
That can be done using something like #team.lnkteamplayer_ids.
All in all, your line probably needs to look like
<% #players = Player.find(:all, :conditions => ["id NOT IN (?)", #team.lnkteamplayer_ids]) %>
but without more information we can't say for sure.

How to return a single attribute instead of all models

Usually a query returns a user model array, but I want a name array. Is there some convenient way to implement this?
Here you go:
names = User.find(:all, :conditions => ['age > 10'], :select => 'name').collect {|obj| obj.name }
Here's the Ruby 1.9 + Rails 3 way to do it.
names = User.select(:name).map(&:name)

Rails: combining optional params into a query

I have a view with a huge paginated list of records that need filtering.
Users can filter by records a few different ways (such as 'saved' records, 'read' records, and 'mark deleted' records) and I'd like for them to be able to combine these filters any possible way.
My current, flawed, non-functioning approach. The code below does not produce anything unless all of the params are specified and valid:
#view. Set the 'se' filter to true; leave all others as is
<%= link_to list_url(:id=>params[:id], :se=>"true", :st=>params[:st], :re=>params[:re]) do %>
<div class="button">Toggle SE</div>
<% end %>
#controller query. Add whichever params are passed into the conditions for the new page.
#query is paginated and sorted
#records = Record.where("user_id IN (?) AND see = ? AND star = ? AND delete = ? AND like = ?", #users.select("id"), params[:se], params[:st], params[:re]).paginate :page => params[:page], :order => (sort_column + " " + sort_direction)
What is the best way to create this filtering system?
I imagine a client-side sort would be faster than asking the server to become involved every time - is there a simple AJAX way to accomplish this kind of thing? Imagine filters the user can toggle on and off in any combination.
Try this:
conditions = {:user_id => #users.select("id")}
{
:se => :see,
:st => :star,
:del => :delete
}.each{|k1, k2| conditions[k2] = params[k1] unless params[k1].blank?}
#records = Record.where(conditions).paginate(...)
The conditions hash will be filled based on the values present in the params hash.
Edit 1
You can combine conditions hash and array.
#records = Record.where(conditions).where(
":created_at > ?", Date.today - 30).paginate(...)
You can change the user_id condition to what ever you want by specifying
conditions[:user_id] = #user.id
In the above statement, if the RHS is an array, rails automatically generates the IN clause. Otherwise, equality check(=) is performed.
Can also use Anonymous scopes: Combine arrays of conditions in Rails

Group and count in Rails

I know I've seen this before but I can't find anything now. I want to group a query by a certain column and be able to display how many are in each group. I got the first part down:
#line_items = #project.line_items.all(:group => "device_id")
This is for my line item index view, which is just a table displaying the line items. How do I make a column in that table for "count" now that the line items are grouped by device?
You can do count on line_items which will return you an ordered hash of device_id and count.
#project.line_items.group(:device_id).count
hash of devise_id as key and associated records count
#project.line_items.group(:device_id).count
I think you can try this as well.
#project.line_items.group(:device_id).pluck("device_id, count(device_id)")
^^ This gives array of arrays with elements 'device_id and count'
Just add a :select option:
#line_items = #project.line_items.all(
:group => "device_id",
:select => "device_id, COUNT(*) as count"
)
Then each #line_item will have a count attribute.
something like
User.all(:joins => :comments, :select => "users.*, count(comments.id) as comments_count", :group => "users.id")
might also work...
After this commit:
https://github.com/rails/rails/commit/a1c05dd8b9bd3623289d3aa73dda2943d620cc34
there's a new way to do the same thing:
#project.line_items.count(:group => LineItem.arel_table[:device_id])
For only count pluck would be faster here rather than group
#project.line_items.pluck(:device_id).count
#project.line_items.pluck(:device_id).uniq.count

Resources