How to implement search like Stack Overflow - ruby-on-rails

I have implemented the full text search using Sphinx and Thinking Sphinx.
I want to add column wise search. Some thing like:-(taking an example of Stack Overflow)
Suppose you want to see actvities related to you, just type:
user:me
Then it will return a result with all the questions and answers related to piemesons.
If you type
votes:15
then it will return a result with all the questions tagged with having more than 15 votes.
And if you type
user:me votes:15
then it will return all the questions and answers belonging to you with more than 15 votes.
How can I implement this thing?
Right now my search results are based upon full text search. How can these kinds of features be included?
Any options avaliable in Sphinx or Solr or any other search engines?

:with option in thinking sphinx.
First of all, you have to define those attributes in your index definition (check out attributes section here).
has views_count, :as => :views, :type => :integer
has user.id, :as => :user, :type => :integer
Then you could search for posts like this:
Post.search '', :with => {:views => 12..maxint, :user => User.first.id}
(I am not sure if there is any more elegant possibility of giving open ranges, but 12..max_int should be enough)
Two important things:
if you want to count related objects (e.g. responses), you should use counter cache
if your "user" is polymorphic association, I recommend "CRC32(CONCAT(user_type, user_id))" instead of user.id

Related

Creating a multiple choice test on Rails using a hash table

I'm in a tech summer camp and our teacher is showing us how to use Ruby on Rails. I told my teacher want I wanted to make and he told us to post here because it's important to learn from other developers.
I was looking on the site and found this question that is similar to what I want to do for my project. I want my friends to be able to read a few pages of text and then do a short test to make sure they were paying attention.
I want to make a hash table like the question I linked above but I don't know where to put it in my app! I would also like to tie it to users somehow so I can see which of my friends completed the test or not.
So far I have made user, question and answer models and would like to use a hash table like this test my fellow students.
# Question:
{ :question_id => 1,
:text => 'What is Minecraft?',
:answers => # Answers:
[{:answer_id => 1, :text => 'A game'},
{:answer_id => 2, :text => 'A food'},
{:answer_id => 3, :text => 'A store'} ],
:correct_answer_id => 1 }
Can someone tell me where I need to put this in my app and how I can save it to a database? My teacher said it is okay if people tell us how to do stuff as long as we tell him in our write up.
If you have related your answers with a has_many relationship to your question you can create answers with questions like this:
question = Question.create!(text => 'What is Minecraft?')
question.answer.create!(:text => 'A game')
question.answer.create!(:text => 'A food')
...
Ids will usually given out by the database, just don't need to define them yourself.

Need to find all "Clubs" where column "userlist" contains "current_user.id" [duplicate]

This question already has answers here:
Searching serialized data, using active record
(10 answers)
Closed 9 years ago.
I need to find all "Clubs" where column "userlist" (which contians an array of user ids) contains "current_user.id".
Here is the line of code in the controller that I am trying to use to accomplish this:
#userclubs = Club.where('userlist in (?)', current_user.id).all
I end up with an empty array: []
This is the code I am using in the view:
<%= f.association :clubs, :collection => #userclubs, :label_method => :name, :value_method => :name, :as => :check_boxes, :item_wrapper_class => 'inline' %>
If I change the controller code to #userclubs = Club.all I get all of the clubs as check boxes, but I only want the ones that the user belongs to to show.
I am probably going about this all wrong, but I am new to RoR and can't seem to find the proper way to do it.
The where method is thin layer over SQL. Your attribute is only an array in the eyes of Rails. From the database perspective, userlist is a text field and you can only search it as a text field.
While searching serialized structures is generally not practical, as discussed in Searching serialized data, using active record, in the case of flat array, you could do a regex search of the column looking for user_id in question, taking care to insure that the user_id is surrounded by the appropriate separate characters, something like:
userlist rlike ...
where ... is the constructed regex including ? for the value of current_user.id surrounded by the appropriate separate characters.
Since you're using Postgres, you could change the column datatype to be a native array, but that would only be a marginal improvement, as described in http://www.postgresql.org/docs/9.1/static/arrays.html under "Searching Arrays".
Unless you have an extraordinary situation, it's best to store relationships like this use the Rails association mechanism.

Thinking Sphinx search using 'with' and indexed attributes

