Rails 3: Feed is taking too long - ruby-on-rails

I have a twitter-like feed in my web app. I've tried to optimize the query as much as possible but it still freezes when it's loading the "tweets" from PostgresSQL database.
Twitter, github, facebook, feeds are so smooth and fast.
What's the best way to accomplish that?
Regards.
**Edit: the code is the one in railstutorial.org.
http://railstutorial.org/chapters/following-users#sec:scopes_subselects_and_a_lambda
# Return microposts from the users being followed by the given user.
scope :from_users_followed_by, lambda { |user| followed_by(user) }
private
# Return an SQL condition for users followed by the given user.
# We include the user's own id as well.
def self.followed_by(user)
followed_ids = user.following.map(&:id).join(", ")
where("user_id IN (#{followed_ids}) OR user_id = :user_id",
{ :user_id => user })
end
# Table name: microposts
#
# id :integer not null, primary key
# content :string(255)
# user_id :integer
# created_at :datetime
# updated_at :datetime
#
Sorry for not providing enough information.**

Did you put any index on your tables?

Related

Arel::SelectManager how to access the result including the projections

I am attempting to access the results of a join where columns of both tables are specified as part of a projection.
I have 2 models (Rails 4.2.11; Arel 6.0.4; Ruby 2.5.3)
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
class User < ActiveRecord::Base
has_many :photos
end
# Table name: photos
#
# id :integer not null, primary key
# name :string(255)
# created_by_id :integer
# assigned_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# user_id :integer
class Photo < ActiveRecord::Base
belongs_to :user
end
creators = User.arel_table.alias('creators')
updaters = User.arel_table.alias('updaters')
photos = Photo.arel_table
photos_with_credits = photos.
join(photos.join(creators, Arel::Nodes::OuterJoin).on(photos[:created_by_id].eq(creators[:id]))).
join(photos.join(updaters, Arel::Nodes::OuterJoin).on(photos[:assigned_id].eq(updaters[:id]))).
project(photos[:name], photos[:created_at], creators[:name].as('creator'), updaters[:name].as('editor'))
# generated SQL
SELECT photos.name, photos.created_at, creators.name AS creator, updaters.name AS editor
FROM photos
INNER JOIN (SELECT FROM photos LEFT OUTER JOIN users creators ON photos.created_by_id = creators.id)
INNER JOIN (SELECT FROM photos LEFT OUTER JOIN users updaters ON photos.updated_by_id = updaters.id)
How I'd like to process the result
photos_with_credits.map{|x| "#{photo.name} - copyright #{photo.created_at.year} #{photo.creator}, edited by #{photo.editor}"}.join('; ')
This may be a very dumb question, but ...
I have not been able to find a way to use the SelectManager result to produce a meaningful output since map was deprecated (& removed) from the SelectManager class.
I would appreciate your help.
Simplified the activerecord / arel query builder
amended code
photos_with_credits = Photo.select([photos[:name], photos[:created_at], creators[:name].as('creator'), updaters[:name].as('editor')]).
joins(photos.outer_join(creators).on(photos[:created_by_id].eq(creators[:id])).join_sources).
joins(photos.outer_join(updaters).on(photos[:assigned_id].eq(updaters[:id])).join_sources)
photos_with_credits.map do |photo|
puts "#{photo.name} - copyright #{photo.created_at.year} #{photo.creator}, edited by #{photo.editor}".light_blue
end
amended SQL (simpler)
SELECT photos.name, photos.created_at, creators.name AS creator, updaters.name AS editor
FROM photos
LEFT OUTER JOIN users creators ON photos.created_by_id = creators.id
LEFT OUTER JOIN users updaters ON photos.assigned_id = updaters.id
We should figure out a way to trigger SelectManage and get ActiveRelation result, then we can iterate it by using map, etc.
So I would like to suggest:
Photo.join(photos_with_credits.join_sources).map do |photo|
"#{photo.name} - copyright #{photo.created_at.year} #{photo.creator}, edited by #{photo.editor}"}.join('; ')
end

Ruby comparing hashes built from non-associated models

I am trying to compare hashes, they reference a complex set of associations. Payments are managed by an engine within the app so there is no belongs_to: / has_many: relationships between it and users(which is on the main application). However i can pull user from the main app using a service.
I have a Payments table - in engine
# Table name: payments
#
# id :integer not null, primary key
# account_id :integer
# currency :string(255)
....
The Account table just maps users to accounts - in engine
# Table name: account
#
# id :integer not null, primary key
# user_id :integer
....
The User table also has a column for currency, this is to mark what currency he opted to pay in during the sign-up phase.
# Table name: user
#
# id :integer not null, primary key
# currency :string(255)
....
We have an issue where sometimes users pay in different currencies, I would like to build a hash where :payment_id => true/false(where this would be user.currency == payment.currency), something where i can count the offences I was thinking of saving the payment_id just incase they wanted detailed error data.
What I ended up doing was.
Building a payments Hash with account_id: currency
Building a accounts Hash using only account_ids found in step 1 id: user_id
Rebuilding the payments hash by replacing account_id: with user_id:
Building a users Hash with users_id: currency
Iterating through each user_id and ticking a counter when payment_hash[user_id] != user_hash[user_id]
looks something like this
def inconsistancy_count(payments)
#list of account ids involved with the payments made
matching_account_ids = Recon::Account.where(account_id: payments).map(&:id)
# list of users that are attached to those accounts above
user_ids = Recon:Account.where(account_id: matching_account_ids).map(&:user_id)
# Payment hash mapping account_id and currency
payment_currency_hash = Recon::Payment.where(account_id: matching_account_ids).reduce({}) {|hash, payment| hash.merge(payment.account_id => payment.currency)}
#build a hash using the user service.
main_app_user_currency_hash = Recon.user_service.get_user_info_from_ids(user_ids)
inconsistancy_count = 0
matching_user_ids.each do |user|
inconsistancy_count += 1 if main_app_user_currency_hash[user] != payment_currency_hash[user]
end
inconsistancy_count
end
The above won't work because the payments hash is still based off account_id and not user_id.
I feel my route is very round about it seems as if this method has a ton of things it should do. Is there a better way of accomplishing this

