How could I change the column name in the argument of a where clause? Something like this is what I'm after:
def self.filter_by_time(datetime)
where('? < ?', datetime, Time.now)
end
Thanks!
def self.filter_by_time(datetime)
where("#{datetime} < ?", Time.now)
end
You can try this:
def self.filter_by_time(datetime)
where("datetime < ?", Time.now)
end
Related
I have a model that has two columns (started_at and ended_at). I want to add a custom validator that ensures that no other record exists with dates that overlap with the record I'm validating. So far I have:
# app/models/event.rb
class Event < ActiveRecord::Base
validates_with EventValidator
end
# app/validators/event_validator.rb
class EventValidator < ActiveModel::Validator
attr_reader :record
def validate(record)
#record = record
validate_dates
end
private
def validate_dates
started_at = record.started_at
ended_at = record.ended_at
arel_table = record.class.arel_table
# This is where I'm not quite sure what type of query I need to perform...
constraints = arel_table[:started_at].gteq(ended_at)
.and(arel_table[:ended_at].lteq(started_at))
if record.persisted?
constraints = constraints
.and(arel_table[:id].not_eq(record.id))
end
if record.class.where(constraints).exists?
record.error[:base] << "Overlaps with another event"
end
end
end
I don't know exactly what query I need to ensurethat there is no overlapping. Any help is greatly appreciated
I don't use Arel but I think the query should be:
constraints = arel_table[:started_at].lteq(ended_at)
.and(arel_table[:ended_at].gteq(started_at))
Two periods overlap when
period1.start < period2.end
period1.end > period2.start
Have a look at Validates Overlap gem
You can either use it instead your code or take condition code from it
# Return the condition string depend on exclude_edges option.
def condition_string(starts_at_attr, ends_at_attr)
except_option = Array(options[:exclude_edges]).map(&:to_s)
starts_at_sign = except_option.include?(starts_at_attr.to_s.split(".").last) ? "<" : "<="
ends_at_sign = except_option.include?(ends_at_attr.to_s.split(".").last) ? ">" : ">="
query = []
query << "(#{ends_at_attr} IS NULL OR #{ends_at_attr} #{ends_at_sign} :starts_at_value)"
query << "(#{starts_at_attr} IS NULL OR #{starts_at_attr} #{starts_at_sign} :ends_at_value)"
query.join(" AND ")
end
I would construct a query that looks something like this:
Event.exists?( 'started_at < ? AND ended_at > ?', ended_at, started_at )
If this returns true, an overlapping record exists.
I am trying to search my postgresql db in rails. I followed the Railscasts #111 Advanced Search tutorial and it is working for the name and category of my items column in plain text. However, I want to set a min/max price on my search as well which is where I come into my problem. In my db my price is stored as a string in the format "AU $49.95". Can I convert this into a float on the fly in my scoped search? If so how? If not, what should I do?
Here is the code:
search.rb
class Search < ActiveRecord::Base
attr_accessible :keywords, :catagory, :minimum_price, :maximum_price
def items
#items ||= find_items
end
private
def find_items
scope = Item.scoped({})
scope = scope.scoped :conditions => ["to_tsvector('english', items.name) ## plainto_tsquery(?)", "%#{keywords}%"] unless keywords.blank?
scope = scope.scoped :conditions => ["items.price >= ?", "AU \$#{minimum_price.to_s}"] unless minimum_price.blank?
# scope = scope.scoped :conditions => ["items.price <= ?", "AU \$#{maximum_price.to_s}"] unless maximum_price.blank?
scope = scope.scoped :conditions => ["to_tsvector('english', items.catagory) ## ?", catagory] unless catagory.blank?
scope
end
end
searches_controller.rb
class SearchesController < ApplicationController
def new
#search = Search.new
end
def create
#search = Search.new(params[:search])
if #search.save
redirect_to #search, :notice => "Successfully created search."
else
render :action => 'new'
end
end
def show
#search = Search.find(params[:id])
end
end
Thanks for reading this far!
Use the data type numeric or money for exact numerical calculation without rounding errors - and sorting as a number (not as text).
Converting string literal to numeric should not be a performance problem at all.
def self.search(search)
if search
find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
end
end
How would I modify this function so that it first checks if there is an exact match for the search parameter in the name field and, if there is an exact match, returns that match instead of the result of the LIKE ? query?
You could try the following, although it might potentially take two queries:
result = where("name = #{search}")
result.empty? ? where("name LIKE %#{search}%") : result
def self.search(search)
if search
results = find(:all, :conditions => ['name LIKE ?', "#{search}"])
results = find(:all, :conditions => ['name LIKE ?', "%#{search}%"]) if results.empty?
end
end
For complex searches I would suggest to use a seatrch engine like sphinx
I got a model called items, with a field called weeks. The content in weeks is as follows:
{2011=>["46", "47", "48", "49"]}
How can i do something like this:
Item.where(:week => week, :year => year)
When just passing one week example: 47 and year 2011
Thanks.
# Model
class Item < AR::Base
def self.with_week(weeek)
where("week LIKE (?)", "\"#{week}\"")
end
def self.with_year(year)
where("week LIKE (?)", "{#{year}=>")
end
end
usage
#items = Item.with_week(47).with_year(2011)
I want to find records in ActiveRecord that have an attribute that is either nil or some value:
class Model < ActiveRecord::Base
end
class CreateModels < ActiveRecord::Migration
def self.up
create_table :models do |t|
t.string :some_property
end
end
def self.down
drop_table :models
end
end
Model.all(:conditions => ["some_property IN (?)"], [nil, "this value"])
How do I set that up?
Try this
Model.all(:conditions =>
["some_property = ? or some_property is null", "this value"])
You need to split out the NULL condition separately:
Model.all(:conditions => [ "some_property=? OR some_property IS NULL", "this value" ])
To test if a value is NULL, you need to use IS NULL or IS NOT NULL.