From 62f501be2eb64d5c9546264de85f59107fa79fe4 Mon Sep 17 00:00:00 2001 From: Codex Night Worker Date: Thu, 12 Nov 2026 18:55:43 +0900 Subject: [PATCH] AI: address review on PR #461 --- backend/app/services/post_tag_search.rb | 33 +++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/backend/app/services/post_tag_search.rb b/backend/app/services/post_tag_search.rb index 0eed657..1e4b9dd 100644 --- a/backend/app/services/post_tag_search.rb +++ b/backend/app/services/post_tag_search.rb @@ -3,12 +3,12 @@ class PostTagSearch def self.literals(locale, raw) tokens = raw.split.reject { _1.downcase == 'type:or' } names = tokens.map { _1.sub(/\Anot:/i, '') } - registered = TagName.where(language_code: locale.language_code, name: names) - .pluck(:name, :tag_id).to_h.transform_keys(&:downcase) + registered = matching_names(TagName.where(language_code: locale.language_code), + names, 'tag_names.name', :tag_id).to_h primary = TagName.where(language_code: locale.language_code, tag_id: registered.values, primary_flg: true).pluck(:tag_id, :name).to_h tokens.zip(names).map do |token, name| - { name: primary[registered[name.downcase]] || name, + { name: primary[registered[name]] || name, negative: token.downcase.start_with?('not:') } end end @@ -19,12 +19,25 @@ class PostTagSearch def self.references_for(locale, literals) names = literals.map { _1[:name] }.uniq - internal = TagName.where(language_code: locale.language_code, name: names) - .pluck(:name, :tag_id).group_by { _1.first.downcase } - external = names.empty? ? {} : ExternalTag.where( - "CONCAT(platform, ':', name) IN (?)", names) - .pluck(Arel.sql("CONCAT(platform, ':', name)"), :id).group_by { _1.first.downcase } - names.to_h { |name| [name, [internal.fetch(name.downcase, []).map(&:last).uniq, - external.fetch(name.downcase, []).map(&:last).uniq]] } + internal = matching_names(TagName.where(language_code: locale.language_code), + names, 'tag_names.name', :tag_id).group_by(&:first) + external = matching_names(ExternalTag.all, names, + "CONCAT(external_tags.platform, ':', external_tags.name)", :id).group_by(&:first) + names.to_h { |name| [name, [internal.fetch(name, []).map(&:last).uniq, + external.fetch(name, []).map(&:last).uniq]] } end + + def self.matching_names(scope, names, expression, identifier) + return [] if names.empty? + + queries = names.uniq.map do |name| + scope.klass.sanitize_sql_array(['SELECT ? AS name', name]) + end + # Return the input spelling so Ruby does not recheck database equivalence. + scope.joins(<<~SQL).pluck(Arel.sql('search_names.name'), identifier) + INNER JOIN (#{ queries.join(' UNION ALL ') }) AS search_names + ON #{ expression } = search_names.name COLLATE utf8mb4_0900_ai_ci + SQL + end + private_class_method :matching_names end