Ruby on rails data fetching issue - mysql

Hello i'm new to rails
I have a table named 'messages' which has columns current_user__id , to_user_id and created time
I am trying to build a chat application where different users can chat individually and those message will be stored at messages table with their respective ids.
Now in order to print the messages on screen.
I'm facing issues
I need a query such that both the current_user__id and to_user_id conversations and to_user_id and current_user__id conversation will be listed by the latest created time.

i will assume that you have two ActiveModel's: User and Message. Make sure that you have classes like:
class User < ApplicationRecord
has_many :messages
end
class Message < ApplicationRecord
belongs_to :current_user, class_name: 'User', foreign_key: 'current_user_id'
belongs_to :to_user, class_name: 'User', foreign_key: 'to_user_id'
end
A small trivia when you add t.timestamps to your migrations it creates created_at and updated_at fields for you.
Now I will just hard code the raw sql query for you:
def get_messages(current_user_id, to_user_id)
#messages = Message.where(' current_user_id=? OR receiver_user_id=? OR current_user_id=? OR receiver_user_id=? ',
current_user_id, current_user_id, to_user_id, to_user_id).order('created_at DESC')
end
You can play with order('created_at DESC') in order if you just want in ascending order you can replace DESC with ASC or order(:created_at)
You may put any other query conditions also like not showing deleted messages etc. You can learn more from official Ruby on Rails document for Active Record Query Interface.

Related

Sorting associated objects based on the association's creation date

Right now, I'm working on a simple app. It requires to get the associated objects ordered by the date that they we're added to the object. For that, I want to order them based on the pivot-table's id.
My app looks a bit like this:
class Product < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :products
end
However, when a user wants to buy a product, I would add a new relation into the pivot table courses_users. When I then run #product.users, I will get them back in the order the users where created, not added as the relation.
I've tried creating a query scope, but it didn't work. I also tried to create a order on the has_and_belongs_to_many, as such:
has_and_belongs_to_many :users, order: 'course_users.id ASC'
But none of that seemed to work, no ORDER statement could be found in the logs.
Add the created_at field to your table.
rails g migration AddTimestampsToCourseUsers created_at:datetime
then you can
#product.users.order "course_users.created_at ASC"

Unable to make sense of database data in Rails console

I'm trying to gather a set of data for current_user so in my controller code I have:
#user_workouts = current_user.planned_workouts
with planned_workouts being the join table b/w the user and the workouts. When I test that in console:
#user_workouts.count
I get 4.
When I do:
PlannedWorkout.count
I get 4.
But when I try using a .each loop for my #user_workouts:
#user_workouts.each do |user_workout|
puts user_workout
end
I get far more than 4, I get something like ~25, how is that possible when I tested above how many planned_workouts there are in the table and for the user themselves? I also tried deleting all of the planned_workouts so that the table is empty and created 4 new ones, and still the same outcome. Can it have anything to do with the fact that I'm using add_index in the schema?
EDIT:
My model associations:
User.rb
has_many :planned_workouts
has_many :workouts, through: :planned_workouts
Workout.rb
has_many :planned_workouts
has_many :users, through: :planned_workouts
PlannedWorkout.rb
belongs_to :workout
belongs_to :user

Preventing duplicates via a custom foreign key in has_many :through

