Find result from joining a table in rails :conditions part - ruby-on-rails

Currently the attachments are searched based on attachment_file_name, tags and employee_id, I would like to search based on Employee name as well, how can I go with it?
Two models must be used.
Employee - which has name of employee
LessonplanAttachment - contains the id of the employee.
The code part.
def search_ajax
#attachments = LessonplanAttachment.find(:all,
:conditions => ["attachment_file_name LIKE ? OR tags LIKE ? OR employee_id = ?",
"#{params[:query]}%","#{params[:query]}%",params[:query]])
end

try this
LessonplanAttachment.joins("JOIN employee on employee_id").find(:all, :conditions =>
["attachment_file_name LIKE ? OR tags LIKE ? OR employee_id = ? OR employee.name = ?",
"#{params[:query]}%","#{params[:query]}%",params[:query], params[:query]]

I guess you have the following association in your LessonplanAttachment model.
belongs_to :employee
Now you can change your code as per the following
#attachments = LessonplanAttachment.joins([:employee]).where("attachment_file_name LIKE ? OR tags LIKE ? OR employee_id = ? OR employees.name LIKE ?", "#{params[:query]}%","#{params[:query]}%",params[:query],"#{params[:query]}%")

Provided you have an association belongs_to :employee on your LessonplanAttachment class, for rails 2, you can add a :joins attribute to the options on find. Something like this should work:
#attachments = LessonplanAttachment.find(:all,
:joins => :employee,
:conditions => [
"attachment_file_name LIKE ? OR tags LIKE ? OR
employee_id = ? OR employees.name LIKE ?",
"#{params[:query]}%","#{params[:query]}%",params[:query],"#{params[:query]}%"])

Related

rails: query and filter n:m related objects using active record

I'm trying to use active-record query possible connections between airports.
I described the models I created already in another question here:
n:m self-join with ruby on rails active record
Basically, what I can do now is that:
ny = Airport.create({"city" => "New York"})
la = Airport.create({"city" => "Los Angeles"})
ny.destinations << la
la.destinations << ny
I ran into an issue querying the data I'm looking for, which is quite simple in SQL but I had no luck with active record yet.
ny = Airport.where('city = ?', 'New York')
ny.destinations
returns the correct objects, but all of them.
The SQL query looks like that:
SELECT "airports".* FROM "airports" INNER JOIN "connections" ON "airports"."id" = "connections"."destination_id" WHERE "connections"."airport_id" = 3
I'd like to filter those results by cities starting with "s" for example, so an SQL query could look like that:
SELECT "airports".* FROM "airports" INNER JOIN "connections" ON "airports"."id" = "connections"."destination_id" WHERE "connections"."airport_id" = 3 AND airports"."city" LIKE "s%"
I tried it this way:
ny.destinations.where('city LIKE ?', '#{params[:query]}%')
But I always get an empty result.
How could I use active record to filter my resulting objetcs?
edit: Thats the best solution I found so far:
I added the cityLike() method to the Airport model:
app/models/airport.rb:
class Airport < ActiveRecord::Base
attr_accessible :city, :name
has_many :connections
has_many :destinations, :through => :connections
has_many :inverse_connections, :class_name => "Connection", :foreign_key => "destination_id"
has_many :inverse_destinations, :through => :inverse_connections, :source => :airport
def self.cityLike(query)
where("city LIKE ?", "%#{query}%")
end
end
app/model/connection.rb:
class Connection < ActiveRecord::Base
attr_accessible :destination_id, :airport_id
belongs_to :airport
belongs_to :destination, :class_name => "Airport"
end
Now I can query the objects with the following statement:
Airport.find(1).destinations.cityLike("a")
Not sure if it's the best solution, but it produces the query I was looking for.
Thanks a lot to all af you!
ActiveRecord::Base.execute(sql) lets you use pure SQL to do your query and returns the relevant model.
What about this?
Airport.find(:all, joins: "INNER JOIN `connections` ON airports.id = connections.destination_id").where("connections.airport_id = ? AND airports.city LIKE ?", ny_id, "s%")
This code:
ny.destinations.where('city LIKE ?', '#{params[:query]}%')
works like this--first you have an object ny repesenting the city of New York. When you say ".destinations" you have now followed a relation you defined in your model to retrieve all the destinations that you can get to from New York. However, if I'm imagining your database schema correctly, these destinations don't actually have a field called "city"; instead, they have a destination_id, which ties the destination to a particular airport, and it's the airport that has a city associated with it.
So when you query the destination table for 'city LIKE ?', it doesn't find any matching records.
Instead, try
ny.destinations.joins(:airports).where('city LIKE ?', '#{params[:query]}%')

Rails How to use join in this specific situation?

I have three models, each connected as such: Groups which has many Students which has many Absences.
Absences has a field called created_on.
I only have a group id and would like to obtain all students with an absence of today.
I have created this method inside my Student model:
# Inside student.rb
def self.absent_today_in_group (group)
#SQLITE
find(:all, :joins => :absences, :conditions => ["STRFTIME('%d', created_on) = ? AND STRFTIME('%m', created_on) = ?", Date.today.day, Date.today.month])
#POSTGRES
#find(:all, :joins => :absences, :conditions => ["EXTRACT(DAY FROM created_on) = ? AND EXTRACT(MONTH FROM created_on) = ?", Date.today.day, Date.today.month])
end
Why would that query not return anything? And how could I then also check for group_id?
What version of rails are you using? You can do this in rails 3:
def self.absent_today_in_group(group)
joins(:absences, :group).where(
'absences.created_on' => (Time.now.beginning_of_day..Time.now.end_of_day),
'groups.id' => group.id
)
end
That would find all users which were absent for today for given group.
Shouldnt this be :where and not :conditions?