Rails filter on date returning no results

I'm trying to filter records that were assigned on particular day (today) using this query:
assignments = p.assignments.where("assigned_date = ?", Date.today)
Even though I know these records exist, I always get a blank result back.
I've also tried ...where(:assigned_date => Date.today) with no luck.
The database schema is:
# == Schema Information
#
# Table name: assignments
#
# id :integer not null, primary key
# name :string(255)
# rep :integer
# set :integer
# instructions :text
# created_at :datetime not null
# updated_at :datetime not null
# player_id :integer
# actual_percentage :float
# predicted_percentage :float
# assigned_date :date
#
And in the console, when I type
p.assignments.first.assigned_date == Date.today
it returns true.
Any idea what I'm doing wrong?
DateTime holds a date and a time, so you're looking for records that have a precise value, not just the same day.
assignments = p.assignments.where('assigned_date BETWEEN ? AND ?', DateTime.now.beginning_of_day, DateTime.now.end_of_day).all
should return what's expected
P.S.
all credits to Tadman

Dealing with time periods in Rails 3.1

I'm developing a booking system in Rails 3.1. I have created a model for a Booking:
# == Schema Information
#
# Table name: bookings
#
# id :integer not null, primary key
# product_id :integer
# customer_id :integer
# booked_from :datetime
# booked_to :datetime
# paid :boolean
# payment_type :string(255)
# created_at :datetime
# updated_at :datetime
#
So what I want to do is to validate each entry and check whether the desired time period (booked_from - booked_to) is overlapping any period of another booking with the same product_id. The products also have an available_from and available_to field which it also has to validate against.
How do I do this?
Check if this works:
class Booking
validate :booking_period_not_overlapped
private
def booking_period_not_overlapped
unless Booking.where(
'(booked_from <= ? AND booked_to >= ?) OR (booked_from >= ? AND booked_from <= ?)',
booked_from, booked_from,
booked_from, booked_to
).empty?
errors.add(:booked_from, 'Invalid period.')
end
end
end
It just checks if there is any existing records whose booked_from and booked_to satisfy one of the following conditions (suppose your new booking is from 16:00 to 17:00):
it starts before the new booking, and not yet ended (e.g. 15:00 - 16:30 or 15:00 - 17:30)
it starts within the new booking period (e.g. 16:20 - 16:50 or 16:30 - 17:30)

Group using Sunspot/Solr?

I'm having some problem searching by group when using Sunspot.
Here is an example:
# == Schema Information
#
# Table name: movies
#
# id :integer(4) not null, primary key
# title :string(255)
class Movie < ActiveRecord::Base
has_and_belongs_to_many :actors
searchable do
text :title
integer :ages, multiple: true do
actors.map(&:age)
end
text :names, multiple: true do
actors.map(&:name)
end
end
end
# == Schema Information
#
# Table name: actors
#
# id :integer(4) not null, primary key
# name :string(255)
# age :integer(30)
class Actor < ActiveRecord::Base
has_and_belongs_to_many :movies
searchable do
integer :age
text :name
end
end
I want to find every movie that has an actor named John at age 30.
Movie.search do
with(:names).equal_to("John")
with(:ages).equal_to(30)
with(:title).equal_to("...")
# ...
end
The problem is here that it may find a movie that has two actors; one named John and one at age 30. Is there a way to somehow group this together so that the movie found have an actor named John at age 30?
The solution, just like MaurĂ­cio Linhares wrote in his comment, is to go through the actors model and group by movies.
The problem is that Sunspot doesn't support Solr 3.3 or 4.0, which is the only Solr versions that support grouping.
Here is my solution using Sunspot 1.2.1 and Solr 3.3.
In my example movie_id is placed in the actors table, this isn't done in my real application.
# == Schema Information
#
# Table name: actors
#
# id :integer(4) not null, primary key
# name :string(255)
# created_at :datetime
# updated_at :datetime
# movie_id :integer(4)
#
class Actor < ActiveRecord::Base
searchable do
# We need to store the movie_id as an string
# So it can be sorted. We also need to pass the
# stored: true params
string :movie_id, stored: true do
movie_id.to_s
end
end
def search_using_solr
scoped = Sunspot.new_search(Actor)
scoped.build do
adjust_solr_params do |params|
params[:group] = true
params[:"group.field"] = "movie_id_s"
params[:"group.format"] = "simple"
end
end
# Sunspot 1.2.1 doesn't support grouping, so we need to do some hacking.
def scoped.hits
#hits ||= #solr_result["grouped"].values.first["doclist"]["docs"].map do |doc|
Sunspot::Search::Hit.new(doc, nil, self)
end
end
def scoped.total
#total ||= #solr_result["grouped"]["movie_id_s"]["matches"] || 0
end
# Here we'll only fetch the stored data from Solr it self,
# and then pass it manualy to ActiveRecord.
Movie.where({
id: scoped.execute.hits.map{ |h| h.stored(:movie_id) }
})
end
end
Cred to alindeman for his example gist.

Resources