How do I perform a search in Rails3? - sql

I'm aware that I can search queries by calling where() on a model as follows:
Post.where(:title => 'My First Post')
However, what if I don't know if the user wants to filter out the search parameters?
For example, I have a search form that has an optional title field. If the title is filled, the form should search for a title. If it is not, however, the form should just return all fields.
I tried doing something along the lines of
search = Post.all
if params[:title].present?
search.where(:title => params[:title])
end
However, Rails immidiately returns the result of the search when I call Post.all and I cannot further add conditions/
How do I do this?
Thanks!

.all is a 'finisher' method for arel and causes the query to actually be called (same goes for .each and .first). So, if you want to be able to keep building up the scope conditionally, .all should be the last thing called, if you want to call it at all.

Given that there really isn't much in the way of relations to chain here, it would seem that a simple one-liner with a ternary operator might do:
#posts = params[:title].present? ? Post.where(:title => params[:title]) : Post.all
... because when there is no :where clause scope, you couldn't later chain it to :all, anyway.

Related

Rails3 Autocomplete Gem - Two Different Autocompletes on Same ClassName and Method

I am successfully using the ":scopes =>" option of this gem to return a subset of the rows of the table. Now I want to create a nearly identical autocomplete, on the same ClassName and Method, with a different scope, for a different view.
The problem is, the first two arguments of autocomplete in the controller are ClassName and Method, and those also create the functional name of the autocomplete function, as it is used in the view and route. Therefore, both of my autocompletes would have the same name.
Is there a workaround I can use to assign a different name to each of the similar autocompletes?
Example code:
autocomplete :my_class_name, :my_method, :display_value => :my_formatting_method,
:extra_data => [:id], :scopes => [:active_and_special]
def active_and_special
where("active = ?", true).
where("special = ?", true).
order("name ASC")
end
Then the again for the 2nd one, with the same class-name and method, but a different scope.
Best workaround I've found is to use "column_name" to override the part of the naming which ordinarily refers to the method - like this:
autocomplete :my_class_name, :foo, :column_name => 'my_method',
:display_value => :my_formatting_method, :extra_data => [:id],
:scopes => [:active_and_not_special]
def active_and_not_special
where("active = ?", true).
where("special = ?", false).
order("name ASC")
end
This has route:
get 'my_class_names/autocomplete_my_class_name_foo'
Whereas the original has route:
get 'my_class_names/autocomplete_my_class_name_my_method'
This allows us to generate a 2nd, different autocomplete, on the same Class and Method as another, with its own unique name.
Having an independent name, vs the 'magic' autogenerated one (to save 1 second of typing?) would prevent this trouble - or am I missing something?
I'll leave this open for a bit to see if anyone has a better solution.

will_paginate for results of method on an object

