Address N+1 queries in Place::FetchData and skip locationless points

This commit is contained in:
Eugene Burmakin 2025-05-17 19:14:28 +02:00
parent c681fdbdb6
commit abd4325891
6 changed files with 22 additions and 19 deletions

View file

@ -9,6 +9,7 @@ class Overland::BatchCreatingJob < ApplicationJob
data = Overland::Params.new(params).call
data.each do |location|
next if location[:lonlat].nil?
next if point_exists?(location, user_id)
Point.create!(location.merge(user_id:))

View file

@ -18,6 +18,7 @@ class Geojson::Importer
data = Geojson::Params.new(json).call
data.each.with_index(1) do |point, index|
next if point[:lonlat].nil?
next if point_exists?(point, user_id)
Point.create!(point.merge(user_id:, import_id: import.id))

View file

@ -21,15 +21,18 @@ class ReverseGeocoding::Places::FetchData
first_place = places.shift
update_place(first_place)
# Extract all osm_ids for preloading
osm_ids = places.map { |place| place.data['properties']['osm_id'].to_s }
# Preload all existing places with these osm_ids in a single query
existing_places = Place.where("geodata->'properties'->>'osm_id' IN (?)", osm_ids)
.index_by { |p| p.geodata.dig('properties', 'osm_id').to_s }
return if osm_ids.empty?
# Process with preloaded data
places.each { |reverse_geocoded_place| fetch_and_create_place(reverse_geocoded_place, existing_places) }
existing_places =
Place.where("geodata->'properties'->>'osm_id' IN (?)", osm_ids)
.index_by { |p| p.geodata.dig('properties', 'osm_id').to_s }
.compact
places.each do |reverse_geocoded_place|
fetch_and_create_place(reverse_geocoded_place, existing_places)
end
end
private
@ -50,7 +53,7 @@ class ReverseGeocoding::Places::FetchData
)
end
def fetch_and_create_place(reverse_geocoded_place, existing_places = nil)
def fetch_and_create_place(reverse_geocoded_place, existing_places)
data = reverse_geocoded_place.data
new_place = find_place(data, existing_places)
@ -66,19 +69,14 @@ class ReverseGeocoding::Places::FetchData
new_place.save!
end
def find_place(place_data, existing_places = nil)
def find_place(place_data, existing_places)
osm_id = place_data['properties']['osm_id'].to_s
# Use the preloaded data if available
if existing_places
return existing_places[osm_id] if existing_places[osm_id].present?
else
# Fall back to individual query if no preloaded data
found_place = Place.where("geodata->'properties'->>'osm_id' = ?", osm_id).first
return found_place if found_place.present?
end
existing_place = existing_places[osm_id]
return existing_place if existing_place.present?
Place.find_or_initialize_by(
# If not found in existing places, initialize a new one
Place.new(
lonlat: "POINT(#{place_data['geometry']['coordinates'][0].to_f.round(5)} #{place_data['geometry']['coordinates'][1].to_f.round(5)})",
latitude: place_data['geometry']['coordinates'][1].to_f.round(5),
longitude: place_data['geometry']['coordinates'][0].to_f.round(5)

View file

@ -6,6 +6,8 @@ class ReverseGeocoding::Points::FetchData
def initialize(point_id)
@point = Point.find(point_id)
rescue ActiveRecord::RecordNotFound => e
ExceptionReporter.call(e)
Rails.logger.error("Point with id #{point_id} not found: #{e.message}")
end

View file

@ -5,8 +5,8 @@
data-controller="trips"
data-trips-target="container"
data-distance_unit="<%= DISTANCE_UNIT %>"
data-api_key="<%= current_user.api_key %>"
data-user_settings="<%= current_user.settings.to_json %>"
data-api_key="<%= trip.user.api_key %>"
data-user_settings="<%= trip.user.settings.to_json %>"
data-path="<%= trip.path.coordinates.to_json %>"
data-started_at="<%= trip.started_at %>"
data-ended_at="<%= trip.ended_at %>"

View file

@ -7,4 +7,5 @@ Sentry.init do |config|
config.dsn = SENTRY_DSN
config.traces_sample_rate = 1.0
config.profiles_sample_rate = 1.0
config.enable_logs = true
end