Group by name in rails 3 - ruby-on-rails

I am using User.where("name LIKE ?", "frodo") to find users with similar names.
It may return the following users
frodobaggins
frodo
frodo1
frodo2
....
How would I group all users by similar names (partially the same)?

Try this
User.find(:all, :conditions => ["name LIKE ?", "frodo%"])
or
User.where(["name LIKE ?", "frodo%"])
it return array of all user which name start with frodo.

From Divyang's answer, one small change to group users by name:--
User.where(["name LIKE ?", "frodo%"]).group(:name)

ActiveRecord provides a way to support group by clause. Checkout http://guides.rubyonrails.org/active_record_querying.html#group

Related

Group with ActiveRecord and Postgres

I've seen several answers, but couldn't find anything that helps me
I have a Model which has a country column and a city column.
doing some SQL query, I want to add to that query a group by first country and for each country to group by city
Model.where("name LIKE ?", "%#{query}%").group(["country", "city"])
but I get
PG::GroupingError: ERROR: column "models.id" must appear in the GROUP BY clause or be used in an aggregate function
and I am not sure what to do, since I don't want to group by the ID, just by country and city
I am sure I am missing something here, not sure what
using the fields, as suggested with
Model.where("name LIKE ?", "%#{query}%").group("country, city").select("country, city")
still gives the same error
You should select before grouping
Model.where("name LIKE ?", "%#{query}%")
.select("country, city")
.group("country, city")
If you still have troubles with:
Model.where("name LIKE ?", "%#{query}%").group("country, city").select("country, city")
I guess you have defined a default scope, and you must do this way:
Model.unscoped.where("name LIKE ?", "%#{query}%").group("country, city").select("country, city")
Edited:
If it didn't works, you can do this and publish the output:
Model.where("name LIKE ?", "%#{query}%").group(["country", "city"]).to_sql

Active Record Query used "NOT IN" [duplicate]

