Improve performance sorting SDG records
We noticed there was a performance issue while browsing the SDG Management section and when one of our tests started failing sometimes because the request to the relations#index controller took too long. The issue proved to be `SDG::Target#<=>`. This method calls `.goal` for each target, meaning we were generating 169 database queries when sorting all targets. So we're comparing codes directly to minimize the number of database queries and improve performance. Requests to the relations index take now less than third of the time they used to take.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
module SDG::Related
|
||||
extend ActiveSupport::Concern
|
||||
include Comparable
|
||||
|
||||
RELATABLE_TYPES = %w[
|
||||
Budget::Investment
|
||||
@@ -23,4 +24,20 @@ module SDG::Related
|
||||
def relatables
|
||||
relations.map(&:relatable)
|
||||
end
|
||||
|
||||
def <=>(goal_or_target)
|
||||
if goal_or_target.class.ancestors.include?(SDG::Related)
|
||||
subcodes <=> goal_or_target.subcodes
|
||||
end
|
||||
end
|
||||
|
||||
def subcodes
|
||||
code.to_s.split(".").map do |subcode|
|
||||
if subcode.to_i.positive?
|
||||
subcode.to_i
|
||||
else
|
||||
subcode.to_i(36) * 1000
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
class SDG::Goal < ApplicationRecord
|
||||
include Comparable
|
||||
include SDG::Related
|
||||
|
||||
validates :code, presence: true, uniqueness: true, inclusion: { in: 1..17 }
|
||||
@@ -19,14 +18,6 @@ class SDG::Goal < ApplicationRecord
|
||||
I18n.t("sdg.goals.goal_#{code}.description")
|
||||
end
|
||||
|
||||
def <=>(goal_or_target)
|
||||
if goal_or_target.class == self.class
|
||||
code <=> goal_or_target.code
|
||||
elsif goal_or_target.respond_to?(:goal)
|
||||
[self, -1] <=> [goal_or_target.goal, 1]
|
||||
end
|
||||
end
|
||||
|
||||
def self.[](code)
|
||||
find_by!(code: code)
|
||||
end
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
class SDG::LocalTarget < ApplicationRecord
|
||||
include Comparable
|
||||
include SDG::Related
|
||||
|
||||
translates :title, touch: true
|
||||
@@ -23,26 +22,8 @@ class SDG::LocalTarget < ApplicationRecord
|
||||
find_by!(code: code)
|
||||
end
|
||||
|
||||
def <=>(goal_or_target)
|
||||
if goal_or_target.class == self.class
|
||||
[target, numeric_subcode] <=> [goal_or_target.target, goal_or_target.numeric_subcode]
|
||||
elsif [target.class, goal.class].include?(goal_or_target.class)
|
||||
-1 * (goal_or_target <=> self)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def numeric_subcode
|
||||
subcode.to_i
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def subcode
|
||||
code.split(".").last
|
||||
end
|
||||
|
||||
def set_related_goal
|
||||
self.goal ||= target&.goal
|
||||
end
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
class SDG::Target < ApplicationRecord
|
||||
include Comparable
|
||||
include SDG::Related
|
||||
|
||||
validates :code, presence: true, uniqueness: true
|
||||
@@ -12,37 +11,13 @@ class SDG::Target < ApplicationRecord
|
||||
I18n.t("sdg.goals.goal_#{goal.code}.targets.target_#{code_key}.title")
|
||||
end
|
||||
|
||||
def <=>(goal_or_target)
|
||||
if goal_or_target.class == self.class
|
||||
[goal.code, numeric_subcode] <=> [goal_or_target.goal.code, goal_or_target.numeric_subcode]
|
||||
elsif goal_or_target.class == goal.class
|
||||
-1 * (goal_or_target <=> self)
|
||||
elsif goal_or_target.class.name == "SDG::LocalTarget"
|
||||
[self, -1] <=> [goal_or_target.target, 1]
|
||||
end
|
||||
end
|
||||
|
||||
def self.[](code)
|
||||
find_by!(code: code)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def numeric_subcode
|
||||
if subcode.to_i > 0
|
||||
subcode.to_i
|
||||
else
|
||||
subcode.to_i(36) * 1000
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def code_key
|
||||
code.gsub(".", "_").upcase
|
||||
end
|
||||
|
||||
def subcode
|
||||
code.split(".").last
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user