How to use DataMapper `NOT LIKE` clause? - ruby

I'm using DataMapper in a Sinatra project. I'd like to be able to use a NOT LIKE stament in a DataMapper finder method, but cannot figure out how to do so.
One might imagine that this would work:
#people = People.all(:female => 1, :name.like.not => '%julie%')
...but DataMapper throws an error. Switching the order of not and like doesn't help.
Any way around this?

According to the docs you can use subtraction to produce NOT queries:
# Subtraction produces a NOT query
Zoo.all(:state => 'IL') - Zoo.all(:tiger_count.gte => 5)
# in SQL => SELECT * FROM "zoos" WHERE
# ("state" = 'IL' AND NOT("tiger_count" >= 5))
Therefore the answer would seem to be:
#people = People.all(:female => 1) - People.all(:name.like => '%julie%')

Related

I want to do an autocomplete with Ruby and Sequel

I am using Sequel with prostgres and Sinatra. I want to do an autocomplete search. I’ve verified my jQuery which sends a GET works fine.
The Ruby code is:
get '/search' do
search = params[:search]
DB[:candidates].select(:last).where('last LIKE ?', '_a_').each do |row|
l = row[:last]
end
end
The problem is the Sequel query:
I have tried every possible configuration of the query that I can think of with no luck.
So, for example, in the above query I get all the people who have "a" in their last name but when I change the query to:
DB[:candidates].select(:last).where('last LIKE ?', 'search')
or
DB[:candidates].select(:last).where('last LIKE ?', search) # (without '')
I get nothing.
I have done warn params.inspect which indicates the param search is being passed, so I am stuck.
Any ideas how the query should be written?
Finally, the second part of the question the results (when it works with '_a_') are rendered as {:last=>"Yao"} I would like just Yao, how can I do that?
I have tried numerous different types of query including raw SQL but no luck. Or is the approach just plain wrong?
Just installed Sequel and made working example:
require "rubygems"
require "sequel"
# connect to an in-memory database
DB = Sequel.sqlite
# create an items table
DB.create_table :items do
primary_key :id
String :name
Float :price
end
# create a dataset from the items table
items = DB[:items]
# populate the table
items.insert(:name => 'abc', :price => rand * 100)
items.insert(:name => 'def', :price => rand * 100)
items.insert(:name => 'ghi', :price => rand * 100)
items.insert(:name => 'gui', :price => rand * 100)
# print out the number of records
puts "Item count: #{items.count}"
# print out the average price
puts "The average price is: #{items.avg(:price)}"
recs = items.select(:name).where(Sequel.like(:name, 'g%'))
recs.each do |rec|
puts rec.values
end
I think you will get the point.
UPDATED
So in your case you should try this:
DB[:candidates]
.select(:last)
.where(Sequel.like(:last, "#{search}%"))
.map{|rec| rec.values}.flatten
It should return array of found strings.
Copy/pasting from the Sequel documentation:
You can search SQL strings in a case sensitive manner using the Sequel.like method:
items.where(Sequel.like(:name, 'Acme%')).sql
#=> "SELECT * FROM items WHERE (name LIKE 'Acme%')"
You can search SQL strings in a case insensitive manner using the Sequel.ilike method:
items.where(Sequel.ilike(:name, 'Acme%')).sql
#=> "SELECT * FROM items WHERE (name ILIKE 'Acme%')"
You can specify a Regexp as a like argument, but this will probably only work on PostgreSQL and MySQL:
items.where(Sequel.like(:name, /Acme.*/)).sql
#=> "SELECT * FROM items WHERE (name ~ 'Acme.*')"
Like can also take more than one argument:
items.where(Sequel.like(:name, 'Acme%', /Beta.*/)).sql
#=> "SELECT * FROM items WHERE ((name LIKE 'Acme%') OR (name ~ 'Beta.*'))"
Open up a Sequel console (not your Sinatra app) and play with the query until you get results back. Since you say you want only the last column your query should be something like:
# Search anywhere inside the last name
DB[:candidates].where( Sequel.ilike(:last, "%#{search}%") ).select_map(:last)
# Find last names starting with the search string
DB[:candidates].where( Sequel.ilike(:last, "#{search}%") ).select_map(:last)
Uglier alternatives:
DB[:candidates]
.select(:last)
.where( Sequel.ilike(:last, "%#{search}%") )
.all
.map{ |hash| hash[:last] }
DB[:candidates]
.select(:last)
.where( Sequel.ilike(:last, "%#{search}%") )
.map( :last )
If you want to rank the search results by the best matches, you might be interested in my free LiqrrdMetal library. Instead of searching on the DB, you would pull a full list of all last names into Ruby and use LiqrrdMetal to search through them. This would allow a search string of "pho" to match both "Phong" as well as "Phrogz", with the former scoring higher in the rankings.