I'm hoping there is a easy solution that doesn't involve find_by_sql, if not then I guess that will have to work.
I found this article which references this:
Topic.find(:all, :conditions => { :forum_id => #forums.map(&:id) })
which is the same as
SELECT * FROM topics WHERE forum_id IN (<#forum ids>)
I am wondering if there is a way to do NOT IN with that, like:
SELECT * FROM topics WHERE forum_id NOT IN (<#forum ids>)
Rails 4+:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Rails 3:
Topic.where('id NOT IN (?)', Array.wrap(actions))
Where actions is an array with: [1,2,3,4,5]
FYI, In Rails 4, you can use not syntax:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Using Arel:
topics=Topic.arel_table
Topic.where(topics[:forum_id].not_in(#forum_ids))
or, if preferred:
topics=Topic.arel_table
Topic.where(topics[:forum_id].in(#forum_ids).not)
and since rails 4 on:
topics=Topic.arel_table
Topic.where.not(topics[:forum_id].in(#forum_ids))
Please notice that eventually you do not want the forum_ids to be the ids list, but rather a subquery, if so then you should do something like this before getting the topics:
#forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)
in this way you get everything in a single query: something like:
select * from topic
where forum_id in (select id
from forum
where /*whatever conditions are desirable*/)
Also notice that eventually you do not want to do this, but rather a join - what might be more efficient.
You can try something like:
Topic.find(:all, :conditions => ['forum_id not in (?)', #forums.map(&:id)])
You might need to do #forums.map(&:id).join(','). I can't remember if Rails will the argument into a CSV list if it is enumerable.
You could also do this:
# in topic.rb
named_scope :not_in_forums, lambda { |forums| { :conditions => ['forum_id not in (?)', forums.select(&:id).join(',')] }
# in your controller
Topic.not_in_forums(#forums)
To expand on #Trung LĂȘ answer, in Rails 4 you can do the following:
Topic.where.not(forum_id:#forums.map(&:id))
And you could take it a step further.
If you need to first filter for only published Topics and then filter out the ids you don't want, you could do this:
Topic.where(published:true).where.not(forum_id:#forums.map(&:id))
Rails 4 makes it so much easier!
The accepted solution fails if #forums is empty. To workaround this I had to do
Topic.find(:all, :conditions => ['forum_id not in (?)', (#forums.empty? ? '' : #forums.map(&:id))])
Or, if using Rails 3+:
Topic.where( 'forum_id not in (?)', (#forums.empty? ? '' : #forums.map(&:id)) ).all
Most of the answers above should suffice you but if you are doing a lot more of such predicate and complex combinations check out Squeel. You will be able to doing something like:
Topic.where{{forum_id.not_in => #forums.map(&:id)}}
Topic.where{forum_id.not_in #forums.map(&:id)}
Topic.where{forum_id << #forums.map(&:id)}
You may want to have a look at the meta_where plugin by Ernie Miller. Your SQL statement:
SELECT * FROM topics WHERE forum_id NOT IN (<#forum ids>)
...could be expressed like this:
Topic.where(:forum_id.nin => #forum_ids)
Ryan Bates of Railscasts created a nice screencast explaining MetaWhere.
Not sure if this is what you're looking for but to my eyes it certainly looks better than an embedded SQL query.
The original post specifically mentions using numeric IDs, but I came here looking for the syntax for doing a NOT IN with an array of strings.
ActiveRecord will handle that nicely for you too:
Thing.where(['state NOT IN (?)', %w{state1 state2}])
Can these forum ids be worked out in a pragmatic way? e.g. can you find these forums somehow - if that is the case you should do something like
Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")
Which would be more efficient than doing an SQL not in
This way optimizes for readability, but it's not as efficient in terms of database queries:
# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - #forums.map(&:id)
You can use sql in your conditions:
Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", #forums.map(&:id)])
Piggybacking off of jonnii:
Topic.find(:all, :conditions => ['forum_id not in (?)', #forums.pluck(:id)])
using pluck rather than mapping over the elements
found via railsconf 2012 10 things you did not know rails could do
When you query a blank array add "<< 0" to the array in the where block so it doesn't return "NULL" and break the query.
Topic.where('id not in (?)',actions << 0)
If actions could be an empty or blank array.
Here is a more complex "not in" query, using a subquery in rails 4 using squeel. Of course very slow compared to the equivalent sql, but hey, it works.
scope :translations_not_in_english, ->(calmapp_version_id, language_iso_code){
join_to_cavs_tls_arr(calmapp_version_id).
joins_to_tl_arr.
where{ tl1.iso_code == 'en' }.
where{ cavtl1.calmapp_version_id == my{calmapp_version_id}}.
where{ dot_key_code << (Translation.
join_to_cavs_tls_arr(calmapp_version_id).
joins_to_tl_arr.
where{ tl1.iso_code == my{language_iso_code} }.
select{ "dot_key_code" }.all)}
}
The first 2 methods in the scope are other scopes which declare the aliases cavtl1 and tl1. << is the not in operator in squeel.
Hope this helps someone.
If someone want to use two or more conditions, you can do that:
your_array = [1,2,3,4]
your_string = "SOMETHING"
YourModel.where('variable1 NOT IN (?) AND variable2=(?)',Array.wrap(your_array),your_string)

Ruby on Rails: how to use find on multiple conditions and multiple tables?

I have a product model and a category model. Product has_many_and_belongs_to Category, and vice-versa. Now I want to use Product.find to search for a Product.name but also a Product.categories. I tried something like this but it didn't work:
Product.find(:all, :conditions => ['name LIKE ? or categories LIKE ?', '%#{keyword}%']
Basically, I'm trying to get results for products with name like 'car' or products with a category of 'car'. Thanks in advance!
Product.joins(:categories).where('products.name LIKE ? or categories.name LIKE ?', "%#{keyword}")
I think something like this can help you if you using rails > 3

Rails: Sorting Objects From Different Models

I've populated a hash with two different models. I then try to sort them like so:
#search_results = User.find(:all, :conditions => ['name LIKE ?', "%#{params[:query]}%"])
#search_results += Book.find(:all, :conditions => ['title LIKE ?', "%#{params[:query]}%"])
#search_results.sort! { |a,b| a.impressions_count <=> b.impressions_count }
This throws the following error:
comparison of User with Book failed
Both users and books have an integer-based impressions_count. Why can't I sort via this attribute? What other options do I have?
I faced a similar problem recently and ended up writing some custom sql because all other ways returned an array. Pretty sure its not a good idea to use the sort method since it will always be more efficient to sort in SQL than ruby, especially when the data set gets large
#combined_results = User.find_by_sql(["SELECT title, id, impressions_count, NULL as some_attribute_of_book
FROM user
WHERE title LIKE ?
UNION SELECT title, id, impressions_count, some_attribute_of_book FROM book
WHERE title LIKE ?
ORDER BY impressions_count", params[:query], params[:query]])
The above is completely untested code, more of an example than anything

Multiple keywords database search

I am currently using scopes in my model to perform searches within a database. I can stack these scopes and it will output results matching all parameters.
scope :search_between, lambda{|begin_date, end_date|
where "sub.date BETWEEN ? AND ?", begin_date, end_date
}
What I am having trouble with is integrating a keywords search that will search the entire database and output the results that contain the sum of the keywords. I would like to do something like this (displayed for simplicity):
scope :keywords, lambda{|search|
search.chomp.split(/,\s*/) do |item|
where "date like ? or city like ? or state like ?", "%#{item}%" and
where "date like ? or city like ? or state like ?", "%#{item}%" and
where "date like ? or city like ? or state like ?", "%#{item}%"
end
}
I am currently using something like this to take care of searching multiple columns:
scope :keywords, lambda{|search|
search.chomp.split(/,\s*/) do |item|
where(Sub.column_names.map {|cn| "#{cn} like ?" }.join("or "), "%#{item}%"] Sub.column_names.size)).join(' AND ')
end
}
My problem is that I want to do multiple "where()'s" in the scope. Is it possible and if so, how?
Just turn it into a method that returns a scope:def self.keywords(search)
scope = self.scoped
search.chomp.split(/,\s*/).each do |item|
scope = scope.where(["date like ? or
city like ? or
state like ?", "%#{item}%","%#{item}%","%#{item}%"])
end
scope
end
The drawback is that you can't chain keywords off of other scopes, but you can chain other scopes off of keywords.
I think you will be hitting the limit with using the database for full text search.
Have you looked into using Solr or Sphinx for your full text searches? There's also IndexTank if you want to use a 3rd party service.
For solr, there are rubygems available to help you: sunspot, solrsan(i'm the author)
Sunspot is definitely more full-featured and solrsan is a barebones layer.

Resources