Rails - complicated query - ruby-on-rails

I am new to rails, and Im having hard time writing a query using rails' methods.
What I am trying to accomplish is to have a Canteen model that has many Meals, however I want to only display meals that are being served that day.
So I created a Canteen model, that has_many Meals.
Then I created Served_date model, that belongs_to meal and Meal has_many Served_dates so we can specify multiple dates when the meal is being served.
How would I make query like this?
//P.S.: Served_date.where(served_at: Date.today) returns Served_dates that are being served today
Thanks !

Firstly, the model name should not be underscored by convention, hence you should use ServedDate instead of Served_date.
This query should give you what you need:
Meal.joins(:served_dates).where(served_dates: {served_at: Date.today})
Better yet, you can parameterize it and turn it into a scope to be more portable:
class Meal < ActiveRecord::Base
scope :served_on, lambda{|date| joins(:served_dates).where(served_dates: {served_at: date}) }
end
And then call:
Meal.served_on(Date.today)

Related

How to access has_many through relationship possible in rails?

How can I access my related records?
class Post < ActiveRecord::Base
has_many :post_categories
has_many :categories, through: :post_categories
class Categories < ActiveRecord::Base
has_many :post_categories
has_many :post, through: :post_categories
class PostCategories < ActiveRecord::Base
belongs_to :post
belongs_to :category
PostCategories table has id, posts_id, and categories_id columns.
id | posts_id | categories_id
1. | 2 | 3
2. | 2 | 4
What I want is: to get posts related to a category. like: all Posts where in x category.
Yep, this is an easy one.
one_or_more_categories = # Category.find... or Category.where...
posts = Post.joins(:categories).where(category: one_or_more_categories)
Rails is clever enough to take either a model or a query that would find some data and turn that into an efficient appropriate query, that might be a subquery. Trying things out in the Rails console (bundle exec rails c) is a good way to see the generated SQL and better understand what's going on.
(EDIT: As another answer points out, if you've already retrieved a specific Category instance then you can just reference category.posts and work with that relationship directly, including chaining in .order, .limit and so-on).
Another way to write it 'lower level' would be:
Post.joins(:categories).where(category: {id: one_or_more_category_ids})
...which is in essence what Rails will be doing under the hood when given an ActiveRecord model instance or an ActiveRecord::Relation. If you already knew the e.g. category "name", or some other indexed text column that you could search on, then you'd adjust the above accordingly:
Post.joins(:categories).where(category: {name: name_of_category})
The pattern of joins and where taking a Hash where the join table name is used as a key with values nested under there can be taken as deep as you like (e.g. if categories had-many subcategories) and you can find more about that in Rails Guides or appropriate web searches. The only gotcha is the tortuous singular/plural stuff, which Rails uses to try and make things more "English-y" but sometimes - as in this case - just creates an additional cognitive burden of needing to remember which parts should be singular and which plural.
Not sure if this answers it but in ActiveRecord your Post will have direct access to your Category model and vice versa. So you could identify the category you want the posts from in a variable or an instance variable, and query #specific_category.posts. If you are doing this in your controller, you could even do it in before_action filter. If you are using it in serializers its not much different.
You could also create a scope in your Post model and use either active record or raw SQL to query specific parameters.
You also have an error in your Category model. Has many is always plural so it would be has_many :posts, through: :post_categories
Get the category object and you can directly fetch the related posts. Please see the following
category = Category.find(id)
posts = category.posts
Since you have already configured the has_many_through relation, rails will fetch post records related the category.

Get children of different models in one single query

class Category
has_many :images
has_many :articles
end
class Image
belongs_to :category
end
class Article
belongs_to :category
end
I'm trying to understand what solutions there are in Rails for children of different models to be queried by the same parent?
E.g. I'd like to get all images and articles that belong to the same category and sort them all by created_at.
You can try 'includes' in rails
Article.includes(:Category)
As I said it seems to me you can use eager loading multiple associations. In your case it could be something like this:
Category.where(id: 2).includes(:images, :articles).sort_by(&:created_at)
Basically you pass your desired Category ID and get :images, :articles which belongs_to Category with particular ID. sort_byprobably should do the sorting thing.
This blog post on eager loading could help you as well.
You can't simply force Active Record to bring all their dependences in a single query (afaik), regardless if is lazy/eager loading. I think your best bet is:
class Category
has_many :images, -> { order(:created_at) }
has_many :articles, -> { order(:created_at) }
end
categories = Category.includes(:images, :articles)
As long as you iterate categories and get their images and articles, this will make three queries, one for each table categories, images and articles, which is a good tradeoff for the ease of use of an ORM.
Now, if you insist to bring all that info in just one query, for sure it must be a way using Arel, but think twice if it worths. The last choice I see is the good old SQL with:
query = <<-SQL
SELECT *, images.*, articles.*
FROM categories
-- and so on with joins, orders, etc...
SQL
result = ActiveRecord::Base.connection.execute(query)
I really discourage this option as it will bring A LOT of duplicated info as you will joining three tables and it really would be a pain to sort them for your use.

Should I use a LIKE query for these ActiveRecord relationships?

Let's say I have a single web page form user interface with 2 sets of checkboxes. With set 1 checkboxes, I can check off what Trainers I would like ("Jason", "Alexandra, etc.) With set 2 checkboxes, I can check off what animals I would like to see ("Tigers", "Bears", etc.) Once I submit the form with these options, I get back a list of zoos that match the criteria (let's assume all the trainers work at all the zoos and all the animals are at all the zoos for discussion's sake)
We'll be running our database query by "name" (e.g., search using trainer names and animal names, NOT database ids)
Let's say we are using a Postgres database that has hundreds of thousands of rows (if not millions).
Is it more efficient to search using an "ILIKE" query or is it better to do a standard join query (e.g., Zoo.includes(:animals, :trainers).where("animals.name = ? and trainers.name = ?", animal_names, trainer_names)?
Is there a better way than what I just showed in #1 above?
model setup
class Zoo < ActiveRecord::Base
has_many :animals, through: zoo_animals
has_many :trainers, through: zoo_trainers
has_many :zoo_trainers
has_many :zoo_animals
end
class Animal < ActiveRecord::Base
has_many :zoos, through :zoo_animals
has_many :zoo_animals
end
class Trainer < ActiveRecord::Base
has_many :zoos, through :zoo_trainers
has_many :zoo_trainers
end
class ZooAnimal < ActiveRecord::Base
belongs_to :animal
belongs_to :zoo
end
class ZooTrainer < ActiveRecord::Base
belongs_to :zoo
belongs_to :trainer
end
EDIT: let's suppose I don't have access to the database ID's.
LIKE '%Jason%' is much less efficient than querying for the exact string 'Jason' (or querying for an ID), because while exact comparisons and some uses of LIKE can use an index on the column being queried, LIKE with a pattern beginning with a wildcard can't use an index.
However, performance doesn't sound like the most important consideration here. LIKE %Jason% will still probably be fast enough on a reasonably sized database under reasonable load. If the application really needs to search for things by substring (which implies that a search might have multiple results), that requirement can't be met by simple equality.
There are an endless number of higher-powered solutions to searching text, including Postgres built-in full-text search and external solutions like Elasticsearch. Without specific requirements for scaling I'd go with LIKE until it started to slow down and only then invest in something more complicated.

Rails - How to query records in 5 different tables by user_id

I have models UserVote, Comment, Edit, etc, all of which have a user_id attribute. I'm trying to create a sort of timeline of recent activity, and this has me querying all 5 of my models separately and sorting by datetime. However, with accounts that have a lot of activity, these 5 queries take a very long time to execute. I'd like to find a way to optimize the performance, and I figured combining the 5 queries might work.
I haven't been able to come up with any working query to achieve what I'd like.
Thanks for any help!
I think the best suggestion in the comments is from Steve Jorgensen, with "I have generally seen this done by adding records to an activity log, and then querying that.".
If you want to take this idea to the next level, check out sphinx (a search engine designed for indexing database content). You can integrate easily with rails using thinksphinx - http://freelancing-god.github.com/ts/en/.
Also, as Tim Peters brings up, you really should have indexs on all of your fkeys, regardless of how you solve this - http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index.
I think it is good idea to use Polymorphic associations for this problem - http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
class TimeLine < ActiveRecord::Base
belongs_to :timelineable, :polymorphic => true
end
class UserVote < ActiveRecord::Base
has_many :time_lines, :as => :timelineable
end
class Comments < ActiveRecord::Base
has_many :time_lines, :as => :timelineable
end
Now you can sort time_line and access associated resources.

Can I override what an association method returns?

In Rails, I want to override the behavior of an association. For example, by default, if Person has_many :hats, calling some_person.hats would do a simple join using person.id and hat.person_id.
I want to modify that query to include some other criteria. For example, maybe a person's collection of hats should be just the hats that are appropriate to their country.
It seems that I could do something like this:
class Person < ActiveRecord::Base
has_many :hats, :through => :country do
# John lives in Canada, so he gets a baseball cap and a hockey helmet
self.country.hats
end
end
Can I control what an association returns like this? If not, would a scope be the best solution?
I know this is a silly example, but explaining the domain logic that I need this for would be way too boring for everyone here. :)
Scopes are probably your best option because they're chainable and reusable outside your association. Otherwise, you could use association extensions. Check out this thread for more info. Association Extensions

Resources