Ruby: How to get array instead of DataMapper::Collection in query result

here is my code:
today_ids_from_db = Rating.all(:fields => 'my_id', :ts_d => Time.now)
today_ids_from_db.class == DataMapper::Collection
but I need to get simple ruby array with my_id values
how to achieve this?
You can also try out this Activerecord beauty called pluck
Rating.where(:ts_d => Time.now).pluck(:my_id)
Try this code
Rating.all(:ts_d => Time.now).map(&:my_id)

Trying to call find_by_id on ActiveRecord::Relation, Arel exception

I'm getting to grips with Rails 3 and I can't seem to do a basic find from a result set. My code looks like the following:
#project =
#user.where({:projects => {:project_member_id => user.id}}).find_by_id(params[:id])
I understand that the "where" section will not return a collection but merely create a query that is waiting to be run against the db. However, I can't understand why I get the following error when I try to run the find_by_id:
undefined method `to_sql' for #<Arel::Attributes::String:0x106d9ecf0>
Can somebody point out where I'm going wrong please?
May be you can write something like this:
#project =
#user.where({:projects => {:project_member_id => user.id}}).where(:id => params[:id])
I think it will works.
Alex.
I could be misunderstanding what you are trying to do here, but it looks like you have an association called projects in the user model, right? If so, this is much simpler (and works):
#project = #user.projects.find params[:id]
If you don't have an association setup, you should look into them. There is a great rails guide here on the subject.
I hope this helps.
As you mentioned above, "where" would return ActiveRecord::Relation object, you can check it by running:
#project =
#user.where({:projects => {:project_member_id => user.id}}).class
In order to return a collection of instantiated objects and run find, you can try to force it by running something like:
#project =
#user.where({:projects => {:project_member_id => user.id}}).all.find_by_id(params[:id])
Love method chaining in Rails!
However, this would first find all projects with passed uesr.id (which can be costly, and, probably not what you want)...
Good luck!
Try
#project =
#user.where({:projects => {:project_member_id => user.id}}).where(:id => params[:id]).first
But I dont understand why you are doing all these when you have the primary key with you..just Model.find params[:id] is enough I think (I assume there is something more to it).
Try this:
#project = #user.where({:projects => {:project_member_id => user.id}}).all.find {|p| p.id == params[:id]}

How do you handle type/code columns (non-STI) with AR?

I was discussing this pattern with a colleague and everyone seems to have their own take ont this. Thought using a type/code attribute seems to be a pretty common case, there's no standardized solution for it. I wonder what is yours?
Say you have a non-Single Table Inheritance type/code column on a ActiveRecord model. What do you use to specify types and create accessors, scopes, etc. A hash of symbols, a hash of codes, plain constants or...?
Something like the following:
class Listing < ActiveRecord::Base
LISTING_TYPES = {
:sale => 1,
:rent => 2,
:lease => 3,
}
validates :listing_type, :inclusion => {:in => LISTING_TYPES.values}
end
Thanks in advance.
I use the simple_enum gem.
class Listing < ActiveRecord::Base
as_enum :types, {:sale => 0, :rent => 1, :lease => 2}
end
The gem creates all the finders for you so you can query by the types and not the index:
Listing.find_by_type(:sale)

Preventing SQL Injection/Good Ruby method

What is a good method in Ruby to prevent SQL Injection?
in straight up ruby? use prepared statements:
require 'mysql'
db = Mysql.new('localhost', 'user', 'password', 'database')
statement = db.prepare "SELECT * FROM table WHERE field = ?"
statement.execute 'value'
statement.fetch
statement.close
Not just in Ruby - bind your parameters (be it in the database, or in your client code).
Check out the guide they have up on this: http://guides.rubyonrails.org/security.html#injection
Basically, you want to use bind variables in your models to find data, rather than inline parameters..
Model.find(:first, :conditions => ["login = ? AND password = ?", entered_user_name, entered_password])
According to http://ruby.railstutorial.org/ you can prevent Cross Site Request Forgery by inserting the
<%= csrf_meta_tags %>
tag in the header of app/views/layouts/application.html.erb.
Direct link of example
This thread references:
http://www.ruby-forum.com/topic/90258#new
http://www.ruby-forum.com/topic/82349#143790
ActiveRecord's find() method has built in ways to avoid SQL injection by
using the format
> :conditions => [ "user_name = ?", user_name]
Is there any such system for escaping injection in order? It seems to
only take a string and feed it to the SQL statement. This causes a
vulnerability when using params to set : order as in this function:
def list
sort_by = params[:sort_by]
#book_pages, #books = paginate :books,
:order => sort_by,
:per_page => 10
end
We've tried a few methods to sanitize sort_by such as order => ['?',
sort_by] but it just passes that to the SQL statement like a flattened
array. The 'escaping magic' does not work for order. Should I use
gsub! to sanitize params[:sort_by]?

Resources