I am using the textacular gem to implement fuzzy search in my Rails application, and I cannot figure out how to perform a search on multiple tables at once. Here's an example query that searches multiple tables WITHOUT textacular:
#results = Owner.includes(:car => :company).where("name LIKE ? OR cars.name LIKE ? OR companies.name LIKE ?", sql_query, sql_query, sql_query).references(:cars, :companies)
As you can see, this query searches the Owner, Car, and Company tables. The farthest I got with textacular is this:
#results = Owner.includes(:car => :company).references(:cars, :companies).fuzzy_search({name: sql_query})
The above query only searches the Owner table, though. I've tried doing the following to get it to search the Companies table as well:
#results = Owner.includes(:car => :company).references(:cars, :companies).fuzzy_search({name: sql_query, "companies.name" => sql_query})
This doesn't work because it tries searching the owners.companies.name field, which of course does not exist. I want it to search the companies.name field in addition to the owners.name field. Does anyone know how to perform a fuzzy search on multiple tables using textacular?
I figured it out! You need to add the joined table name as a nested hash. Let's say I want to search by the name fields of the Owner model AND the Car model:
#results = Owner.includes(:car).references(:cars).fuzzy_search({name: sql_query, cars: { name: sql_query}})
The cars table specified in the fuzzy_search method refers to the name of the table, NOT the name of the association.
I got inspiration from this old Rails commit from 2008, which made me realize that the conditions you can use in any standard Rails query are available in the fuzzy_search method.
Not sure if you're wedded to textacular but from their docs it looks like you can only search by one model.
I've used PgSearch before and and it's pretty robust, you can search across models.
Related
I have a simple rails application for a competition with these models: User, Team, and Trip. The associations are: team has_many users, and user has_many trips. Trips has a distance_miles field.
I'm am working on a simple view to display a table of teams with their stats that have multiple aggregations.
My initial approach was the following but results in N+1 queries.
#teams.each do |team|
#team.user.joins(:trips).count()
#team.user.joins(:trips).sum(:distance_miles)
end
The following works, but seems ugly as I want to add pagination, sorting and filtering eventually.
#teams = Team.left_joins(users: :trips)
.select('teams.id, teams.name, SUM(trips.distance_miles) as num_miles, COUNT(trips.id) as num_trips')
.group(:id)
I've been reading preload and includes but cant seem to get it to also get multiple aggregations. The following gets me part way there, but it is now missing fields from Team and still need the other aggregation:
#teams = Team.includes(users: :trips).group(:id).sum('trips.distance_miles')
Is there a "rails way" that I'm missing?
ActiveRecord::Calculations which provides .sum, .count is only really useful in very simple cases as it always creates a separate database query.
You're on the right track with .select but it could be cleaned up by extracting the code into the model:
class Team
def self.with_trips
trips = Trip.arel_table
self.left_joins(users: :trips)
.select(
self.arel.projections, # adds the existing select clause
trips[:distance_miles].sum.as(:num_miles),
trips[:id].count.as(:distance_miles)
)
.group(:id)
end
end
Using .eager_load instead of .left_joins will result in a PG::GroupingError on Postgres as the results would be ambiguous. Instead you need use a window function.
Sorry for the long title, I wanted to make sure I was being as specific as possible. This is my first question so please be patient!
I am building an index page that displays all the items in the consultations model that are associated with the current user. This used to be easy when I set #consultations = Consultation.where(user_id: current_user.id) in my controller. However, because consultations should be able to have more than one consultant assigned to each one, I changed the Consultation.user_id field to be a string array (I wasn't able to change to an integer array). I also connected the User and Consultation models via has_and_belongs_to_many and a join table.
I am now trying to query them with #consultation = Consultation.where {|a| a.user_id == ['current_user.id']} so that I can display all consultations in which the Consultation.user_id array contains the current_user.id.
This works as expected in pry, but on the actual view page I'm getting an error: NoMethodError: undefined method /'each/' for #<ActiveRecord::QueryMethods::WhereChain:0x007f82c23fe840>.
I understand that this is to be expected, since .where returns an ActiveRecord, which does not support methods such as each do, which is what I'm using on the index page to show all the consultations.
Any idea on how I can properly display all the consultations associated with the current_user.id? Thanks in advance.
you can do it like
#consultations = current_user.consultations
use associations instead of query
BTW
#consultation = Consultation.where {|a| a.user_id == ['current_user.id']}
this will not work because 'current_user.id' is string, it isn't id
it means that you try find all consultations where user_id == 'current_user.id'.
Do you see what I mean?
It sounds like you have a many-to-many relationship between consultants and clients. You can solve this by having a join table in the middle that holds both the client_id and the consultant_id. This would be a 3rd model to the consultant model and client models that you already have. You shouldn't try to store arrays as fields in ActiveRecord. ActiveRecord is just helping you organize data that is being written to a database.
Once you have a join table with both foreign_id's you can easily set up has_many relationships for both the client and consultants and then a has_may_through association. Then you won't need to worry about any #where calls because you can just call the associations.
The problem is that the syntax where you pass a block to where is something that you've just made up. Because no actual arguments are passed to where you don't get an ActiveRecord::Relation
You could change relationship between consultation and users to be has many through. If that join model was called assignments, then you could write a query such as
Consultation.joins(:assignments).where(assignments: {user_id: current_user.id})
If you keep your current approach with a user_id array column then, assuming this is Postgres, you can use the array query methods:
Consultation.where("user_id #> ARRAY[?]", current_user.id)
I am writing a Rake task to populate a database for development purposes to have some data to work with. ( but In test I am using FactoryGirl, so not talking about that. )
So let's say I have two sample Organizations in my demo database, so I have defined them like this:
sample_organizations = [ '37 Signals', 'Fog Creek']
and then a small method to populate the DB like this:
def create_organizations
sample_organization.each { |item|
Organization.first_or_create(
name: item,
time_zone: 'Central'
)
}
end
so the good thing is that two months from now if I wanted to add a third organization I just go to top of my code and hand type another organization name in that array. Method still works and creates it.
Now here starts the question: I have more tables to populate! For example Summaries table which is hanging off of Organization table, so each organization can have many Summaries, so we have an organization_id foreign key in Summary table to fill too.
But still I want to have a method like create_summaries that does something similar to create_organization method above, but this time fills in Summaries table, and the tricky part is to fill in that organization_id field of the table.
How should I approach this?
Let's say Summaries table has these fields: id (auto generate by Rails) , organization_id, name, member_count
Try using Populator gem for tasks like this. It is easy to use, and you can generate complex structures. Screencast.
As you can't use those, use select a random parent object, or specify one by name in a hash.
summaries = [{:name => "foo", :organization => "bar"}]
Hi I have just followed Ryan Bates guide to loading data from the server as the client interacts with the datatable.
http://railscasts.com/episodes/340-datatables?view=asciicast
In his ProductsDatatable class he defines a sorting function:
def sort_column
columns = %w[name category released_on price]
columns[params[:iSortCol_0].to_i]
end
Basically the column name is used to query the database, so "name", "category", etc are all attributes to the product model. So an SQL SELECT statement is generated with Name or Category being used for sorting ASC or DESC.. and so on...
If I have data being displayed in a column that does not map explicitly to a attribute.. such as product.reviews.count, how would I then allow sorting of that column?
You would need something like this
def sort_column
columns = ['name', 'category', 'released_on,' 'price', 'number_of_reviews]
columns[params[:iSortCol_0].to_i]
end
But this means that you need to change the way you get your data, for example:
Product.where(…).select('name, category, count(reviews.id) as number_of_reviews').group('name, category').includes('reviews')
I haven't been able to test the definition of columns, you may have to use reviews.number_of reviews instead
I know this is a really old post (by now), but just wanted to let you know that this gem: https://github.com/antillas21/ajax-datatables-rails can help you jumpstart creating datatables classes easily, as described in the Railscast you mention (it's actually based in this same pattern).
What the gem does is to let you focus on writing the query to retrieve your model(s) data from the db and how to expose it in the JSON that jquery.dataTables needs.
Take it for a spin.
I want to create a really simple search form, basically a single field, that appears in the header of every page in my application.
My application has several models, each with several fields.
I'd like to know how to have that search get passed as a parameter into an output which shows all instances of that text item or items (meaning, if there are two elements, a first name and a last name, for example, that it pulls up records that have both values).
I currently use searchlogic, but I think that's really field specific. Looked into texticle since I am on heroku, but I still not clear what to do or if this is right way to go. Thank you.
Try out SOLR -- it will create an index of your models, and you can search on it. SOLR is the search engine -- use the following gems in rails: 'sunspot', 'sunspot_rails'
You'll do something like:
#sunspot_search = Sunspot.search User, Event, Coupon do |query|
query.keywords #search_query
query.with(:starts_at).greater_than Time.now
query.order_by(:starts_at, :asc)
query.paginate(:page => #page_number, :per_page => 200)
end
#sunspot_search will have your results array that you'll iterate over.
http://websolr.com/ gets you setup easily; works on Heroku.