UPDATE:
Ok so I am no longer getting an error, but I am not getting any results back (even when there is only one search option).
I added the has clause to my list of indexes:
define_index do
has bar_profiles(:day), :as => :days
indexes bar_profiles.budget, :as => :budget_tags
.
.
end
So my search is:
bars = Bar.search(search_options)
with
search_options = {:conditions=>{:budget_tags=>"LOW BUDGET"}, :with=>{:days=>"thursday"}, :page=>1, :per_page=>20}
bar_profiles has rows for budget, experience, tags, day, etc.
Bar has many bar_profiles (potentially 1 for each day)
What I am trying to do is use the thinking sphinx search (in the bar model) to match the users selected criteria for budget, experience, tags against the bar_profile that has the day that matches with "today" (the current day).
This is the last thing I have to do to finish this app and I'm pulling my hair out cause I can't find any examples of how to set this up right...
If you have any insight please post it, anything helps. Thanks.
At first I thought my question was similar to this with an extra layer of abstraction, but I think my problem is with the search options not the indexing.
First off let me state that I have been having the worse time trying to fix a previous' groups implementation using thinking sphinx. I have finally got the project 90% working and the last 10% deals with being able to get the right filters to check against.
Here is a brief overview. The application has bars and bar_profiles (amongst many other tables that connect to these 2, and users, but they are not necessary to understand this issue.) There can be a bar_profile for each day of the week, for each bar.
So in the bar_profile model there is:
belongs_to :bar
and in bar there is:
has_many :bar_profiles
followed by the indexes in bar (written by the previous developer):
define_index do
# name is a reserved keyword so we need to specify it as a symbol to work
indexes :name
indexes tags
indexes bar_profiles.day, :as => :day
indexes bar_profiles.tags, :as => :daily_tags
indexes bar_profiles.budget, :as => :budget_tags
indexes bar_profiles.experience, :as => :experience_tags
set_property :delta => true
end
The issue I am having is this current implementation does not constrain the search properly to the current day. Instead of checking the current days profile for the bar, it seems to be checking against ALL the bars profiles.
So I set the current day at the start of the method:
today = (Time.now + Time.zone_offset('EST')).strftime("%A")
Then I think it needs to be something like below. I referenced this post by pat about using 'with', but I am not sure if I am messing up the syntax (because I am getting an error):
search_options = {:conditions => {}, :with => {:day=>today}, :page => 1, :per_page => algorithm.results_per_page}
Then I use these search options:
search_options[:conditions][:experience_tags] = options[:experience] unless options[:experience].blank?
budget = combine_budgets(options[:budget])
search_options[:conditions][:budget_tags] = budget unless budget.blank?
But when I try to run the search I get this in my development log:
^^^^ ERROR! Reason: index bar_core,bar_delta: no such filter attribute 'day'
Now I am pretty confused by this since the index for :day was set up as shown above... I'm not sure if 'filter attribute' is different then an index attribute. If someone could please offer some insight into this it would be greatly appreciated (looking at you #pat).
This is the final issue in this app, so if anyone can help me I would be very grateful.
Thanks,
Alan

Searching with thinking_sphinx and filtering results

I have this scenario where I thought it would be pretty basic, but found out that I can't really achieve what I need. This is why I have this question for a thinking_sphinx's expert.
The scenario is this: I need do a search within a list of companies and only return those who has an address (there can be many address by company) which belongs to a particular city or none at all (this I can do).
I have the following models :
class Company < ActiveRecord::Base
has_many :company_addresses
define_index
indexes :name
indexes :description
indexes :keywords
end
end
and
class CompanyAddress < ActiveRecord::Base
end
The CompanyAddress has a city_id property. Without looping through all returned records from a sphinx search, is there a way to achieve the same thing more easily?
I'm using Rails 3.0.3 and thinking_sphinx.
You'll want to add an attribute pointing to the city_id values for the company:
has company_addresses.city_id, :as => :city_ids
And then you can filter on Companies belonging to a specific city:
Company.search 'foo', :with => {:city_ids => #city.id}
If you want both matching to a specific city or has no cities, that's a little trickier, as OR logic for attribute filters is more than a little tricky at best. Ideally what you want is a single attribute that contains either 0, or all city ids. Doing this depends on your database, as MySQL and Postgres functions vary.
As a rough idea, though - this might work in MySQL:
has "IF(COUNT(city_id) = 0, '0', GROUP_CONCAT(city_id SEPARATOR ',')",
:as => :city_ids, :type => :multi
Postgres is reasonably similar, though you may need to use a CASE statement instead of IF, and you'll definitely want to use a couple of functions for the group concatenation:
array_to_string(array_accum(city_id, '0')), ',')
(array_accum is provided by Thinking Sphinx, as there was no direct equivalent of GROUP_CONCAT in PostgreSQL).
Anyway, if you need this approach, and get the SQL all figured out, then your query looks something like:
Company.search 'foo', :with => {:city_ids => [0, #city.id]}
This will match on either 0 (representing no cities), or the specific city.
Finally: if you don't reference the company_addresses association anywhere in your normal fields and attributes, you'll need to force to join in your define_index:
join company_addresses
Hopefully that provides enough clues - feel free to continue the discussion here or on the Google Group.

Searching multiple columns with Sphinx

I have model Products with columns:
name, number, description
Index defined for this model looks like this:
define_index do
indexes :name, :sortable => true
indexes :number
indexes :description
where "amount > 0"
has :price
end
Since in description can be lots of random words I want to exclude it from searching sometimes (when user clicks chceckbox 'don't search in descriptions').
I went to the sphinx page and found following:
#(name, number) *pencil* *123*
And it seems like I don't understand how sphinx works. When I execute search
*pencil* *123*
word 'pencil' is found in name and '123' is found in number and I get 1 result. But when I execute
#(name, number) *pencil* *123*
no results are found.
Is searching by columns somehow different?
You can only search on fields when using the :extended match mode - Thinking Sphinx sets this automatically if you use :conditions - but you're constructing a multi-field query yourself, hence why this isn't happening. Try this:
Product.search "#(name, number) *pencil* *123*", :match_mode => :extended
Hope this helps.
It was all about spaces :/
This works:
#(name,number) *pencil* *123*

Resources