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.
Related
I have two models with both have :phone column,
I'm trying to query Leads who don't have :phone present in User's Records
So if lead has 3 records with unique phones
Lead.last(3)
#<Lead>
{
:id => 1
:phone => "9898989898"
},
#<Lead>
{
:id => 2
:phone => "1212121212"
},
#<Lead>
{
:id => 3
:phone => "3434343434"
}
and user has same :phone present
#<User>
{
:id => 95
:phone => "3434343434"
}
the record phone present in user's table should not come in query of Lead records
Please note that i don't want to add any relations or associations on this two Models.
Any help/suggestions appreciate.
I would go with:
Lead.where.not(phone: User.select(:phone))
I am creating a Rails project which requires a country table. I also like to include the country calling codes into them. I found help in creating the country table from GitHub and it looks like this:
class CreateCountries < ActiveRecord::Migration
def change
create_table :countries do |t|
t.string :name
t.string :printable_name
t.string :iso2, :size => 2
t.string :iso3, :size => 3
t.integer :numcode
t.timestamps
end
end
Country.reset_column_information
Country.create(:iso2 => 'AF', :name => 'AFGHANISTAN', :printable_name => 'Afghanistan', :iso3 => 'AFG', :numcode => '004')
Country.create(:iso2 => 'AL', :name => 'ALBANIA', :printable_name => 'Albania', :iso3 => 'ALB', :numcode => '008')
Country.create(:iso2 => 'DZ', :name => 'ALGERIA', :printable_name => 'Algeria', :iso3 => 'DZA', :numcode => '012')
and then I've also found a list of country calling codes and managed to put them in a spreadsheet:
Afghanistan 93
Albania 355
Algeria 213
What I'd like to a fast way to join the calling code into the above list so it looks like this:
Country.create(:iso2 => 'AF', :name => 'AFGHANISTAN', :printable_name => 'Afghanistan', :iso3 => 'AFG', :numcode => '004' :call_code => 93)
Any fast solution to achieve this using Excel or OpenOffice spreadsheet or MySQL?
Just as long as I don't have to key it in manually.
Copy your Country.create lines of code into Excel, say starting in A1.
Copy your list of codes into Excel and if necessary split into two
columns (eg with Text to Columns).
Name the result (say CArray).
In B1 put: =FIND("printable_name => '",A1)+19.
In C1 put:
=SUBSTITUTE(A1,")"," :call_code => "&VLOOKUP(MID(A1,B1,FIND("'",A1,B1)-B1),cArray,2,0)&")")
Copy B1:C1 down to suit.
Copy as much of ColumnC back in to your code as suitable.
cArray need not be in the same sheet.
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.
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
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