autocomplete: recognize Japanese tags in autocomplete.

Allowing typing Japanese tags in autocomplete. For example, typing 東方
in autocomplete will be completed to the touhou tag. Typing ぶくぶ will
complete to the bkub tag.

This works using wiki page and artist other names. Effectively, any name
listed as an other name in a wiki or artist page will be treated like an
alias for autocomplete purposes. This is limited to non-ASCII other names,
to prevent English other names from interfering with regular tag searches.
This commit is contained in:
evazion
2020-12-14 16:48:21 -06:00
parent 23f6b8a46d
commit c02c31b966
3 changed files with 50 additions and 2 deletions

View File

@@ -72,8 +72,11 @@ class AutocompleteService
results = results.uniq.sort_by { |r| [r[:antecedent].length, -r[:post_count]] }.take(limit) results = results.uniq.sort_by { |r| [r[:antecedent].length, -r[:post_count]] }.take(limit)
elsif string.include?("*") elsif string.include?("*")
results = tag_matches(string) results = tag_matches(string)
results = tag_other_name_matches(string) if results.blank?
else else
results = tag_matches(string + "*") string += "*"
results = tag_matches(string)
results = tag_other_name_matches(string) if results.blank?
results = tag_autocorrect_matches(string) if results.blank? results = tag_autocorrect_matches(string) if results.blank?
end end
@@ -81,6 +84,8 @@ class AutocompleteService
end end
def tag_matches(string) def tag_matches(string)
return [] if string =~ /[^[:ascii:]]/
name_matches = Tag.nonempty.name_matches(string).order(post_count: :desc).limit(limit) name_matches = Tag.nonempty.name_matches(string).order(post_count: :desc).limit(limit)
alias_matches = Tag.nonempty.alias_matches(string).order(post_count: :desc).limit(limit) alias_matches = Tag.nonempty.alias_matches(string).order(post_count: :desc).limit(limit)
union = "((#{name_matches.to_sql}) UNION (#{alias_matches.to_sql})) AS tags" union = "((#{name_matches.to_sql}) UNION (#{alias_matches.to_sql})) AS tags"
@@ -100,6 +105,7 @@ class AutocompleteService
end end
def tag_autocorrect_matches(string) def tag_autocorrect_matches(string)
string = string.delete("*")
tags = Tag.nonempty.autocorrect_matches(string).limit(limit) tags = Tag.nonempty.autocorrect_matches(string).limit(limit)
tags.map do |tag| tags.map do |tag|
@@ -107,6 +113,21 @@ class AutocompleteService
end end
end end
def tag_other_name_matches(string)
return [] unless string =~ /[^[:ascii:]]/
artists = Artist.undeleted.any_other_name_like(string)
wikis = WikiPage.undeleted.other_names_match(string)
tags = Tag.where(name: wikis.select(:title)).or(Tag.where(name: artists.select(:name)))
tags = tags.nonempty.order(post_count: :desc).limit(limit).includes(:wiki_page, :artist)
tags.map do |tag|
other_names = tag.artist&.other_names.to_a + tag.wiki_page&.other_names.to_a
antecedent = other_names.find { |other_name| other_name.ilike?(string) }
{ type: "tag", label: tag.pretty_name, value: tag.name, category: tag.category, post_count: tag.post_count, antecedent: antecedent }
end
end
def autocomplete_metatag(metatag, value) def autocomplete_metatag(metatag, value)
results = case metatag.to_sym results = case metatag.to_sym
when :user, :approver, :commenter, :comm, :noter, :noteupdater, :commentaryupdater, when :user, :approver, :commenter, :comm, :noter, :noteupdater, :commentaryupdater,

View File

@@ -45,7 +45,7 @@ class WikiPage < ApplicationRecord
def other_names_match(name) def other_names_match(name)
if name =~ /\*/ if name =~ /\*/
subquery = WikiPage.from("unnest(other_names) AS other_name").where_ilike("other_name", name) subquery = WikiPage.from("unnest(other_names) AS other_name").where_ilike("other_name", normalize_other_name(name))
where(id: subquery) where(id: subquery)
else else
other_names_include(name) other_names_include(name)

View File

@@ -97,6 +97,33 @@ class AutocompleteServiceTest < ActiveSupport::TestCase
assert_autocomplete_includes("mole_under_eye", "~/mue", :tag_query) assert_autocomplete_includes("mole_under_eye", "~/mue", :tag_query)
end end
should "autocomplete tags from wiki and artist other names" do
create(:tag, name: "touhou")
create(:tag, name: "bkub", category: Tag.categories.artist)
create(:wiki_page, title: "touhou", other_names: %w[東方 东方 동방])
create(:artist, name: "bkub", other_names: %w[大川ぶくぶ フミンバイン])
assert_autocomplete_equals(["touhou"], "", :tag_query)
assert_autocomplete_equals(["touhou"], "", :tag_query)
assert_autocomplete_equals(["touhou"], "", :tag_query)
assert_autocomplete_equals(["touhou"], "*東*", :tag_query)
assert_autocomplete_equals(["touhou"], "東*", :tag_query)
assert_autocomplete_equals([], "*東", :tag_query)
assert_autocomplete_equals(["touhou"], "*方*", :tag_query)
assert_autocomplete_equals(["touhou"], "*方", :tag_query)
assert_autocomplete_equals([], "", :tag_query)
assert_autocomplete_equals(["bkub"], "*大*", :tag_query)
assert_autocomplete_equals(["bkub"], "", :tag_query)
assert_autocomplete_equals([], "*大", :tag_query)
assert_autocomplete_equals(["bkub"], "*川*", :tag_query)
assert_autocomplete_equals([], "*川", :tag_query)
assert_autocomplete_equals([], "", :tag_query)
end
should "autocomplete wildcard searches" do should "autocomplete wildcard searches" do
create(:tag, name: "mole", post_count: 150) create(:tag, name: "mole", post_count: 150)
create(:tag, name: "mole_under_eye", post_count: 100) create(:tag, name: "mole_under_eye", post_count: 100)