I'm trying to implement a two-way has_many :through association between a User model and a Location model using a UserLocations join table. This will enable setting user locations with built in ActiveRecord methods, ie. #user.locations = [<Location 1>, <Location 2>, ...]. My goal is to not associate locations to users individually, but rather for users to add and remove them, one or more at a time, via another field: :zip_code. This means that when a user adds a zip code, ActiveRecord should insert a single record into UserLocations (something like INSERT INTO user_locations (user_id, zip_code) VALUES (1, 12345)). Then, when #user.locations is called, ActiveRecord should join by :zip_code and get the matching location(s). My current implementation works, except that one INSERT into UserLocations is generated for each location associated with a zip code.
class User < ActiveRecord::Base
has_many :user_locations
has_many :locations, through: :user_locations
end
class UserLocation < ActiveRecord::Base
belongs_to :user
belongs_to :location, primary_key: :zip_code, foreign_key: :zip_code
end
class Location < ActiveRecord::Base
has_many :user_locations, primary_key: :zip_code, foreign_key: :zip_code
has_many :users, through: :user_locations
end
Things I've tried:
validates_uniqueness_of :zip_code, scope: :user_id - just throws a validation error and prevents all record creation
has_many unique: true - doesn't prevent duplicate DB queries
add_index unique: true for (user_id, zip_code) - would at least prevent duplicate entries from being created, but I'm trying to prevent unnecessary queries entirely
Using questions like this one for guidance hasn't gotten me any closer. Is what I'm trying to do possible without using my own methods to get/set user locations?
First of all, I'm not very experienced in rails yet, but I'll still try to help :)
What I would do is not using zipcodes as a key. When a user inputs zip codes you look up the code in the Location:
#zip_code = Location.where(zipcode: user_input).first
#zip_code.user_locations.create!(user_id #some other stuff you want)
This way you store the id of the location into the user location and no duplicates are made. You can then generate user locations by joining the UserLocation and Location.
But as I said, there may be a better way of doing this as I'm beginner.
Stop me if I'm wrong :)
You have zipcodes in your locations table (i.e: 111, 222, 333) When a user selects a zipcode of '111' for him self, his record is associated with the existing locations record; but when a user selects a zipcode of '444' a new locations record is created and link to that user. Next use that selects '444' will be linked to this same record.
If my assumption if correct, you should have:
validates_uniqueness_of :zip_code (without scope) in your Location model
in your User model while creating/updating you could use Location.find_or_create_by(params[:zipcode])
This is pseudo-code (don't copy-paste it), I don't exactly know how your code is writen, but my point is for you to have a look at find_or_create, I believe it could be your solution
It looks like you have the association setup correctly.
When you have a has_many association in rails and want to do something like this:
#user.locations = [<Location 1>, <Location 2>, ...]
Rails will create individual INSERT statements for each location in the array, although it will do a bulk DELETE for you. If you want it to do bulk INSERT statements, you'll need to roll your own code or look into the activerecord-import gem to do this.
As for the duplicates, if you are only doing the above code, there shouldn't be duplicate record errors unless there are duplicates in that location array, in which case you should call uniq on it first.

rails active record associations sql behavior

I have 2 models:
# models/car.rb
class Car < ActiveRecord::Base
belongs_to :model
end
and
# models/manufacturer.rb
class Manufacturer < ActiveRecord::Base
has_many :cars
end
When I'm executing command in rails console Car.find(1).manufacturer it shows me that one more sql query was executed SELECT manufacturers.* FROM manufacturers WHERE manufacturers.id = 54 LIMIT 1,
so I am interested is it usual (for production, first of all) behavior, when a lot of sql queries being executed just to get some object property? what about performance?
UPDATE, ANSWER:
I got an answer from another source: I was told it's "necessary evil" as a payment for abstraction
This is not a "necessary evil" and your intuition that the second query is needless is correct. What you need to do is use :include/includes to tell Rails to do a JOIN to get the associated objects in the same SELECT. So you could do this:
Car.find 1, :include => :manufacturer
# or, in Rails 3 parlance:
Car.includes(:manufacturer).find 1
Rails calls this "eager loading" and you can read more about it in the documentation (scroll down to or Ctrl+F for "Eager loading of associations").
If you always want to eager-load the associated objects you can declare default_scope in your model:
class Car
belongs_to :manufacturer
default_scope :include => :manufacturer
# or Rails 3:
default_scope includes(:manufacturer)
end
However you shouldn't do this unless you really need the associated Manufacturer every time you show a Car record.

complicated search design

I am implementing a recipe search on rails3 using mysql.
The idea of search is that user enters any number of ingredients and search outputs suggestions what to make, sorted in product deficiency order.
class Recipe < ActiveRecord::Base
has_many :ingredients
end
# these records will be entered by user
class IngredientType < ActiveRecord::Base
has_many :ingredients
end
# this table is join table
class Ingredient < ActiveRecord::Base
belongs_to :ingredient_type
belongs_to :recipe
end
What would be the most efficient way to implement this search?
What gems or techniques would you recommend?
Thank you for your answers
def self.from_ingredients ingredients
count_sql = Ingredient.
select('COUNT(*)').
joins(:recipes_ingredients).
where('`recipes_ingredients`.`recipe_id` = `recipes`.`id`').
where('`ingredients`.`id` in (?)', ingredients).to_sql
where("(#{count_sql}) > 0").
order("((`recipes`.`ingredients_count`) - (#{count_sql})) ASC")
end
I managed to find solution by creating such method in recipe model.