How to fix this search method - sql

I have the following in my model to search for a string entered by a user:
def self.find_products(product="")
find(:all, :select => 'product_id, name', :order => "name", :conditions => ["name like ? and locale =?", "%#{product.capitalize}%", I18n.locale])
end
But it only works if the string is one word.
If the string contains two or more words such as "card guides" it returns nothing even if those are the exact words in the name.
How can I do this search properly.
Rails 3 Ruby 1.9.2
Update
It turns out that its a Postgresql syntax. I needed to user iLIKE for case insensitivity. Then it worked better. I still like the answer below as it also helped to refine my syntax.

Hey i am also using this type of search logic in mine project, I had done in this way . If you want to try then look if it might helps you -:
class << self
def search(query)
product = "%#{query}%"
where("name LIKE ? AND locale =?", product,I18n.locale)
end
end
Thanks

I notice that you're using capitalize. Are you sure that the product name is being formatted in the exact same way that you're storing the data? Could it be that the code is returning "Card guides" but your data expects "Card Guides"?

Related

search for a substring in where clause on an encrypted field

Using the attr_encryptor-2.0.0 gem with rails 3.2.11 to encrypt a text field in my postgresql 9.2.2 database.
class Task < ActiveRecord::Base
attr_encrypted :description, :key => 'A secret key'
Before encryption I used the following where-clause to find records with description containing a given search term s.
current_user.tasks.where("description like ? ", '%'+s+'%')
How can I do it with encrypted description column?
I don't get how the find_by_encrypted_<column> works. The code below returns nil whether I use the entire description or a substring (of an existing record) as search term s.
current_user.tasks.find_by_encrypted_description(Task.encrypt_description(s))
I haven't changed the way I create records after adding encryption. Should I?
t = current_user.tasks.build(:description => #description)
Thank you!
Ended up using attr_encrypted-1.2.1 gem and the following select clause:
tasks = current_user.tasks.select {|t| Task.decrypt(:description, t.encrypted_description).include?(s))}
With attr_encryptor-2.0.0 gem I was getting a bad decrypt error with the same code.

Validate singularity of a string in rails

Anyone know if there is anything built into rails to the effect of validates_signularity_of :string? I can't find any documentation of anything like this, but just wanted to check. I want to validate that a string the user can enter in is always a singular word.
One way would be to leverage the singularize method.
If singularizing a string results in the same string, the string is already singular. Otherwise, it is plural.
A custom validator like the following might work:
class SingularValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
unless value.to_s.singularize == value
object.errors[attribute] << (options[:message] || "is not singular")
end
end
end
Then in your model:
validates :column_name, :singular => true
Credit:
Basic structure of a custom validator extracted from Ryan's Railscast #211

Searching issue with the rails-sunspot gem

I'm very new to Solr and the Rails Sunspot gem, but it looks very promising for complex searching on a big database.
What I'm trying to do is allow a model in my rails app to be searched on a few fulltext columns and then a collection of its "filters" (which are just a has_and_belongs_to_many association of names).
I tried setting up my model search block as follows
self.searchable do
text :name, :boost => 5
text :description, :instructions
text :filters do
filters.map(&:name)
end
end
And my controller looks like so:
#search = ModelName.search do
keywords params[:q].to_s
end
However, I cannot seem to produce any results based on keywords found in the filters association. Am I doing something wrong? This is all very new to me.
When you initially set up your classes for search, you need to reindex the data into Solr. Have you done that? If not:
rake sunspot:reindex

Rails: Find by related model attribute?

