Pretty much a noobie here, so I appreciate any help someone can give.
I'm trying to add faceting to the search on my site through Sunspot. Ryan just released a great Railscast which got me started: http://railscasts.com/episodes/278-search-with-sunspot. I got that working and was able to add additional facets. My problem is that the facets are independent of each other. If I have 3 facets on 3 different attributes, when I select a facet once I already have on selected, I would like to display only results falling into both of those facests. As of now, it just switches from one facet to the other. I feel like this shouldn't be that difficult, but I can't figure out how to do it.
I did find this tutorial: http://blog.upubly.com/2011/01/06/using-sunspot-in-your-views/ which I think is doing what I want. I tried to get this working but, even when I attempt to make it work with just one facet I don't any results listed. Just the facet name and then nothing else.
Thoughts?
Thank you!!
UPDATE
Here is the code samples of what I am trying to do:
Adjusting the Railscasts code I got this:
In my StylesController:
def index
#search = Style.search do
fulltext params[:search]
facet :departmental, :seasonal, :classifier
with(:departmental, params[:department]) if params[:department].present?
with(:classifier, params[:classification]) if params[:classification].present?
with(:seasonal, params[:season]) if params[:season].present?
end
In my Style Index view (I know I need to condense this)
= form_tag styles_path, :method => :get do
%p
= text_field_tag :search, params[:search]
= submit_tag "Search", :name => nil
#facets
%h4 Departments
%ul
- for row in #search.facet(:departmental).rows
%li
- if params[:department].blank?
= link_to row.value, :department => row.value
(#{row.count})
- else
%strong= row.value
(#{link_to "remove", :department => nil})
%h4 Classifications
%ul
- for row in #search.facet(:classifier).rows
%li
- if params[:classification].blank?
= link_to row.value, :classification => row.value
(#{row.count})
- else
%strong= row.value
(#{link_to "remove", :classification => nil})
%h4 Seasons
%ul
- for row in #search.facet(:seasonal).rows
%li
- if params[:season].blank?
= link_to row.value, :season => row.value
(#{row.count})
- else
%strong= row.value
(#{link_to "remove", :season => nil})
In my Style Model:
searchable do
text :number, :description, :department, :classification, :season
string :departmental
string :classifier
string :seasonal
end
def departmental
self.department
end
def classifier
self.classification
end
def seasonal
self.season
end
And my version of the upubly code, paired down to just try to get the "seasonal" facet working:
I left the the Search Partial, the Search Model and the SearchHelper the same as in the example. I tried to mess with the Helper as my Facets will be pulling text values, not just IDs of other Models, but to no avail. I don't have my various attributes set up as individual Models as I didn't think I needed that functionality, but I am starting to think otherwise.
StylesController:
def index
#title = "All styles"
#search = search = Search.new(params[:search]) # need to set local variable to pass into search method
#search.url = styles_path
#search.facets = [:seasonal]
#solr_search = Style.search do
keywords search.query
with(:seasonal, true)
search.facets.each do |item|
facet(item)
with(:seasonal, params[:season]) if params[:season].present?
end
any_of do
# filter by facets
search.facets.each do |item|
with(item).all_of( params[item].try(:split, "-") ) if params[item].present?
end
end
paginate(:page => params[:page], :per_page => 10)
end
Again, I appreciate the help. Definitely a noob, but really enjoying the process of building this site. Stackoverflow has been a HUGE help for me already, so I owe everybody who posts answers on here a big-time thank you.
I needed the answer to this myself, and seeing as there seems to be nothing else on the web about it, I decided I'd try to figure it out myself.
First I came to the conclusion through logic, that the controller can handle multiple facets and there's no reasons it cannot, I remembered that the best part about ruby is that it is the most human readable code, try to read your first controller and you'll see that it makes sense that it works. I tested this by manually entering in a query string in url, which returned expected results. Therefore, once I figured that out, I knew the issue resided in my view (which made me facepalm because it's fairly obvious now)
Your example is significantly more complex than mine, and my answer might not 100% meet every requirement but I'm pretty sure it's close. Also your code in your model regarding "departmental" etc is a little redundant in my view
Controller
def index
#search = Style.search do
fulltext params[:search]
facet :departmental, :seasonal, :classifier
with(:departmental, params[:department]) if params[:department].present?
with(:classifier, params[:classification]) if params[:classification].present?
with(:seasonal, params[:season]) if params[:season].present?
end
View
%h4 Departments
%ul
- for row in #search.facet(:departmental).rows
%li
- if params[:department].blank?
= link_to row.value, styles_path(
:department => row.value,
:classification => (params[:classification] unless params[:season].blank?),
:season => (params[:season] unless params[:season].blank?))
(#{row.count})
- else
%strong= row.value
= link_to "remove", styles_path(
:department => nil,
:classification => (params[:classification] unless params[:season].blank?),
:season => (params[:season] unless params[:season].blank?))
Related
Okay so im new to this site but this is what I have:
Report.rb
def self.search(search)
if search
where('JOBLETTER_CD_NUMBER LIKE ? AND DATE LIKE? AND CUST LIKE ?', "%#{search}%")
else
scoped
end
end
end
index.html.erb
select_tag "search", options_for_select([ "Job Letter and CD #", "Date", "Cust", "Job", "Date shipped", "Date billed", "Billed by" ], params[:search])
form_tag reports_path, :method => 'get' do
text_field_tag :search, params[:search], :class=> "form-search", :align => "right"
<%= submit_tag "Search", :JOBLETTER_CD_NUMBER => nil, :class => "btn btn-success", :align => "right"
reports controller
def index
#report = Report.paginate(:per_page => 1, :page => params[:page])
#report = Report.search(params[:search]).paginate(:per_page => 1, :page => params[:page])
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #views }
end
end
The only field it will search is the Job Letter and CD # field I need it to allow me to search whatever is selected in the drop down box. Btw I am using bootstrap fro js and css functions.
Your query has 3 placeholders ? but passed only one argument "#{search}" - if you run it like that, what you really should be getting is an exceptions stating
ActiveRecord::PreparedStatementInvalid: wrong number of bind variables (1 for 3) ...
Also, your select_tag is outside the form, so it won't be passed to the controller at all. If you move it into the form, you'd have to rename (e.g. to column) it since the name search is already used by the text field. Then you could pass both the column and the search parameters to your search function to construct the query.
HOWEVER, this is not safe, since nothing prevents a user to pass in any other column by manipulating the post request, and since you can't use placeholders for column names, there's a danger of SQL injection as well.
There are many solutions out there to construct searches, no need to reinvent the wheel. Take a look at the ransack gem. Here's a recent Railscast on how to use it.
I want to highlight found words in text, for example, as shown here.
As far as I know I must follow these steps:
1) In my model, I must add :stored => true option to the field which I want to highlight:
searchable do
text :title, :stored => true
text :description
end
2) In my controller, I have to declare which field I want highlighted:
def search
#search = Article.search do
keywords params[:search] do
highlight :title
end
end
end
3) In the view I'm not sure what to do, I tried this:
- #search.each_hit_with_result do |hit, result|
%p= link_to raw(hit_title(hit)), article_path(result)
It is what doing method hit_title:
def hit_title(hit)
if highlight = hit.highlight(:title)
highlight.format { |word| "<font color='green'>#{word}</font>" }
else
h(hit.result.title)
end
end
But it doesn't work as expected, it always highlights the first word of the title, even if the searched word is at the end of it.
Is there an easier way to do this?
I bumped into this looking for a solution to render highlights from sunspot search on rails view.
I didn't find much of a ready solution anywhere, so I used part of this post to make one of my one.
I am quite new to rails so this might not be fully the RoR way.
In my case, I did a full text search on two fields, call them notes and description.
In order to be able to render to html the highlights, I introduced a hash of values containing the id of the record, the name of the column and its highlighted value, adequately formatted. This allows me to highlight the search results on different fields.
entry.rb:
searchable do
text :description, :stored => true
text :notes, :stored => true
end
entries_controller.rb:
#search = Entry.search
if params[:search].nil? || params[:search].empty?
stext=''
else
stext=params[:search]
end
fulltext stext, :highlight => true
paginate(page: params[:page], :per_page => 10)
end
#entries=#search.results
#results=Hash.new
#search.hits.each do |hit|
hit.highlights(:description).each do |highlight|
id=hit.primary_key.to_s.to_sym
fr=highlight.format { |word| "<result>#{word}</result>" }
#results.merge!(id => ["description",fr])
end
hit.highlights(:notes).each do |highlight|
id=hit.primary_key.to_s.to_sym
fr=highlight.format { |word| "<result>#{word}</result>" }
#results.merge!(id => ["notes",fr])
end
end
and on the view, wherever I want to render any value of those, I do the following:
<% #entries.each do |f| %>
<% j=f[:id].to_s.to_sym %>
<% if !#results[j].nil? && #results[j][0]=="description" %>
<%= #results[j][1].html_safe %>
<% else %>
<%= f[:description] %>
<% end %>
[...] (likewise for notes)
<% end %>
Please, note I created a css definition for <result> markup to make the text notable.
Code looks good to me for highlighting the first matching word in the title, since I have similar code. Have you tried rebuilding your solr index and restarting the servers?
Also, can you try reverting your solrconfig.xml to its default values? Someone had a similar problem after modifying solrconfig.xml, Ref https://groups.google.com/forum/#!searchin/ruby-sunspot/highlight/ruby-sunspot/kHq0Dw35UWs/ANIUwERArTQJ
If you want to override the highlighting option in solrconfig.xml, search for max_snippets on this site http://outoftime.github.io/ . You may want to try options like
highlight :title, :max_snippets => 3, :fragment_size => 0 # 0 is infinite
Are you using substring search? I've got the same problem here and realized that enabling substring match by following sunspot wiki tutorial led to the problem.
Trying to build a search on my homepage with simple_form (Pretty much same as formtastic). The search works fine and im getting my results but after submission I want to retain the vales with what the user submitted.
I am using a namespace for my form so how can I retain the data for the form. Here is some code which may help.
Controller
def index
#results = Property.search(params[:search])
end
View
%h1 Search Form
= simple_form_for(:search) do |f|
= f.input :location, :as => :select, :collection => Location.all.asc(:name)
= f.input :type, :collection => PropertyType.all.asc(:name)
= f.input :bedrooms, :collection => 1..10,
%p.box
= f.button :submit
-if #results
%h1 Search Results
.results
- #results.each do |property|
.result
%h1= property.title
Within the Index controller I have tried all sorts of things ie
#search = params[:search]
But each time I try something the search breaks.
What am I doing wrong ?
Hope you can advise
One approach is to do as Xavier Holt suggested, and pass in values to each input. The simpleform doco suggests:
= f.input :remember_me, :input_html => { :value => '1' }
The other approach is to have simpleform do it for you. SimpleForm will automatically populate the fields with values if you give it something like an activerecord object.
In this case, that means creating a model object:
class PropertySearchCriteria
attr_accessor :location, :type, :bedrooms
def initialize(options)
self.location = options[:location]
self.type = options[:bedrooms]
self.bedrooms = options[:bedrooms]
end
end
Then, change your controller:
def index
#property_search_criteria = PropertySearchCriteria.new(params[:search])
#results = Property.search(#property_search_criteria)
end
(you'll have to change the Property.search method as well)
Then, change your simple_form_for:
= simple_form_for(:search, #property_search_criteria) do |f|
And if you do all that, and get the stars to align just right, then simpleform will pre-populate the form fields all by itself. You may have to add some stuff to PropertySearchCriteria to get simpleform to be properly happy.
This is a lot of stuffing around just to get the values showing up, but it'll keep you sane if you need to add validations.
I'm doing something similar in the app I'm working on (I'm not using formtastic, but this should be at least very close to something that works for you). I got around it by making sure #search was a hash in the controller:
#search = params[:search] || {}
And then using #search[:key] as the :value option in all my search inputs (There's a chance you'll need to set #search.default = '' to get this working):
<%= text_field_tag :name, :value => #search[:name] %>
And that's all it took. As my app is getting more complicated and AJAXy, I've been thinking of moving the search parameters into the session information, which you might want to do now to stay ahead, but if you're just looking for a simple solution, this worked great for me.
Hope this helps!
you can try storing your parameters in session like so:
def index
#results = Property.search(params[:search])
store_search
end
def store_search
session[:search] = params[:search]
end
just be sure when you are done with the parameters that you clean them up
...
clear_search if session[:search]
def clear_search
session[:search] = nil
end
I'm creating an application that'll display a random picture based upon a defined letter in a word.
Images are attached to a Pictures model (containing another "letter" field) using Paperclip, and will be iterated through in an each block.
How would I go about passing the letter back from the each block to the model for random selection.
This is what I've come up with so far, but it's throwing the following error.
undefined method `%' for {:letter=>"e"}:Hash
Model:
def self.random(letter)
if (c = count) != 0
find(:first, :conditions => [:letter => letter], :offset =>rand(c))
end
end
View:
<% #letters.each do |a| %>
<%= Picture.random(a).image(:thumb) %>
<% end %>
Thanks
One problem is your conditions has a syntax error. The hash notation is wrong:
:conditions => [:letter => letter]
should be
:conditions => {:letter => letter}
Also, it seems to me that your random scope will always exclude the first Picture if you don't allow an offset of 0. Besides that, do you really want to return nil if the random number was 0?
Picture.random(a).image(:thumb) would throw "undefined method 'image' for nil:NilClass" exception every time c==0. Can probably just use:
def self.random(letter)
find(:first, :conditions => {:letter => letter}, :offset =>rand(count))
end
EDIT: You'll either need to guarantee that your db has images for all letters, or tell the user no image exists for a given letter.
<% #letters.each do |a| %>
<% if pic = Picture.random(a).image(:thumb) %>
<%= pic.image(:thumb) %>
<% else %>
No image available for <%= a %>
<% end %>
<% end %>
Or the like...
EDIT: Actually I don't think your offset strategy will work. One other approach would be to return the set of images available for the given letter and randomly select from that collection, something like:
def self.random(letter)
pics = find(:all, :conditions => {:letter => letter})
pics[rand(pics.size)] if !pics.blank?
end
I have built a blog application w/ ruby on rails and I am trying to implement a search feature. The blog application allows for users to tag posts. The tags are created in their own table and belong_to :post. When a tag is created, so is a record in the tag table where the name of the tag is tag_name and associated by post_id. Tags are strings.
I am trying to allow a user to search for any word tag_name in any order. Here is what I mean. Lets say a particular post has a tag that is 'ruby code controller'. In my current search feature, that tag will be found if the user searches for 'ruby', 'ruby code', or 'ruby code controller'. It will not be found if the user types in 'ruby controller'.
Essentially what I am saying is that I would like each word entered in the search to be searched for, not necessarily the 'string' that is entered into the search.
I have been experimenting with providing multiple textfields to allow the user to type in multiple words, and also have been playing around with the code below, but can't seem to accomplish the above. I am new to ruby and rails so sorry if this is an obvious question and prior to installing a gem or plugin I thought I would check to see if there was a simple fix. Here is my code:
View: /views/tags/index.html.erb
<% form_tag tags_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search], :class => "textfield-search" %>
<%= submit_tag "Search", :name => nil, :class => "search-button" %>
</p>
<% end %>
TagsController
def index
#tags = Tag.search(params[:search]).paginate :page => params[:page], :per_page => 5
#tagsearch = Tag.search(params[:search])
#tag_counts = Tag.count(:group => :tag_name,
:order => 'count_all DESC', :limit => 100)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #tags }
end
end
Tag Model
class Tag < ActiveRecord::Base
belongs_to :post
validates_length_of :tag_name, :maximum=>42
validates_presence_of :tag_name
def self.search(search)
if search
find(:all, :order => "created_at DESC", :conditions => ['tag_name LIKE ?', "%#{search}%"])
else
find(:all, :order => "created_at DESC")
end
end
end
If I read your problem correctly, you want to return a row if the tag names for the row matches one of the words passed in the query string.
You can rewrite your search method as follows:
def self.search(search)
all :conditions => (search ? { :tag_name => search.split} : [])
end
If you need partial matching then do the following:
def self.search(str)
return [] if str.blank?
cond_text = str.split.map{|w| "tag_name LIKE ? "}.join(" OR ")
cond_values = str.split.map{|w| "%#{w}%"}
all(:conditions => (str ? [cond_text, *cond_values] : []))
end
Edit 1
If you want pass multiple search strings then:
def self.search(*args)
return [] if args.blank?
cond_text, cond_values = [], []
args.each do |str|
next if str.blank?
cond_text << "( %s )" % str.split.map{|w| "tag_name LIKE ? "}.join(" OR ")
cond_values.concat(str.split.map{|w| "%#{w}%"})
end
all :conditions => [cond_text.join(" AND "), *cond_values]
end
Now you can make calls such as:
Tag.search("Ruby On Rails")
Tag.search("Ruby On Rails", "Houston")
Tag.search("Ruby On Rails", "Houston", "TX")
Tag.search("Ruby On Rails", "Houston", "TX", "Blah")
Tag.search("Ruby On Rails", "Houston", "TX", "Blah", ....) # n parameters
Caveat:
The wild card LIKE searches are not very efficient(as they don't use the index). You should consider using Sphinx (via ThinkingSphinx) OR Solr(via SunSpot) if you have lot of data.
You can try to set up ferret, or if you are really bend on just using rails, try this:
# Break the search string into words
words = params[:search].blank? ? [] : params[:search].split(' ')
conditions = [[]] # Why this way? You'll know soon
words.each do |word|
conditions[0] << ["tag_name LIKE ?"]
conditions << "%#{word}%"
end
conditions[0] = conditions.first.join(" OR ") # Converts condition string to include " OR " easily ;-)
# Proceed to find using `:conditions => conditions` in your find
hope this helps =)
Sounds like you need a full text search. The best search integration right now is with Sphinx and the Thinking_Sphinx plugin. I have used it on several projects and it's super easy to setup.
You do need to install sphinx on your host so if you are using a shared host that could present some issues.
You could also use full text search in a MyISAM MySQL database, but performance on that is pretty poor.
Once you have your sphinx installed you just put what you want to index in your model and call model.search. The results will be a list of model objects. It supports will_paginate as well.
I'd suggest looking at Searchlogic if you don't want to use a separate fulltext search engine (Ferret, Sphinx, etc). It makes simple searches extremely easy, although you may not want to use it in a public facing area without lots of testing.
Also check out the Railscast on it: http://railscasts.com/episodes/176-searchlogic
1.You can do some coding in your controller post as such:-
<pre>
def show
#post = Post.find(params[:id])
#tag_counts = Tag.count(:group => :name, :order => 'updated_at DESC', :limit => 10)
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
</pre>
2.Now make some changes in your view file:-
<pre>
<b>Tags:</b>
<%= join_tags(#post) %>
<%unless #tag_counts.nil?%>
<% #tag_counts.each do |tag_name, tag_count| %>
<tr><td><%= link_to(tag_name, posts_path(:name => tag_name)) %></td>
<td>(<%=tag_count%>)</td>
</tr><% end %>
<%end%>
</pre>
3. And one important thing is that there should be many to many relationship between tags and post.