From ef2887072e72f9d22083d5e33dcffebb204b1768 Mon Sep 17 00:00:00 2001 From: Ben Zenik Date: Fri, 9 Mar 2018 12:29:30 +0200 Subject: [PATCH] enable location search with Sphinx --- app/helpers/feature_flag_helper.rb | 2 +- app/indices/listing_index.rb | 2 + .../search/database_search_helper.rb | 9 +++-- .../search/sphinx_adapter.rb | 39 ++++++++++++++++++- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/app/helpers/feature_flag_helper.rb b/app/helpers/feature_flag_helper.rb index 7f40bb82c7..844d2d3f75 100644 --- a/app/helpers/feature_flag_helper.rb +++ b/app/helpers/feature_flag_helper.rb @@ -68,6 +68,6 @@ def search_engine end def location_search_available - search_engine == :zappy + true end end diff --git a/app/indices/listing_index.rb b/app/indices/listing_index.rb index 4dbfb6d7cf..038a0e1d27 100644 --- a/app/indices/listing_index.rb +++ b/app/indices/listing_index.rb @@ -26,6 +26,8 @@ has community_id has custom_dropdown_field_values.selected_options.id, :as => :custom_dropdown_field_options, :type => :integer, :multi => true has custom_checkbox_field_values.selected_options.id, :as => :custom_checkbox_field_options, :type => :integer, :multi => true + has 'RADIANS(locations.latitude)', as: :latitude, type: :float + has 'RADIANS(locations.longitude)', as: :longitude, type: :float set_property :enable_star => true diff --git a/app/services/listing_index_service/search/database_search_helper.rb b/app/services/listing_index_service/search/database_search_helper.rb index 3e068e8ddc..2ea83d72d1 100644 --- a/app/services/listing_index_service/search/database_search_helper.rb +++ b/app/services/listing_index_service/search/database_search_helper.rb @@ -2,9 +2,12 @@ module ListingIndexService::Search::DatabaseSearchHelper module_function - def success_result(count, listings, includes) - Result::Success.new( - {count: count, listings: listings.map { |l| ListingIndexService::Search::Converters.listing_hash(l, includes) }}) + def success_result(count, listings, includes, distances = {}) + converted_listings = listings.map do |listing| + distance_hash = distances[listing.id] || {} + ListingIndexService::Search::Converters.listing_hash(listing, includes, distance_hash) + end + Result::Success.new({count: count, listings: converted_listings}) end def fetch_from_db(community_id:, search:, included_models:, includes:) diff --git a/app/services/listing_index_service/search/sphinx_adapter.rb b/app/services/listing_index_service/search/sphinx_adapter.rb index acbe2b2c33..01e4bf3bf4 100644 --- a/app/services/listing_index_service/search/sphinx_adapter.rb +++ b/app/services/listing_index_service/search/sphinx_adapter.rb @@ -61,6 +61,8 @@ def search_with_sphinx(community_id:, search:, included_models:, includes:) DatabaseSearchHelper.success_result(0, [], nil) else + geo_search = parse_geo_search_params(search) + with = HashUtils.compact( { community_id: community_id, @@ -68,6 +70,7 @@ def search_with_sphinx(community_id:, search:, included_models:, includes:) listing_shape_id: search[:listing_shape_id], price_cents: search[:price_cents], listing_id: numeric_search_match_listing_ids, + geodist: geo_search[:distance_max] }) selection_groups = search[:fields].select { |v| v[:type] == :selection_group } @@ -88,12 +91,14 @@ def search_with_sphinx(community_id:, search:, included_models:, includes:) star: true, with: with, with_all: with_all, - order: 'sort_date DESC', + order: geo_search[:order] || 'sort_date DESC', + geo: geo_search[:origin], max_query_time: 1000 # Timeout and fail after 1s ) begin - DatabaseSearchHelper.success_result(models.total_entries, models, includes) + distances_hash = geo_search[:origin] ? collect_geo_distances(models, search[:distance_unit]) : {} + DatabaseSearchHelper.success_result(models.total_entries, models, includes, distances_hash) rescue ThinkingSphinx::SphinxError => e Result::Error.new(e) end @@ -113,5 +118,35 @@ def selection_groups(groups) groups[:values] end end + + DISTANCE_UNIT_FACTORS = { miles: 1609.0, km: 1000.0 } + + def parse_geo_search_params(search) + return {} unless search[:latitude].present? && search[:longitude].present? + + geo_params = { + order: (search[:sort] == :distance ? 'geodist ASC' : nil), + origin: [radians(search[:latitude]), radians(search[:longitude])] + } + + if search[:distance_max].present? + max_distance_meters = search[:distance_max] * DISTANCE_UNIT_FACTORS[search[:distance_unit]] + geo_params[:distance_max] = 0..max_distance_meters + end + + geo_params + end + + def radians(degrees) + degrees * Math::PI / 180 + end + + def collect_geo_distances(models, geo_unit) + models.each_with_object({}) do |listing, result| + # get distance from ThinkingSphinx::Search::Glaze / DistancePane + distance = listing.distance / DISTANCE_UNIT_FACTORS[geo_unit] + result[listing.id] = { distance_unit: geo_unit, distance: distance } + end + end end end