So, I tried to search for an example of how to do this, and I'm not sure I even know how to describe what I'm trying to do. I'm an utter noob when it comes to SQL, and I'm sure this is really basic but I'm totally lost:
I have a model, Photo, which has_many :tags, :through => :taggings. Tags have a name and an id.
I want to do something like: Photo.where( #tag_name in [array] )
... but like I said I have no idea how to write something like that, or what to search to see an example on Google.
Can anyone give me an example of that kind of query, and what it might be called?
Thanks!
Just tried this on a similar model of my own and seemed to work fine:
Photo.joins(:tags).where('tags.name' => ['herp','derp']).group(:id)
Also, here's a great resource on the AREL querying interface rails 3 uses, with information on these and other ActiveRecord calls related to querying.
So, as a twist on Brett's method, it turns out the following works without breaking PostgreSQL:
def self.tagged_with( string )
array = string.split(',').map{ |s| s.lstrip }
select('distinct photos.*').joins(:tags).where('tags.name' => array )
end
Problem solved! See this article for a great explanation as to why this is a better idea than 'group'.

How can I see the SQL that will be generated by a given ActiveRecord query in Ruby on Rails

I would like to see the SQL statement that a given ActiveRecord Query will generate. I recognize I can get this information from the log after the query has been issued, but I'm wondering if there is a method that can be called on and ActiveRecord Query.
For example:
SampleModel.find(:all, :select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")
I would like to open the irb console and tack a method on the end that would show the SQL that this query will generate, but not necessarily execute the query.
Similar to penger's, but works anytime in the console even after classes have been loaded and the logger has been cached:
For Rails 2:
ActiveRecord::Base.connection.instance_variable_set :#logger, Logger.new(STDOUT)
For Rails 3.0.x:
ActiveRecord::Base.logger = Logger.new(STDOUT)
For Rails >= 3.1.0 this is already done by default in consoles. In case it's too noisy and you want to turn it off you can do:
ActiveRecord::Base.logger = nil
Stick a puts query_object.class somewhere to see what type of object your working with, then lookup the docs.
For example, in Rails 3.0, scopes use ActiveRecord::Relation which has a #to_sql method. For example:
class Contact < ActiveRecord::Base
scope :frequently_contacted, where('messages_count > 10000')
end
Then, somewhere you can do:
puts Contact.frequently_contacted.to_sql
just use to_sql method and it'll output the sql query that will be run. it works on an active record relation.
irb(main):033:0> User.limit(10).where(:username => 'banana').to_sql
=> "SELECT "users".* FROM "users" WHERE "users"."username" = 'banana'
LIMIT 10"
when doing find, it won't work, so you'll need to add that id manually to the query or run it using where.
irb(main):037:0* User.where(id: 1).to_sql
=> "SELECT "users".* FROM "users" WHERE "users"."id" = 1"
This may be an old question but I use:
SampleModel.find(:all,
:select => "DISTINCT(*)",
:conditions => ["`date` > #{self.date}"],
:limit=> 1,
:order => '`date`',
:group => "`date`"
).explain
The explain method will give quite a detailed SQL statement on what its going to do
This is what I usually do to get SQL generated in console
-> script/console
Loading development environment (Rails 2.1.2)
>> ActiveRecord::Base.logger = Logger.new STDOUT
>> Event.first
You have to do this when you first start the console, if you do this after you have typed some code, it doesn't seem to work
Can't really take credit for this, found it long time ago from someone's blog and can't remember whose it is.
When last I tried to do this there was no official way to do it. I resorted to using the function that find and its friends use to generate their queries directly. It is private API so there is a huge risk that Rails 3 will totally break it, but for debugging, it is an ok solution.
The method is construct_finder_sql(options) (lib/active_record/base.rb:1681) you will have to use send because it is private.
Edit: construct_finder_sql was removed in Rails 5.1.0.beta1.
Create a .irbrc file in your home directory and paste this in:
if ENV.include?('RAILS_ENV') && !Object.const_defined?('RAILS_DEFAULT_LOGGER')
require 'logger'
RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
end
That will output SQL statements into your irb session as you go.
EDIT: Sorry that will execute the query still, but it's closest I know of.
EDIT: Now with arel, you can build up scopes/methods as long as the object returns ActiveRecord::Relation and call .to_sql on it and it will out put the sql that is going to be executed.
My typical way to see what sql it uses is to introduce a "bug" in the sql, then you'll get an error messages spit out to the normal logger (and web screen) that has the sql in question. No need to find where stdout is going...
Try the show_sql plugin. The plugin enables you to print the SQL without running it
SampleModel.sql(:select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")
You could change the connection's log method to raise an exception, preventing the query from being run.
It's a total hack, but it seems to work for me (Rails 2.2.2, MySQL):
module ActiveRecord
module ConnectionAdapters
class AbstractAdapter
def log_with_raise(sql, name, &block)
puts sql
raise 'aborting select' if caller.any? { |l| l =~ /`select'/ }
log_without_raise(sql, name, &block)
end
alias_method_chain :log, :raise
end
end
end
You can simply use to_sql() function with the active record
Form.where(status:"Active").to_sql
In Rails 3 you can add this line to the config/environments/development.rb
config.active_record.logger = Logger.new(STDOUT)
It will however execute the query. But half got answered :