With rails 3.2, mongoid
I don't want full-text search, I just wanna search one field of the model.
E.g
I have a "People" scaffold with just a name field.
And I want to have a search form. For instance, I search "peter", if there's an exact match(case insensitive) of the search term - "peter" in the database, then I want it immediately redirect to peter show page without listing further search results.
However, if there's no exact match, then suggested results(in the database) will be shown.
Please kindly advise.
You need test if the exact match exist in first case and use after a regexp to have some possibilities of result
user = User.where(:name => params[:name])
if user
redirect_to user_url(user)
return
else
#users = User.where(:name => /params[:name]/i)
end
Related
Let's say I've got User class with an :email field. And let's say I'm using activeadmin to manage Users.
Making a filter that returns emails that match one string, e.g. "smith", is very simple. In admin/user.rb, I just include the line
filter :email
This gives me a filter widget that does the job.
However, this filter doesn't let me search for the intersection of multiple terms. I can search for emails containing "smith", but not for emails containing both "smith" AND ".edu".
Google tells me that activerecord uses Ransack under the hood, and the Ransack demo has an 'advanced' mode that permits multiple term searches.
What's the easiest way to get a multiple term search widget into activeadmin?
Ideally, I'd like a widget that would allow me to enter smith .edu or smith AND .edu to filter for emails containing both terms.
there is simple solution using ranasckable scopes
So put something like this in your model
class User < ActiveRecord::Base
....
scope :email_includes, ->(search) {
current_scope = self
search.split.uniq.each do |word|
current_scope = current_scope.where('user.email ILIKE ?', "%#{word}%")
end
current_scope
}
def self.ransackable_scopes(auth_object = nil)
[ :email_includes]
end
end
After this you can add filter with AA DSL
Like
filter :email_includes, as: :string, label: "Email"
UPD
should work if change email_contains_any to email_includes
I've figured out a solution but it's not pretty.
The good news is that Ransack has no trouble with multiple terms searches. These searches use the 'predicate' cont_all. The following line works for finding emails containing 'smith' and '.edu'.
User.ransack(email_cont_all: ['smith','.edu'] ).result
Since these searches are easy in Ransack, they're probably straightforward in Activeadmin, right? Wrong! To get them working, I needed to do three things.
I put a custom ransack method (a.k.a. ransacker) into User.rb. I named the ransacker email_multiple_terms.
class User < ActiveRecord::Base
# ...
ransacker :email_multiple_terms do |parent|
parent.table[:path]
end
I declared a filter in my activeadmin dashboard, and associated it with the ransacker. Note that the search predicate cont_all is appended to the ransacker name.
admin/User.rb:
ActiveAdmin.register User do
# ...
filter :email_multiple_terms_cont_all, label: "Email", as: :string
This line creates the filter widget in Activeadmin. We're nearly there. One problem left: Activeadmin sends search queries to ransack as a single string (e.g. "smith .edu"), whereas our ransacker wants the search terms as an array. Somewhere, we need to convert the single string into an array of search terms.
I modified activeadmin to split the search string under certain conditions. The logic is in a method that I added to lib/active_admin/resource_controller/data_access.rb.
def split_search_params(params)
params.keys.each do |key|
if key.ends_with? "_any" or key.ends_with? "_all"
params[key] = params[key].split # turn into array
end
end
params
end
I then called this method inside apply_filtering.
def apply_filtering(chain)
#search = chain.ransack split_search_params clean_search_params params[:q]
#search.result
end
This code is live in my own fork of activeadmin, here: https://github.com/d-H-/activeadmin
So, to get multiple term search working, follow steps 1 and 2 above, and include my fork of A.A. in your Gemfile:
gem 'activeadmin', :git => 'git://github.com/d-H-/activeadmin.git'
HTH.
If anyone's got a simpler method, please share!
Just add three filters to your model:
filter :email_cont
filter :email_start
filter :email_end
It gives you a flexible way to manage your search.
This filter executes next sql code:
SELECT "admin_users".* FROM "admin_users"
WHERE ("admin_users"."email" ILIKE '%smith%' AND
"admin_users"."email" ILIKE '%\.edu')
ORDER BY "admin_users"."id" desc LIMIT 30 OFFSET 0
I expect that exactly what you're looking for.
I have users, cafes and their food_items(which have some ingredients listed). Until now i used solr to search for food_items via some ingredients that a user likes. This was accomplished using sunspot-solr search according to the sunspot docs
Also, i am able to gather a relative like-ness of a user to different cafes(based on how many times he has visited it, searched its menu etc)(this is a dynamic value that will be generated on the fly)
Problem:
I want to show the same results(food_items) fetched via solr, ranked by cafes(result re-ranking)(based on the like-ness of the user to a cafe) using sunspot solr for rails
This app is hosted on heroku and uses websolr
i have found these:
https://cwiki.apache.org/confluence/display/solr/Query+Re-Ranking
https://cwiki.apache.org/confluence/display/solr/RankQuery+API
but i have no idea as to how i can create a QParserPlugin or generate a rank query in sunspot.
sunspot provides a way to write custom queries. so if i could get help in constructing a query to fetch the like-ness and rank each record (or) any other way to implement such logic, that would be great. thanks!
you can do something like:-
def build_query(where_conditions)
condition_procs = where_conditions.map{|c| build_condition c}
Sunspot.search(table_clazz) do
condition_procs.each{|c| instance_eval &c}
paginate(:page => page, :per_page => per_page)
end
end
def build_condition(condition)
Proc.new do
# write this code as if it was inside the sunspot search block
keywords condition['words'], :fields => condition[:field].to_sym
end
end
conditions = [{words: "tasty pizza", field: "title"},
{words: "cheap", field: "description"}]
build_query conditions
In a Sunspot search, is it possible to use an attribute of the current model in the if statement of a sunspot scope?
UserFile.search do
keywords searchstring
order_by :last_edit_date
with(:visibility,true) if some_method?(:user_id)
paginate :page => page, :per_page => Pagination::FILES_PER_PAGE if page
end
In this example I want to call some_method? with complicated logic using the :user_id attribute of the UserFile model (this method will involve ActiveRecord queries).
What I am trying to to is limit search results such that files whose owners are strangers to the searcher (not friends with the searcher and not the searcher himself) do not show up if :visibility of UserFile is "1" (friends only) or "2" (private), and files whose owners are friends with the searcher do not show up if :visibility is "2" (private).
Is this possible with Solr/Sunspot or do I have to filter out the results once the search has finished? I don't want to resort to this since it will make pagination difficult.
You cannot do this because the search block just passes in parameters to Solr to do the search. If you want to do this filtering purely in Solr (and have it actually be faster than using a database search), you will need to index the user id's who can see each record:
In your searchable block
integer :viewer_ids, references: User, multiple: true do
some_method_that_returns_viewer_ids
end
You also need to make sure that saving whatever records changes these triggers a re-index.
Looking at the railscasts.com, I am wondering how Ryan implemented the search filters for the site. If the search param does not match anything in the database, then he is returning ALL records (for example, if we type an invalid param for the search param value in the URL, all records are returned because there is nothing to filter on). If it matches records in the database he is returning only those matching records.
How is being achieved? Can I use only Active Record without any gems/full-text-search for this? How would that query look like with Arel?
Also, how does he implement the filters link (links at the top of the page after we do a search)? Is he parsing through each of the search params and generating the links on the page by stripping out the search params one at a time?
I do not see Railscasts.com is showing all records when invalid filters (like search parameters). When no record is found, it is returning a link to see all episodes. For example, see this link:
http://railscasts.com/episodes?utf8=%E2%9C%93&search=
It says:
No episodes found. See all episodes.
However, you can show all records in such cases if you want. it's very easy.
def index
#records = []
if params[:search].present?
#YOUR SEARCH LOGIC and assign to #records. for example:
#records = Episode.where(:title => params[:search])
end
unless #records.length
#records = Episode.all
end
end
I have a Ruby on Rails site that uses thinking sphinx for searching a postgres database.
One of the fields in a table I am searching on is a boolean.
I'd like to be able to match on that boolean when a certain keyword is used in the search query.
To fully explain with an example:
My site is for people who develop their own black and white film.
I have a recipe table where people describe how they develop a film. That table has a boolean column called "stand_developed" (a method of developing film).
I'd like to return results where that field is true when the user searches for the word "stand".
I've been through the sphinx docs and not really found if it's possible.
I guess I could hack something inside my controller method by parsing the query terms and adding a condition but is there a cleaner way of doing it?
This is what I've done as far as searching on boolean fields using ThinkingSphinx. Pass stand_developed as a URL parameter along with your query_string in the following ways:
URL for a general query without search on stand_developed will be http://yoursite.com/search?q=your_query_string
URL for query with stand_developed == TRUE will be http://yoursite.com/search?q=your_query_string&stand_developed=1
URL for query with stand_developed == FALSE will be http://yoursite.com/search?q=your_query_string&stand_developed=0
Then, in your controller, you would do this:
if params[:stand_developed] && params[:stand_developed].to_i == 1
# perform query search with stand_developed == true
#search_results = YourModel.search(params[:q], :with => {:stand_developed => true})
elsif params[:stand_developed] && params[:stand_developed].to_i == 0
# perform query search with stand_developed == false
#search_results = YourModel.search(params[:q], :with => {:stand_developed => false})
else
# perform general query search
#search_results = YourModel.search(params[:q])
end
You could have just tested if params[:search] included the text 'stand' then done the searching from there. You don't need an extra column in your table, that's just overhead that's not needed.
if params[:search].downcase.include?('stand')
Model.search params[:search], :with => {:stand_developed => true}
else
Model.search params[:search]
end
I've come up with a solution to this now.
I tried the "hack" I mentioned in my question - parsing the word "stand" and searching for it explicitly (a variation on the answer from Paul Davis) but it didn't work very well.
I didn't explain this in my question (didn't realise the full implications at the time I asked) but I need it to also match if the user used the word "stand" in their recipe description too.
So, I tried to get sphinx to add a condition along the lines of "if stand_developed is true or notes contains 'stand'" but I couldn't seem to find the right syntax for that.
It also had to deal with any other search text too.
In the end I added an extra column to my recipe table called "search_tags" and I add the word "stand" into it if the user selects "stand_developed" when adding a recipe.
I then get Sphinx to index that field as well as my other fields and it all works perfectly.