Thinking Sphinx - sort across multiple models - ruby-on-rails

To display a directory, I'm doing a search to get lots of items across various models, that all begin with a particular letter like this
#everything = ThinkingSphinx.search(
"#name ^?",
#letter,
:match_mode => :extended,
:classes => [Performer, Promoter, Tour, Venue, User],
:order => :name_sort,
:sort_mode => :asc
)
However the items are not sorted alphabetically. If I change asc to desc, the order changes but it's still in no discernable pattern.
The indexes are set up like this
performer.rb
define_index do
indexes name, :sortable => true
indexes description
has created_at, updated_at
end
promoter.rb
define_index do
indexes name, :sortable => true
indexes description
has created_at, updated_at
end
tour.rb
define_index do
indexes name, :sortable => true
indexes description
has created_at, updated_at
end
venue.rb
define_index do
indexes name, :sortable => true
indexes description
has created_at, updated_at
end
user.rb
define_index do
indexes first_name, :sortable => true
indexes last_name, :as => :name, :sortable => true
indexes bio
has role, created_at, updated_at
end
As you can see the user index aliases last_name as name, but that's the only oddity.
I'm using thinking sphinx 2.0.12

Related

Using crc32 tweak on has_many relations in Thinking Sphinx

It's weird actually. I have two models that have has_many relation each other, here are my models
#model city
class City < ActiveRecord::Base
belong_to :state
end
#model state
class State < ActiveRecord::Base
has_many :city
end
and I have state index
ThinkingSphinx::Index.define 'state', :with => :active_record do
indexes state_name, :sortable => true
#here is the problem
has "CRC32(cities.city_name)", :as => :city_name, :type => :integer
end
I want to use city_name as a filter. My code above doesn't work and i got an error message when run
rake ts:index
here is the error message
ERROR: index 'state_core': sql_range_query: Unknown column 'cities.city_name' in 'field list'
but, when i put city_name in indexes block like below, the indexer runs well!
ThinkingSphinx::Index.define 'state', :with => :active_record do
indexes state_name, :sortable => true
indexes cities.city_name
has "CRC32(cities.city_name)", :as => :city_name, :type => :integer
end
any suggestions ?
Thinking Sphinx can't tell if you're referring to association tables within SQL snippets - so in your first example, there's nothing indicating that it needs to join on cities.
The join method within an index definition exists for this very purpose - so, try the following:
ThinkingSphinx::Index.define 'state', :with => :active_record do
indexes state_name, :sortable => true
has "CRC32(cities.city_name)", :as => :city_name, :type => :integer
join cities
end
However, it's worth noting a few things: firstly, you may also need to add cities.city_name to the GROUP BY clause, since it's not part of any aggregate values:
# within index definition
group_by 'cities.city_name
But also: your State model has many cities, not just one, so it should actually be aggregated into a set of integer values, not just one. This means you don't need the group_by call, but you do need to add the aggregate behaviour yourself. This is done differently depending on whether you're using PostgreSQL or MySQL:
# PostgreSQL
has "array_to_string(array_agg(crc32(cities.name)), ',')",
:as => :city_names, :type => :integer, :multi => true
# MySQL
has "GROUP_CONCAT(CRC32(cities.name) SEPARATOR ',')",
:as => :city_names, :type => :integer, :multi => true
CRC32 is not a native function in PostgreSQL, and so you may need to add it yourself. Thinking Sphinx prior to v3 did this for you, but I've rewritten it so the CRC32 function is no longer required. This is largely due to the fact that CRC32 can result in collisions, and it can't be reversed, and so it's an inelegant and imperfect solution. Hence, I think using fields for string comparison is better, but it's up to you for whether this is preferred in your app.
I would recommend this approach instead:
ThinkingSphinx::Index.define :state, :with => :active_record do
indexes state_name, :sortable => true
has cities.id, :as => :city_ids
end
city = City.find_by_name('Melbourne')
State.search :with => {:city_ids => city.id}
It's accurate and elegant.

tag synonyms in rails using thinking sphinx

What is a proper way to handle tag synonyms in rails?
Model is called Situation, I use acts_as_taggable_on for tags and ThinkingSphinx for search.
Situation.search :conditions => { :tag_name => '(synonym11 | synonym12) | (synonym21 | synonym22)' }, :match_mode => :boolean
but with proper ranking
something like that helps
define_index :main_index do
# fields
indexes :title
indexes :description
indexes user.name, :as => :author
# attributes
has id, user_id, created_at, updated_at
end
define_index :tag_index do
# fields
indexes taggings.tag.name, :as => :tag_name
# attributes
has id, user_id, created_at, updated_at
set_property :wordforms => 'db/synonyms.txt'
end

Rails, Sphinx (thinking_sphinx) and sorting from associations