How to join an ActiveRecord result with select_all result by a column ?

class Author
has_many :post
end
class Post
belong_to :author
has_many :content
end
class Content
belong_to :post
(column: section)
end
c = Content.select("post_id").where("section like ?", 'foo%')
p = ActiveRecord::Base.connection.select_all(Post.select("title, post_id ").joins(:author).where(:id => c.map(&:post_id)).to_sql)
how to join c and p to become a table-like structure by post_id column ?
just like in SQL:
select * from c,p where c.post_id = q.post_id ;
Thanks a lot.
This code
#posts = Content.find(:all, :include => :post)
will generate SQL as you wish. You can access separately content for each post in each cycle or generate plane table like DB returns performing something like this:
#posts = Content.find(:all, :include => :post).map{|c| c.merge(c.post)}

ActiveRecord Association select counts for included records

Example
class User
has_many :tickets
end
I want to create association which contains logic of count tickets of user and use it in includes (user has_one ticket_count)
Users.includes(:tickets_count)
I tried
has_one :tickets_count, :select => "COUNT(*) as tickets_count,tickets.user_id " ,:class_name => 'Ticket', :group => "tickets.user_id", :readonly => true
User.includes(:tickets_count)
ArgumentError: Unknown key: group
In this case association query in include should use count with group by ...
How can I implement this using rails?
Update
I can't change table structure
I want AR generate 1 query for collection of users with includes
Update2
I know SQL an I know how to select this with joins, but my question is now like "How to get data" . My question is about building association which I can use in includes. Thanks
Update3
I tried create association created like user has_one ticket_count , but
looks like has_one doesn't support association extensions
has_one doesn't support :group option
has_one doesn't support finder_sql
Try this:
class User
has_one :tickets_count, :class_name => 'Ticket',
:select => "user_id, tickets_count",
:finder_sql => '
SELECT b.user_id, COUNT(*) tickets_count
FROM tickets b
WHERE b.user_id = #{id}
GROUP BY b.user_id
'
end
Edit:
It looks like the has_one association does not support the finder_sql option.
You can easily achieve what you want by using a combination of scope/class methods
class User < ActiveRecord::Base
def self.include_ticket_counts
joins(
%{
LEFT OUTER JOIN (
SELECT b.user_id, COUNT(*) tickets_count
FROM tickets b
GROUP BY b.user_id
) a ON a.user_id = users.id
}
).select("users.*, COALESCE(a.tickets_count, 0) AS tickets_count")
end
end
Now
User.include_ticket_counts.where(:id => [1,2,3]).each do |user|
p user.tickets_count
end
This solution has performance implications if you have millions of rows in the tickets table. You should consider filtering the JOIN result set by providing WHERE to the inner query.
You can simply use for a particular user:
user.tickets.count
Or if you want this value automatically cached by Rails.
Declare a counter_cache => true option in the other side of the association
class ticket
belongs_to :user, :counter_cache => true
end
You also need a column in you user table named tickets_count.
With this each time you add a new tickets to a user rails will update this column so when you ftech your user record you can simply accs this column to get the ticket count without additional query.
Not pretty, but it works:
users = User.joins("LEFT JOIN tickets ON users.id = tickets.user_id").select("users.*, count(tickets.id) as ticket_count").group("users.id")
users.first.ticket_count
What about adding a method in the User model that does the query?
You wouldn't be modifying the table structure, or you can't modify that either?
How about adding a subselect scope to ApplicationRecord:
scope :subselect,
lambda { |aggregate_fn, as:, from:|
query = self.klass
.select(aggregate_fn)
.from("#{self.table_name} _#{self.table_name}")
.where("_#{self.table_name}.id = #{self.table_name}.id")
.joins(from)
select("(#{query.to_sql}) AS #{as}")
}
Then, one might use the following query:
users = User.select('users.*').subselect('COUNT(*)', as: :tickets_count, from: :tickets)
users.first.ticket_count
# => 5

Advanced find in Rails

I really suck at Rails' finders besides the most obvious. I always resort to SQL when things get more advanced than
Model.find(:all, :conditions => ['field>? and field<? and id in (select id from table)', 1,2])
I have this method:
def self.get_first_validation_answer(id)
a=find_by_sql("
select answers.*, answers_registrations.answer_text
from answers_registrations left join answers on answers_registrations.answer_id=answers.id
where
(answers_registrations.question_id in (select id from questions where validation_question=true))
and
(sale_registration_id=#{id})
limit 1
").first
a.answer_text || a.text if a
end
Can someone create a find method that gets me what I want?
Regards,
Jacob
class AnswersRegistration < ActiveRecord::Base
has_many :answers
end
id = 123
the_reg = AnswersRegistration.first(
:joins => :answers,
:conditions => '(question_id in (select id from questions where validation_question = true)) and (sale_registration_id = ?)', id)
(untested)
Just use binarylogic's Searchlogic gem if that satisfies your need.
Here you go: http://github.com/binarylogic/searchlogic
Sometimes AR will choke on complicated nested conditions, but in theory you should be able to do this:
AnswersRegistration.first(:conditions => { :question => { :validation_question => true },
:sale_registration_id => id },
:include => :answer)

Resources