Rails 3 HABTM find by record associated model attribute - sql

I think this is really basic but I'm horrible with SQL so I have no idea how to do it...
I have a standard HABTM relationship between two models, LandUse and Photo. So I have a land_uses_photos join table, and each model has the standard macro, like:
Photo
has_and_belongs_to_many :land_uses
The land use table has: ID, and Name(string).
I want to find Photos where Land Use Name = 'foo','bar',or 'baz'. How do I do that?
I looked at this question and tried:
Photo.includes(:land_uses).where('land_use.id'=>[6,7])
... but that gave me:
ActiveRecord::StatementInvalid: No attribute named `id` exists for table `land_use`
That's bogus, here's the schema.rb for both LandUse and the join table:
create_table "land_uses", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "display_order"
end
create_table "land_uses_photos", :id => false, :force => true do |t|
t.integer "land_use_id"
t.integer "photo_id"
end
So, how do I do this kind of find? And to make it only one question instead of two, how could I find with an "and" condition instead of only an "or" condition?
Thanks!

Photo.joins(:land_uses).where('land_uses.name' => ['foo', 'bar', 'baz'])

you gen an 'id' error since table name is: land_uses and not land_use

Related

Rails Postgres query, selecting only items that appear in all search params with associations

I'm looking to create a Postgres query based on a few user select params.
A user will select a shop and a start and end year. Once submitted I'm would only like to display average prices of items if they only exist in all years selected.
For example, a user selects a start date of 2014 and end date of 2018. Item banana features in all those years, but apple only features in 2014, 2015 and 2017. So my end result will only show the average price of bananas and not apples.
So far, this is the query I have developed. I'm unsure on how to best implement the part where only the item appearing in all search years is averaged.
#sale_averages = Sale.joins(:shops, :items).where('extract(year from season_year) < ? AND extract(year from season_year) > ? ', params[:start_year], params[:end_year])
.where('shops.name = ?', params[:select_shop])
.select('items.name, AVG(sale.price) as price').group('shop.name')
Schema
create_table "items", force: :cascade do |t|
t.string "name"
end
create_table "sales", force: :cascade do |t|
t.integer "shop_id"
t.integer "item_id"
t.date "season_year"
t.decimal "price"
end
create_table "shops", force: :cascade do |t|
t.string "name"
end
You could check for what items there is a record for every year. You can do that by checking if the number of distinct years for every item is equal to the total of years (using COUNT DISTINCT):
number_years = params[:end_year].to_i - params[:start_year].to_i + 1
#sale_averages = Sale.joins(:shops, :items)
.select('items.name, AVG(sale.price) as price')
.where("EXTRACT(year from season_year) BETWEEN #{params[:start_year]} AND #{params[:end_year]}")
.where('shops.name': params[:select_shop])
.group('items.name')
.having("(COUNT(DISTINCT(EXTRACT(year from season_year))) = #{number_years})")
I have also used BETWEEN instead of < and >.
I think you want to group by item name instead of shop (as it was in you original query).

Creating a feed (following) grouped by day and user that is ordered by most recent on ruby on rails

I am trying to do a feed (kinda timeline following feed in a daily manner)
I have a table photos where it stores photos uploaded by the user, a table follows which stores the user that each user is following.
create_table "photos", force: :cascade do |t|
...
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
...
create_table "follows", force: :cascade do |t|
t.integer "followable_id", null: false
t.string "followable_type", null: false
t.integer "follower_id", null: false
t.string "follower_type", null: false
...
t.datetime "created_at"
t.datetime "updated_at"
end
Basically, I want to accomplish a feed that looks like this (easier drawn than explain)
most recent appear at the top
group all photos uploaded by the user we are "following" including my own in "a day"
display today, yesterday, 2 days ago and so forth
Here's is one solution.
current_user_id = current_user.id
Photo.select(%q{
user_id,
COUNT(*) no_photos,
date_trunc('day', created_at::timestamp) as what_day
}).group("what_day, user_id").order("what_day DESC").where(%Q{
user_id IN (
(SELECT followable_id FROM follows where follower_id = #{current_user_id})
UNION ALL
(SELECT #{current_user_id} as followable_id)
)
})
date_trunc truncates the date to a specified precision, in this case the beginning of a day. I'm not sure what follows.followable_type does in your case, but the above should work.

Order Players on the SUM of their association model

I have a database with 6500 players and each player has an average of 15 game results.
Use case
I want to generate a list of players, ordered by the sum of their prize money (a field in the results table).
I prefer this to be in some sort of scope, so I can also filter the list on the player's country, etc.
Performance
I have seen posts that mention a cache_counter field for performance. In my case I have thousands of result records (75.000+) so I don't want the calculations being done every time someone visits the generated listings.
Question
What is the best pattern to solve this? And how do I implement it?
Models
class Player < ActiveRecord::Base
has_many :results
end
class Result < ActiveRecord::Base
belongs_to :player
end
Schemas
create_table "players", :force => true do |t|
t.string "name"
t.string "nationality"
end
create_table "results", :force => true do |t|
t.integer "player_id"
t.date "event_date"
t.integer "place"
t.integer "prize"
end
Update
What I am trying to accomplish is getting to a point where I can use:
#players = Player.order_by_prize
and
#players = Player.filter_by_country('USA').order_by_prize('desc')
You should be able to use something like this:
class Player
scope :order_by_prize, joins(:results).select('name, sum(results.prize) as total_prize').order('total_prize desc')
Refer rails api - active record querying for details.

Efficient query for distinct count and group with two columns

Given a simple model that consists of descriptions, tags, and some other fields
The results should be:
a list of all tags in Entry.all without duplicates (e.g. Entry.select("DISTINCT(tag)") )
the number of duplicates for each tag, also used to sort tags
all descriptions for each tag sorted alphabetically, again without duplicates (however, the exactly same description can exist with a different tag)
Is it possible to combine this in one (efficient) query?
Edit:
def change
create_table :entries do |t|
t.datetime :datum, :null => false
t.string :description
t.string :tag
(and some others)
end
add_index :entries, :user_id
end
It's better to create additional table:
rails g model Tag name:string description:string
rails g model Entry tag:references ...
And then just call them:
#entries = Entry.select('tag_id, count(tag_id) as total').group(:tag_id).includes(:tag)
After that, you will have all descriptions in your object:
#entries.first.tag.description # description of entry tag
#entries.first.tag.total # total number of such kind of tags
P.S.: Why just one tag per entry?

Rails 3 one-to-many relationship question - how to return column name value

I have a one-tomany relationship with 2 tables as follows:
Models:
class MediaType < ActiveRecord::Base
belongs_to :media
end
class Media < ActiveRecord::Base
has_many :media_types
end
SQL for simplicity sake are:
create_table :media do |t|
t.string "name", :limit => 255
t.integer "media_type_id"
end
create_table :media_types do |t|
t.string "name", :limit => 255
end
Once I insert a Media record relating to a media_type_id, how do I pull back the media_type.name value related to the media record?
I blindly tried:
media = Media.find(1)
media.media_type_id.name
But that didn't work of course. Is my SQL not Rails standards possibly?
Appreciate any help.
If you idea: media_type has many medias, but every media has only one media_type
You need another models:
class MediaType < ActiveRecord::Base
has_many :medias
end
class Media < ActiveRecord::Base
belongs_to :media_type
end
And
media = Media.find(1)
media.media_type.name
give you name
It seems that media has_many media_types.
In that case you would create media_id column in media_types table, but you did it other way around.
You will then approach each relation by
types = Media.media_types
to get the media_types that the Media has, and
media = MediaType.media
to get the media that mediatype belongs to.