There is a store that has a lot of addresses, comments (reviews) are written to the addresses.

@addresses = Address.where(shop_id: @shop.id) 

Next you need to get all confirmed records. I tried to do this:

 @addresses.each do |addr| @comments = addr.comments.confirmed end 

But of course this only works for one last address. I tried

 @comments = Comment.new @addresses.each do |addr| @comments = @comments.merge(addr.comments.confirmed) end 

Error messages:

NoMethodError (undefined method `merge' for#<Comment:0x007ff493627ef8>)

NoMethodError (undefined method `merge' for nil:NilClass) # если @comment = Comment.new

Comment Model :

 class Comment < ActiveRecord::Base belongs_to :address scope :with_text, -> { where.not(:text => nil) } scope :confirmed, -> { where(:confirmed => true) } scope :starred, -> { where.not(:stars => nil) } def confirm! self.update_attributes(:confirmed => true) end end 
  • @ D-side, comments have the field confirm: boolean, for confirmation by moderators - Tiazar
  • writes all comments to @comments which confirm confirm - Tiazar
  • Remove comments that are already irrelevant. Moderators will see them anyway, if you care about it :) Last detail. Do you have has_many :addresses in your Shop has_many :addresses ? If yes, why not use, if not, is it intentional? - D-side
  • And do I understand the current question correctly: having a store, get all confirmed comments to its addresses . It's like that? - D-side
  • Yes, the store model has has_many: addresses, maybe I just don’t understand something. Yes, the task can be considered as such - Tiazar

1 answer 1

As a rule, if you need a collection of models, it is better to receive it directly from the database. That is, gluing with Ruby (which you tried to do with merge ) you usually don't want to do. Well, this merge really does not exist.

Naive and slow way

If you need this way, then collections obtained from the database can be glued together in the manner of arrays with the help of the operator | which implicitly leads the collection to an array and makes the union of the sets of elements of these arrays.

 @comments = Comment.none # NULL relation, коллекция, не содержащая записей вообще никогда @addresses.each do |addr| @comments |= addr.comments end 

It is frighteningly slow. Therefore, it only suits you if everything is completely bad and there are no alternatives .

But there is an alternative .

Fast way with DB

The basis is the ability to connect (a term from relational algebra , in SQL just JOIN ) tables in queries, in order to apply conditions not only on the attributes of the records themselves, but also on other records associated with them.

It sounds difficult, it’s easy to work with (and it’s just a problem with performance, although it’s much more difficult than doing the same on the Ruby side).

We need:

  • get comments
  • filter by their own field (skoup)
  • under connect them to their addresses
  • and then filter the addresses

And-and:

 @comments = Comment.confirmed.joins(:address).merge(@shop.addresses) # ^ скоуп ^ соединение ^ подмешать ^ скоуп по другой модели 

It turns out this kind of SQL:

 SELECT comments.* FROM comments INNER JOIN addresses ON addresses.id = comments.address_id WHERE addresses.shop_id = 16 AND comments.confirmed 
  • In a join, the joined fields are derived from the association. Comment belongs_to :address converted to a connection comments with addresses on the "predicate" (condition) addresses.id = comments.address_id
  • merge allows you to add conditions to different models in one request. But it will work only if there is a connection , which is done here.
  • @shop.addresses is just a query with the condition addresses.shop_id = ? as a scop, just the parameter is taken implicitly
  • I tried the "quick way", but I get "TypeError (no implicit conversion of Symbol into String)" - Tiazar
  • @Tiazar my cant, corrected. The joining method is called joins , do not join , periodically% confuses me) - D-side
  • Sumptuously! perfect solution! Thank you. - Tiazar