I have following association
class Location < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :location
end
Suppose I have some instances of Location, I what to query all items belong to those locations. Currently I managed to get the result as an array
items =[]
Location.near(latitude,longitude,distance).find_each do |location|
location.items.find_each do |item|
items << item
end
end
However, is there any way I can get the results as ActiveRecord::Relation. Because I want to further query the results by using "where" with ActiveRecord::Relation.
P.S. The "near" method is from geocoder gem, it returns a ActiveRecord::Relation.
---------------------Edit----------------------------
Thank you for replies I nearly find the solution
locations = Location.near(latitude,longitude,distance)
Item.where(location_id: locations.pluck(:id))
Is it the right way to do it? to me it is a bit unintuitive.
----------------------Edit again ---------------------------
Just a small comment: I say it is unintuitive because I am switching from DataMapper. If it is Datamapper, it would be quite simple, like
Location.near(blabla).items
It is very simply to make queries through associations. Compared to Datamapper, can not understand why ActiveRecord association is so useless?
Edit to use one query with mapping...
What billy said above, but another option that might be faster:
locations = Location.near(1, 2, 3)
items = Item.where(:location_id => locations.map(&:ids)
Related
I need to get all children from a parent as an ActiveRecord::Relation. Thing is, this children are stored in a polymorphic relation. In my case I need it to paginate some search results obtained with pg_search gem.
I've tried the following:
results = PgSearch.multisearch('query').map(&:searchable)
# Horrible solution, N + 1 and returns an array
docs = PgSearch.multisearch('query').includes(:searchable)
results = docs.map(&:searchable)
# Still getting an array
Also thought of things like select or pluck, but they are not intended for retrieving objects, only column data. I could try to search ids for each children type like so
Post.where(id: PgSearch.multisearch('query').where(searchable_type: "Post").select(:searchable_id)
Profile.where(id: PgSearch.multisearch('query').where(searchable_type: "Profile").select(:searchable_id)
But it doesn't scale, since I would need to do this for every object I want to obtain from a search result.
Any help would be appreciated.
EDIT
Here's some basic pseudocode demonstrating the issue:
class Profile < ApplicationRecord
has_one :search_document, :as => :searchable
end
class Post < ApplicationRecord
has_one :search_document, :as => :searchable
end
class Profile < ApplicationRecord
has_one :search_document, :as => :searchable
end
class SearchDocument < ApplicationRecord
belongs_to :searchable, plymorphic: true
end
I want to obtain all the searchable items as an ActiveRecord::Relation, so that I can dynamically filter them, in this specific case, using limit(x).offset(y)
SearchDocument.all.joins(:searchable).limit(10).offset(10)
Generates an error: cannot eagerly load searchable cause of polymorphic relation
SearchDocument.all.includes(:searchable).limit(10).offset(10)
This one does load the searchable items into memory, but does not return them in the query, instead it applies the filters to the SearchDocument items, as expected. This might be a temporary solution, to filter the search documents and then get the searchable items from them, but collides with pagination on the views.
The question here is: Is there a way I can get all searchable items as ActiveRecord::Relation to further filter them?
I'm unfamiliar with this library. However, looking at your code, I'd guess that any attempt to take a CollectionProxy and map some function over it will trigger evaluating the CollectionProxy and return an array.
Having had a quick look at the library GitHub docs, perhaps something like this might work:
post_docs = PgSearch.multisearch('query').where(searchable_type: "Post")
posts = Post.where(pg_search_document: post_docs)
iOS developer learning Rails here. Trying to query active record for records based on a has_many relation's property. Apologies if this is simple but I just can't figure it out. I've read about and have been trying to use scope, .where, .joins, but there are so many contradicting posts and blogs online I'm unsure which to use and what's correct...
On to the problem:
I have two ActiveRecord models:
class User < ActiveRecord::Base
has_many :items
end
and
class Item < ActiveRecord::Base
belongs_to :user
end
An item has a property title, I am trying to find all of the Users that have an item with a title that is similar to some search parameter in string format.
I have managed to do such using a search for items and then .map like this:
users_owning_item_in_search_parameter = Item.where{ (title =~ my{#search_param + "%"}) }.map! { |i| i.user }
(that syntax comes from the squeel gem.)
But that command returns an Array when I want an ActiveRecord::Relation, because I need to do some further filtering that requires this instance type.
Any help much appreciated.
I think you're looking for something like this:
User.joins(:items).where('items.title LIKE ?', "#{#search_param}%")
You'll have to modify it a bit if you want to take advantage of squeel.
This may be a simple question, but I seem to be pulling my hair out to find an elegant solution here. I have two ActiveRecord model classes, with a has_one and belongs_to association between them:
class Item < ActiveRecord::Base
has_one :purchase
end
class Purchase < ActiveRecord::Base
belongs_to :item
end
I'm looking for an elegant way to find all Item objects, that have no purchase object associated with them, ideally without resorting to having a boolean is_purchased or similar attribute on the Item.
Right now I have:
purchases = Purchase.all
Item.where('id not in (?)', purchases.map(&:item_id))
Which works, but seems inefficient to me, as it's performing two queries (and purchases could be a massive record set).
Running Rails 3.1.0
It's quite common task, SQL OUTER JOIN usually works fine for it. Take a look here, for example.
In you case try to use something like
not_purchased_items = Item.joins("LEFT OUTER JOIN purchases ON purchases.item_id = items.id").where("purchases.id IS null")
Found two other railsey ways of doing this:
Item.includes(:purchase).references(:purchase).where("purchases.id IS NULL")
Item.includes(:purchase).where(purchases: { id: nil })
Technically the first example works without the 'references' clause but Rails 4 spits deprecation warnings without it.
A more concise version of #dimuch solution is to use the left_outer_joins method introduced in Rails 5:
Item.left_outer_joins(:purchase).where(purchases: {id: nil})
Note that in the left_outer_joins call :purchase is singular (it is the name of the method created by the has_one declaration), and in the where clause :purchases is plural (here it is the name of the table that the id field belongs to.)
Rails 6.1 has added a query method called missing in the ActiveRecord::QueryMethods::WhereChain class.
It returns a new relation with a left outer join and where clause between the parent and child models to identify missing relations.
Example:
Item.where.missing(:purchase)
I am feeling a bit slow when it comes to rails and the Active Record associations... I have two tables.
Table = Rings
Table = Variations with foreign_key => "ring_id".
class Ring < ActiveRecord::Base
has_many :variations
end
class Variation < ActiveRecord::Base
belongs_to :ring
end
So in my "index/list" view i want to display all the rings, and both the variations, and i was thinking it would be possible to do this through one SQL query... however, i have tried the join and the include methods and i think i am just not understanding how they work properly.
So my question is, how would i write a query in my controller, that would pull my "title" and "value" column values from the "variations" and combine them into one simple object for easy looping? Or do i have to loop through all rings and look up the variation values during the loop?
thanks
In your controller:
#rings = Ring.includes(:variations).all
In index.html.erb:
#rings.each do |ring|
...
ring.variations.each do |variation|
...
end
end
The includes portion of the query will prevent Rails from repeatedly querying the database as you loop through and render your rings and variations in the view.
You need to use the includes method: Ring.inclues(:variations). Then the variation will be loaded along with the rings in a single SQL query.
For more info: http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
Live site: http://iatidata.heroku.com
Github: https://github.com/markbrough/IATI-Data
Based on aid information released through the IATI Registry: iatiregistry.org
I'm a bit of a Rails n00b so sorry if this is a really stupid question.
There are two key Models in this app:
Activity - which contains details
such as recipient country, funding
organisation
Transaction - which contains details such as how much money (value) was committed or disbursed (transaction_type), when, to whom, etc.
All Transactions nest under an Activity. Each Activity has multiple Transactions. They are connected together by activity_id. has_many :transactions and belongs_to :activity are defined in the Activity and Transaction Models respectively.
So: all of this works great when I'm trying to get details of transactions for a single activity - either when looking at a single activity (activity->show) or looping through activities on the all activities page (activity->index). I just call
#activities.each do |activity|
activity.transactions.each do |transaction|
transaction.value # do something like display it
end
end
But what I now really want to do is to get the sum of all transactions for all activities (subject to :conditions for the activity).
What's the best way to do this? I guess I could do something like:
#totalvalue = 0
#activities.each do |activity|
activity.transactions.each do |transaction|
#totalvalue = #totalvalue + transaction.value
end
end
... but that doesn't seem very clean and making the server do unnecessary work. I figure it might be something to do with the model...?! sum() is another option maybe?
This has partly come about because I want to show the total amount going to each country for the nice bubbles on the front page :)
Thanks very much for any help!
Update:
Thanks for all the responses! So, this works now:
#thiscountry_activities.each do |a|
#thiscountry_value = #thiscountry_value + a.transactions.sum(:value)
end
But this doesn't work:
#thiscountry_value = #thiscountry_activities.transactions.sum(:value)
It gives this error:
undefined method `transactions' for #<Array:0xb5670038>
Looks like I have some sort of association problem. This is how the models are set up:
class Transaction < ActiveRecord::Base
belongs_to :activity
end
class Activity < ActiveRecord::Base
has_and_belongs_to_many :policy_markers
has_and_belongs_to_many :sectors
has_many :transactions
end
I think this is probably quite a simple problem, but I can't work out what's going on. The two models are connected together via id (in Activity) and activity_id (in Transactions).
Thanks again!
Use Active Record's awesome sum method, available for classes:
Transaction.sum(:value)
Or, like you want, associations:
activity.transactions.sum(:value)
Let the database do the work:
#total_value = Transaction.sum(:value)
This gives the total for all transactions. If you have some activities already loaded, you can filter them this way:
#total_value = Transaction.where(:activity_id => #activities.map(&:id)).sum(:value)
You can do it with one query:
#total_value = Transaction.joins(:activity).where("activities.name" => 'foo').sum(:value)
My code was getting pretty messy summing up virtual attributes. So I wrote this little method to do it for me. You just pass in a collection and a method name as a string or symbol and you get back a total. I hope someone finds this useful.
def vsum collection, v_attr # Totals the virtual attributes of a collection
total = 0
collection.each { |collect| total += collect.method(v_attr).call }
return total
end
# Example use
total_credits = vsum(Account.transactions, :credit)
Of course you don't need this if :credit is a table column. You are better off using the built in ActiveRecord method above. In my case i have a :quantity column that when positive is a :credit and negative is a :debit. Since :debit and :credit are not table columns they can't be summed using ActiveRecord.
As I understood, you would like to have the sum of all values of the transaction table. You can use SQL for that. I think it will be faster than doing it the Ruby way.
select sum(value) as transaction_value_sum from transaction;
You could do
#total_value = activity.transactions.sum(:value)
http://ar.rubyonrails.org/classes/ActiveRecord/Calculations/ClassMethods.html