I have 3 models:
Stats (belongs_to :item)
t.integer :skin_id
t.integer :item_id
t.integer :rating
Item (has_many :stats)
and
Skin (has_many :stats)
Using thinking_sphinx i want to create a separate index, for items, sorted by :rating for particular :skin_id
So, i'm trying to:
define_index 'sort_by_rate' do
indexes stats(:rating), :as => :ratings, :sortable => true
end
But this, will generate an index for all :skin_id (in the Stat model), not for particular one.
In other words, i need to gather all items, sorted by Stat.rating, with Stat.skin_id == 1 (for example).
Here is the example of SQL:
"SELECT `stats`.* FROM `stats` INNER JOIN `items` ON `items`.`id` = `stats`.`item_id` WHERE `stats`.`skin_id` = 1 ORDER BY rating DESC"
ANy solutions is very appreciated!
Perhaps you should have in your define_index block:
has :skin_id
and then, when searching, filter by that. Something like this:
Item.search(:with => {:skin_id => skin.id}, :order_by => 'ratings ASC')

Thinking Sphinx - Showing the right record from the association

I have successfully got Thinking Sphinx working with Geolocation on an associated model. Happy days!
But I now need it to show the right associated record on a Google map.
The scenario is a Company with has_many offices. Offices have got the lng,lat values. I am searching on the Company and associating the offices to it.
E.g.
define_index do
indexes :name, :sortable => true
indexes offices(:city), :as => :city
indexes offices(:postal_code), :as => :postal_code
has "RADIANS(offices.lat)", :as => :lat, :type => :float
has "RADIANS(offices.lng)", :as => :lng, :type => :float
has created_at
has updated_at
set_property :latitude_attr => 'lat'
set_property :longitude_attr => 'lng'
set_property :field_weights => { 'name' => 10,
'service_name' => 9,
'city' => 8 }
end
Searching for x company in y location / postcode works perfectly, showing the correct companies that have got offices in the desired location within the #geodist radius.
E.g.
{:geo=>[0.9283660690549609, -0.050527407508941975], :sort_mode=>:expr, :sort_by=>"#weight * #weight / #geodist", :with=>{"#geodist"=>0.0..120700.8}, :conditions=>{:service_name=>"Business strategies"}, :page=>1, :per_page=>12, :star=>true}
The resulting records are company object, not the offices, which is fine for the list view but I want to show icons on a google map of the relevant associated office.
What is the best way to find the relevant associated office record to show within the radius bounds?
Sphinx only reliably handles single-value float attributes - and it has no concept of paired lat/lng values. This means that you can't have a solid search across objects with more than one lat/lng pair.
The best workaround is to actually search on Office instead - and perhaps pull in each office's company information:
define_index do
indexes company.name, :as => :name, :sortable => true
indexes city, postal_code
has "RADIANS(offices.lat)", :as => :lat, :type => :float
has "RADIANS(offices.lng)", :as => :lng, :type => :float
has company.created_at, :as => :created_at
has company.updated_at, :as => :updated_at
has company_id
set_property :field_weights => {
'name' => 10,
'service_name' => 9,
'city' => 8
}
end
And then when searching, you can group by company_id to ensure only one result for any company (if that's what you'd prefer):
Office.search 'foo',
:geo => [lat, lng],
:with => {'#geodist' => 0.0..120700.8}
:group_function => :attr
:group_by => 'company_id'
If it's important as to which Office gets returned for a given company_id, then you'll probably want to use the :group_clause option as well. The docs cover this.

Thinking Sphinx, associations are not working

I have a model:
class Topic < ActiveRecord::Base
define_index do
indexes title, :sortable => true
indexes body
indexes tags(:name), :as => :tag_name
end
has_and_belongs_to_many :tags, :join_table => 'topic_tags', :order => 'tags.name asc'
end
When I run:
rake ts:rebuild
I get:
sql_range_query: Unknown column 'topics.name' in 'field list'
And my 'config/development.sphinx.conf' has this oddness:
sql_query = SELECT `topics`.`id` * 1 + 0 AS `id` , CAST(`topics`.`title` AS CHAR) AS
`title`, CAST(`topics`.`body` AS CHAR) AS `body`, CAST(`topics`.`name` AS CHAR) AS
`tag_name`, `topics`.`id` AS `sphinx_internal_id`, 1552019743 AS `class_crc`, '1552019743'
AS `subclass_crcs`, 0 AS `sphinx_deleted`, IFNULL(`topics`.`title`, '') AS `title_sort`
FROM `topics` WHERE `topics`.`id` >= $start AND `topics`.`id` <= $end GROUP BY
`topics`.`id` ORDER BY NULL
sql_query_range = SELECT IFNULL(MIN(`id`), 1), IFNULL(MAX(`id`), 1) FROM `topics`
So for some reason associations look bust, where have I gone wrong and how do I fix this?
(running rails 2.3.4 and latest thinking sphinx 1.2.11)
Trivial trap:
This works:
class Topic < ActiveRecord::Base
has_and_belongs_to_many :tags, :join_table => 'topic_tags', :order => 'tags.name asc'
define_index do
indexes title, :sortable => true
indexes body
indexes tags(:name), :as => :tag_name
end
end
associations must be defined prior to the index.

Resources