Quick Tip: Search for null dates in Thinking Sphinx »
Created at: 17.10.2009 17:35, source: Hackido, tagged: rails thinking-sphinx sphinx
In one of my projects, when a record is deleted I set a deleted_at field with the timestamp rather than actually destroying it. Because that model is indexed by Thinking Sphinx I want to make sure that it doesn't then show up in any search results (the user says she deleted it, right?) I had thought the way to do that would be as simple as passing an extra condition to my query, but I was wrong.
Instead, what you have to do is create an attribute in your model for the datetime field in question and use :with => to filter it down. It might look like this. # Search Index.
define_index do
indexes :name, :sortable => true
indexes :description, :sortable => true
has :deleted_at
set_property :delta => true
end
In the above, I'm not indexing deleted_at, rather it's set as an attribute. When I do my search, I can then use it as follows:Foo.search(params[:search], :with => {:deleted_at => 0})
Now why did I use 0 instead of nil? Because Sphinx equates nil values with 0.. you'll find out about that if you ever decide to sort any integers that involve null values..
One other thing I want to mention. In my case, I allow admins to search for all the records, including the deleted ones. If I never wanted those records even indexed I would instead add a where clause to my index itself. ('where "deleted_at IS NOT NULL"') This way I wouldn't have to specify the (:with =>) in my controller.
more »
Quick Tip: Search Email in Thinking Sphinx »
Created at: 06.06.2009 19:24, source: Hackido, tagged: rails thinking-sphinx sphinx
If you use Sphinx as your search backend you may have noticed that it won't search for an entire email address. It's a bit perplexing at first since everything else works so well. After banging my head on the issue for a few hours, I got some direct help from Pat Allan, the creator of Thinking Sphinx. The problem is with the "@" symbol which is a reserved character in Sphinx. To get around that we need to modify Sphinx's allowable character set.
Now for better or for worse, you don't actually need to define any sort of config file by default to use Thinking Sphinx.. it's smart enough to pick sensible defaults. But in this case we'll have to go ahead and create one.
Every Ruby on Rails project comes with a config folder where many files you may need to modify are stored. In this directory create a file called sphinx.yml. It's a YAML file so be careful about spacing here. It's as sensitive as your database.yml file so if you accidentally alter the spacing things may not work.
For our example, let's use the UTF-8 character set. So in that sphinx.yml file, paste this content:development:
port: 3312
charset_table: "0..9, a..z, _, @, A..Z->a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F"
test:
port: 3313
charset_table: "0..9, a..z, _, @, A..Z->a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F"
production:
port: 3312
charset_table: "0..9, a..z, _, @, A..Z->a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F"
There are a number of other options you can put in your configuration file of course, but the charset_table one is going to be the one that helps you search email. In order to see the effect, you'll need to rebuild your index. If you've got a recent version of the Thinking Sphinx plugin:rake thinking_sphinx:rebuild
Otherwise, you'll need to stop, index, and then start:rake thinking_sphinx:stop
rake thinking_sphinx:index
rake thinking_sphinx:start
After that searching for emails should work!
more »
Sum and index a related column in Thinking Sphinx »
Created at: 04.06.2009 03:13, source: Hackido, tagged: rails thinking-sphinx sphinx
It's fairly straight forward to get indexing working using the excellent Thinking Sphinx plugin. But sometimes you may want to do something a little more complicated. For example, if you have a Model foo with a has_many relationship to model Bar and you want to search on Bar how do you set it up? Or, what if you want to find out how many Bars Foo has? Enough! Let's work on an example.
Let's start and try to index a User model. That user has many bank accounts each of which has a balance:
User 1
--> Account 1 ($5)
--> Account 2 ($100)
--> Account 3 ($3)
User 2
--> Account 4 ($5)
--> Account 5 ($75)
In our example, we want to search for users by the amount of cash they have in their accounts. To do that we need to configure sphinx to first tell it that a User has many Accounts with a bank balance:
class Brigade < ActiveRecord::Base
has_many :accounts
#sphinx index
define index do
has accounts(:bank_balance), :as => :bank_balance
indexes "SUM(bank_balance)", :as => :total_bank_balance, :sortable => true
end
end
With that done, you can now do a search for users with a bank balance of $5 and it will return the 2 users with that balance. But, you can also do a search for users with a total bank balance of $80 and it will return only user #2.
more »
Named Scopes and Thinking Sphinx »
Created at: 19.04.2009 01:18, source: Hackido, tagged: rails thinking-sphinx
I'm really not a fan of the Thinking Sphinx conditions syntax. It's different than what I'm used to using in ActiveRecord and has caused me some problems with dates and such. Instead I use named scopes whenever I can. For example, in Railfood, a site for locomotive parts, I use a named scope to show only products that are in stock. That causes some issues, not only because of all the nil records but because the default number of results returned is 20.
Note that this article is no longer that useful. Instead of named scopes please look at sphinx scopes. Article coming soon.
For example:@p =Product.current.search("injector").size
=> 20
or@p =Product.current.search("123").size
=> 20
One of the first gotchas (covered here too) is that if you actually inspect that array you'll see that it's filled with nil.[nil, nil, nil, nil nil,nil, nil, nil, nil nil,nil, nil, nil, nil nil,nil, nil, nil, nil nil,]
To get around it, call the compact method on the array:@p =Product.current.search("injector").compact.size
=> 0
The other gotcha here is that if you actually have 20000 records that match "injector" but only 1 of them is current, you'll get zero results. To work around that you need to increase the maximum amount of results per page:@p =Product.current.search("injector", :per_page => 20000).compact.size
=> 1
But what about pagination? Thinking Sphinx does give us this, but the fact that both the methods are called :per_page kind of screws things up. To work around this, just call paginate on your results:@p =Product.current.search("injector",
:per_page => 20000).paginate(:per_page => 8, :page => params[:page])
And that should do it. Of course, depending on how many results you have you may need to up the initial per_page from 20000 to something larger. This isn't ideal in my humble opinion, but it works for the moment. Other suggestions are welcome!
While we're on the subject of Thinking Sphinx, be sure to set :enable_star => 1 in your index block if you want to use wildcards in your searches.
more »