I'm working on a rails app that searches on a database of users. Because the search is fairly complex I have a search model that stores the different search parameters and a results method in that model that actually puts together the query and returns an array of objects.
Since there is no relationship between search and the objects returned in results, is it still possible to paginate the results with will_paginate? Am I going about this the wrong way?
It should be possible to do what you do, without needing to paginate the array. According to the docs:
# paginate in Active Record now returns a Relation
Post.where(:published => true).paginate(:page => params[:page]).order('id DESC')
# the new, shorter page() method
Post.order('created_at DESC').page(params[:page])
(( from https://github.com/mislav/will_paginate/wiki ))
... so you should be able to create your "where" even as a model method and then paginate the results of the model method.
You can paginate the array, if you absolutely have to, according to these instructions:
https://github.com/mislav/will_paginate/wiki/Backwards-incompatibility#willpaginatecollection

Rails 3: Excluding Results by Default

On my site, moderators can flag spammy comments. When these comments are flagged, they get quarantined so they no longer appear in regular views, though they can still be seen in the administrative control panel. At the moment, I exclude them from regular views like so:
#comments = Comment.where(:flagged => false)
I do this in every controller that has comments in it, of which there are many. I get the feeling that there's a cleaner way to handle this in Rails. Perhaps somewhere in the comments model I can specify that when querying for comments, only retrieve those that aren't flagged. If so, how is that done? And even if that's not possible, is there some other way to dry this code?
u can use a default scope
default_scope where(:flagged => false)
see http://apidock.com/rails/ActiveRecord/Base/default_scope/class
the default scope can be ignored using unscoped. See http://apidock.com/rails/ActiveRecord/Base/unscoped/class
i would prefer using a scope rather a default scope since i dont have to override it when all the records are needed. Depends upon the frequency of fetching all/unflagged records.
Make a scope (named 'clean' for this example):
class Comment < ActiveRecord
scope :clean, where(:flagged => false)
end
Then use:
#comments = Comment.clean
For future-proofing, you may may want to add a class method called default_view which just calls clean and use that instead. As your 'default' needs change, just modify the default_view method.

Can anyone explain how CDbCriteria->scopes works?

I've just checked the man page of CDbCriteria, but there is not enough info about it.
This property is available since v1.1.7 and I couldn't find any help for it.
Is it for dynamically changing Model->scopes "on-the-fly"?
Scopes are an easy way to create simple filters by default. With a scope you can sort your results by specific columns automatically, limit the results, apply conditions, etc. In the links provided by #ldg there's a big example of how cool they are:
$posts=Post::model()->published()->recently()->findAll();
Somebody is retrieving all the recently published posts in one single line. They are easier to maintain than inline conditions (for example Post::model()->findAll('status=1')) and are encapsulated inside each model, which means big transparency and ease of use.
Plus, you can create your own parameter based scopes like this:
public function last($amount)
{
$this->getDbCriteria()->mergeWith(array(
'order' => 't.create_time DESC',
'limit' => $amount,
));
return $this;
}
Adding something like this into a Model will let you choose the amount of objects you want to retrieve from the database (sorted by its create time).
By returning the object itself you allow method chaining.
Here's an example:
$last3posts=Post::model()->last(3)->findAll();
Gets the last 3 items. Of course you can expand the example to almost any property in the database. Cheers
Yes, scopes can be used to change the attributes of CDbCriteria with pre-built conditions and can also be passed parameters. Before 1.1.7 you could use them in a model() query and can be chained together. See:
http://www.yiiframework.com/doc/guide/1.1/en/database.ar#named-scopes
Since 1.1.7, you can also use scopes as a CDbCriteria property.
See: http://www.yiiframework.com/doc/guide/1.1/en/database.arr#relational-query-with-named-scopes

Rails: Load just one attribute not a whole model

Lets say I have a model, Foo, which is big and has lots of components. For a given Ajax query I'm only interested in one particular attribute, bar, which is a column in the foos table.
Is there a simple way I could load just that attribute, and not bother with retrieving the rest of the record? For instance if all I want to know is the bar for Foo with id#__, how could I retrieve that?
You can return only specific columns by calling the select method with a string containing the attributes you want to return. For your example:
Foo.select('bar').first #<Foo bar: 1>
Keep in mind that these objects will act like normal ActiveRecord objects but return nil for any field you did not select, so take care using this functionality.
You can call select on the class name itself or any Relation, so you can chain together the ActiveRecord calls you usually use like where, etc.
I prefer this
User.where(:id => user_id).pluck(:user_name).first #'tom'
Foo.where(:age => 23).pluck(:user_name) #['tom', 'jerry', ...]
Foo.where(<condition>).select('fieldname')
Example
results = Foo.where('is_active = ?', true).select('bar')
Access the selected fields as:
results.map {|res| res.bar} returns an array of bar's
pluck(*column_names)
doc: http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-pluck
e.g. Foo.pluck(:bar)
pick(*column_names) select just one top row's columns, docs
Similar to pluck but fetch only one row