backfill user search names

This commit is contained in:
Dylan Knutson
2025-02-12 21:03:53 +00:00
parent ff579c1a30
commit 13c2d3cbed
16 changed files with 1572 additions and 271 deletions

View File

@@ -93,19 +93,35 @@ task :reverse_csv do
out_csv.close
end
task migrate_domain: :environment do
task migrate_to_domain: :environment do
only_user = ENV["only_user"]
allowed_domains = %w[e621 fa ib]
only_domains = (ENV["only_domains"] || "").split(",")
only_domains = allowed_domains if only_domains.empty?
if (only_domains - allowed_domains).any?
raise "only_domains must be a subset of #{allowed_domains.join(", ")}"
end
migrator = Domain::MigrateToDomain.new
migrator.migrate_e621_users(only_user: only_user)
migrator.migrate_e621_posts(only_user: only_user)
migrator.migrate_fa_users(only_user: only_user)
migrator.migrate_fa_posts(only_user: only_user)
migrator.migrate_e621_users_favs(only_user: only_user)
migrator.migrate_fa_users_favs(only_user: only_user)
migrator.migrate_fa_users_followed_users(only_user: only_user)
migrator.migrate_inkbunny_users(only_user: only_user)
migrator.migrate_inkbunny_posts(only_user: only_user)
migrator.migrate_inkbunny_pools(only_user: nil) if only_user.nil?
if only_domains.include?("e621")
migrator.migrate_e621_users(only_user: only_user)
migrator.migrate_e621_posts(only_user: only_user)
migrator.migrate_e621_users_favs(only_user: only_user)
end
if only_domains.include?("fa")
migrator.migrate_fa_users(only_user: only_user)
migrator.migrate_fa_posts(only_user: only_user)
migrator.migrate_fa_users_favs(only_user: only_user)
migrator.migrate_fa_users_followed_users(only_user: only_user)
end
if only_domains.include?("ib")
migrator.migrate_inkbunny_users(only_user: only_user)
migrator.migrate_inkbunny_posts(only_user: only_user)
migrator.migrate_inkbunny_pools(only_user: nil) if only_user.nil?
end
end
task infer_last_submission_log_entries: :environment do

View File

@@ -11,7 +11,6 @@ class Domain::Fa::Job::Base < Scraper::JobBase
def initialize(*args)
super(*T.unsafe(args))
@posts_enqueued_for_scan = T.let(Set.new, T::Set[Integer])
@force_scan = T.let(false, T::Boolean)
end
protected

View File

@@ -473,6 +473,9 @@ class Domain::MigrateToDomain
new_post.last_file_updated_at = old_post.last_file_updated_at
new_post.deep_update_log_entry_id = old_post.deep_update_log_entry_id
new_post.shallow_update_log_entry_id = old_post.shallow_update_log_entry_id
new_post.shallow_updated_at = old_post.shallow_updated_at
new_post.deep_updated_at = old_post.deep_updated_at
new_post.ib_detail_raw = old_post.ib_detail_raw
if old_creator = old_post.creator
new_post.creator =
@@ -792,9 +795,12 @@ class Domain::MigrateToDomain
).returns(T::Hash[String, T.untyped])
end
def clean_attributes(klass, attributes)
json_attributes =
T.unsafe(klass).attr_json_registry.attribute_names.map(&:to_s)
attributes.except(*json_attributes).except("id", "created_at", "updated_at")
reject_attrs = klass.columns.filter(&:virtual?).map(&:name)
if klass < AttrJsonRecordAliases
reject_attrs +=
T.unsafe(klass).attr_json_registry.attribute_names.map(&:to_s)
end
attributes.except(*reject_attrs).except("id", "created_at", "updated_at")
end
sig do
@@ -804,10 +810,12 @@ class Domain::MigrateToDomain
batch: T::Array[T.all(T.type_parameter(:Old), ActiveRecord::Base)],
unique_by: T.nilable(T::Array[Symbol]),
model_mapper:
T
.proc
.params(record: T.all(T.type_parameter(:Old), ActiveRecord::Base))
.returns(T.all(T.type_parameter(:New), ActiveRecord::Base)),
T.nilable(
T
.proc
.params(record: T.all(T.type_parameter(:Old), ActiveRecord::Base))
.returns(T.all(T.type_parameter(:New), ActiveRecord::Base)),
),
)
.returns(T::Array[T.type_parameter(:New)])
end
@@ -816,10 +824,14 @@ class Domain::MigrateToDomain
models = []
klass.transaction do
batch.each do |record|
model = model_mapper.call(record)
model.name_for_search = model.names_for_search_value if model.is_a?(
Domain::User,
)
model = model_mapper ? model_mapper.call(record) : record
if model.is_a?(Domain::User)
model.names_for_search_values.each do |name|
model.user_search_names.build(name:)
end
end
models << model
attributes << clean_attributes(klass, model.attributes)
end
@@ -830,6 +842,20 @@ class Domain::MigrateToDomain
end
returned.zip(models).each { |hash, model| model.id = hash["id"] }
end
if klass < Domain::User
models.each do |model|
model.user_search_names.each do |user_search_name|
user_search_name.user_id = model.id
end
end
migrate_batch(
Domain::UserSearchName,
models.flat_map(&:user_search_names),
)
end
models
end
end

View File

@@ -41,21 +41,39 @@ class Domain::User < ReduxApplicationRecord
super
end
before_save do
self.name_for_search = names_for_search_value
true
after_save :set_names_for_search
sig { void }
def set_names_for_search
values = names_for_search_values
models = user_search_names.pluck(:name)
missing_values = values - models
extra_values = models - values
::Domain::UserSearchName.upsert_all(
missing_values.map { |name| { user_id: id, name: name } },
unique_by: %i[user_id name],
)
::Domain::UserSearchName.where(user_id: id, name: extra_values).delete_all
end
sig(:final) { returns(String) }
def names_for_search_value
sig(:final) { returns(T::Array[String]) }
def names_for_search_values
names_for_search
.map { |name| name.downcase.gsub(/[^a-zA-Z0-9\-\.]/, "") }
.map { |name| name.downcase.strip }
.reject(&:blank?)
.uniq
.join("|")
.sort
end
attr_json :migrated_user_favs_at, :datetime
has_many :user_search_names,
class_name: "::Domain::UserSearchName",
inverse_of: :user,
dependent: :destroy
has_many :user_post_creations,
class_name: "::Domain::UserPostCreation",
inverse_of: :user,

View File

@@ -64,7 +64,7 @@ class Domain::User::FaUser < Domain::User
sig { override.returns(T::Array[String]) }
def names_for_search
[name, full_name].compact
[name, url_name, full_name].compact
end
sig { returns(T::Boolean) }

View File

@@ -0,0 +1,6 @@
# typed: strict
class Domain::UserSearchName < ReduxApplicationRecord
self.table_name = "domain_user_search_names"
belongs_to :user, class_name: "Domain::User", inverse_of: :user_search_names
validates :name, presence: true
end

View File

@@ -60,12 +60,30 @@ class CreateUnifiedDomainTables < ActiveRecord::Migration[7.2]
create_table :domain_users do |t|
t.enum :type, null: false, enum_type: "domain_user_type"
t.string :name_for_search
t.jsonb :json_attributes, default: {}
t.timestamps
t.index :type
t.index :name_for_search, using: :gin, opclass: :gin_trgm_ops
end
create_table :domain_user_search_names do |t|
t.references :user, null: false, foreign_key: { to_table: :domain_users }
t.string :name, null: false
t.virtual :dmetaphone_primary,
type: :string,
as: "dmetaphone(name)",
stored: true
t.virtual :dmetaphone_alt,
type: :string,
as: "dmetaphone_alt(name)",
stored: true
t.index %i[user_id name], unique: true
t.index :name, using: :gin, opclass: :gin_trgm_ops
t.index :dmetaphone_primary, using: :gin, opclass: :gin_trgm_ops
t.index :dmetaphone_alt, using: :gin, opclass: :gin_trgm_ops
end
create_table :domain_user_avatars do |t|

View File

@@ -2993,6 +2993,38 @@ CREATE TABLE public.domain_user_post_favs (
);
--
-- Name: domain_user_search_names; Type: TABLE; Schema: public; Owner: -; Tablespace: mirai
--
CREATE TABLE public.domain_user_search_names (
id bigint NOT NULL,
user_id bigint NOT NULL,
name character varying NOT NULL,
dmetaphone_primary character varying GENERATED ALWAYS AS (public.dmetaphone((name)::text)) STORED,
dmetaphone_alt character varying GENERATED ALWAYS AS (public.dmetaphone_alt((name)::text)) STORED
);
--
-- Name: domain_user_search_names_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.domain_user_search_names_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: domain_user_search_names_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public.domain_user_search_names_id_seq OWNED BY public.domain_user_search_names.id;
--
-- Name: domain_user_user_follows; Type: TABLE; Schema: public; Owner: -; Tablespace: mirai
--
@@ -3010,7 +3042,6 @@ CREATE TABLE public.domain_user_user_follows (
CREATE TABLE public.domain_users (
id bigint NOT NULL,
type public.domain_user_type NOT NULL,
name_for_search character varying,
json_attributes jsonb DEFAULT '{}'::jsonb,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL
@@ -4523,6 +4554,13 @@ ALTER TABLE ONLY public.domain_twitter_users ALTER COLUMN id SET DEFAULT nextval
ALTER TABLE ONLY public.domain_user_avatars ALTER COLUMN id SET DEFAULT nextval('public.domain_user_avatars_id_seq'::regclass);
--
-- Name: domain_user_search_names id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.domain_user_search_names ALTER COLUMN id SET DEFAULT nextval('public.domain_user_search_names_id_seq'::regclass);
--
-- Name: domain_users id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -5300,6 +5338,14 @@ ALTER TABLE ONLY public.domain_user_avatars
ADD CONSTRAINT domain_user_avatars_pkey PRIMARY KEY (id);
--
-- Name: domain_user_search_names domain_user_search_names_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: mirai
--
ALTER TABLE ONLY public.domain_user_search_names
ADD CONSTRAINT domain_user_search_names_pkey PRIMARY KEY (id);
--
-- Name: domain_users domain_users_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: mirai
--
@@ -6948,6 +6994,41 @@ CREATE INDEX index_domain_user_post_favs_on_post_id_and_user_id ON public.domain
CREATE UNIQUE INDEX index_domain_user_post_favs_on_user_id_and_post_id ON public.domain_user_post_favs USING btree (user_id, post_id);
--
-- Name: index_domain_user_search_names_on_dmetaphone_alt; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
--
CREATE INDEX index_domain_user_search_names_on_dmetaphone_alt ON public.domain_user_search_names USING gin (dmetaphone_alt public.gin_trgm_ops);
--
-- Name: index_domain_user_search_names_on_dmetaphone_primary; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
--
CREATE INDEX index_domain_user_search_names_on_dmetaphone_primary ON public.domain_user_search_names USING gin (dmetaphone_primary public.gin_trgm_ops);
--
-- Name: index_domain_user_search_names_on_name; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
--
CREATE INDEX index_domain_user_search_names_on_name ON public.domain_user_search_names USING gin (name public.gin_trgm_ops);
--
-- Name: index_domain_user_search_names_on_user_id; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
--
CREATE INDEX index_domain_user_search_names_on_user_id ON public.domain_user_search_names USING btree (user_id);
--
-- Name: index_domain_user_search_names_on_user_id_and_name; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
--
CREATE UNIQUE INDEX index_domain_user_search_names_on_user_id_and_name ON public.domain_user_search_names USING btree (user_id, name);
--
-- Name: index_domain_user_user_follows_on_from_id_and_to_id; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
--
@@ -6962,13 +7043,6 @@ CREATE UNIQUE INDEX index_domain_user_user_follows_on_from_id_and_to_id ON publi
CREATE INDEX index_domain_user_user_follows_on_to_id_and_from_id ON public.domain_user_user_follows USING btree (to_id, from_id);
--
-- Name: index_domain_users_on_name_for_search; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
--
CREATE INDEX index_domain_users_on_name_for_search ON public.domain_users USING gin (name_for_search public.gin_trgm_ops);
--
-- Name: index_domain_users_on_type; Type: INDEX; Schema: public; Owner: -; Tablespace: mirai
--
@@ -8300,6 +8374,14 @@ ALTER TABLE ONLY public.domain_inkbunny_posts
ADD CONSTRAINT fk_rails_82ffd77f0c FOREIGN KEY (shallow_update_log_entry_id) REFERENCES public.http_log_entries(id);
--
-- Name: domain_user_search_names fk_rails_8475fe75b5; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.domain_user_search_names
ADD CONSTRAINT fk_rails_8475fe75b5 FOREIGN KEY (user_id) REFERENCES public.domain_users(id);
--
-- Name: domain_fa_follows fk_rails_87bfff6dba; Type: FK CONSTRAINT; Schema: public; Owner: -
--

View File

@@ -15,13 +15,13 @@ class Domain::Inkbunny::Job::UserGalleryJob
sig do
params(
args: T.untyped,
args: T::Hash[::Symbol, T.untyped],
block: T.nilable(T.proc.params(job: Domain::Inkbunny::Job::UserGalleryJob).void)
).returns(T.any(Domain::Inkbunny::Job::UserGalleryJob, FalseClass))
end
def perform_later(args, &block); end
sig { params(args: T.untyped).returns(T.untyped) }
sig { params(args: T::Hash[::Symbol, T.untyped]).returns(T.untyped) }
def perform_now(args); end
end
end

View File

@@ -530,6 +530,20 @@ class Domain::User
sig { params(value: T::Enumerable[::Domain::UserPostFav]).void }
def user_post_favs=(value); end
sig { returns(T::Array[T.untyped]) }
def user_search_name_ids; end
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
def user_search_name_ids=(ids); end
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :user_search_names`.
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
sig { returns(::Domain::UserSearchName::PrivateCollectionProxy) }
def user_search_names; end
sig { params(value: T::Enumerable[::Domain::UserSearchName]).void }
def user_search_names=(value); end
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :user_user_follows_from`.
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
sig { returns(::Domain::UserUserFollow::PrivateCollectionProxy) }
@@ -949,51 +963,6 @@ class Domain::User
sig { void }
def migrated_user_favs_at_will_change!; end
sig { returns(T.nilable(::String)) }
def name_for_search; end
sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) }
def name_for_search=(value); end
sig { returns(T::Boolean) }
def name_for_search?; end
sig { returns(T.nilable(::String)) }
def name_for_search_before_last_save; end
sig { returns(T.untyped) }
def name_for_search_before_type_cast; end
sig { returns(T::Boolean) }
def name_for_search_came_from_user?; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_change; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_change_to_be_saved; end
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_for_search_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search_in_database; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_previous_change; end
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_for_search_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search_previously_was; end
sig { returns(T.nilable(::String)) }
def name_for_search_was; end
sig { void }
def name_for_search_will_change!; end
sig { void }
def restore_created_at!; end
@@ -1009,9 +978,6 @@ class Domain::User
sig { void }
def restore_migrated_user_favs_at!; end
sig { void }
def restore_name_for_search!; end
sig { void }
def restore_type!; end
@@ -1048,12 +1014,6 @@ class Domain::User
sig { returns(T::Boolean) }
def saved_change_to_migrated_user_favs_at?; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def saved_change_to_name_for_search; end
sig { returns(T::Boolean) }
def saved_change_to_name_for_search?; end
sig { returns(T.nilable([T.untyped, T.untyped])) }
def saved_change_to_type; end
@@ -1181,9 +1141,6 @@ class Domain::User
sig { returns(T::Boolean) }
def will_save_change_to_migrated_user_favs_at?; end
sig { returns(T::Boolean) }
def will_save_change_to_name_for_search?; end
sig { returns(T::Boolean) }
def will_save_change_to_type?; end

View File

@@ -576,6 +576,20 @@ class Domain::User::E621User
sig { params(value: T::Enumerable[::Domain::UserPostFav]).void }
def user_post_favs=(value); end
sig { returns(T::Array[T.untyped]) }
def user_search_name_ids; end
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
def user_search_name_ids=(ids); end
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :user_search_names`.
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
sig { returns(::Domain::UserSearchName::PrivateCollectionProxy) }
def user_search_names; end
sig { params(value: T::Enumerable[::Domain::UserSearchName]).void }
def user_search_names=(value); end
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :user_user_follows_from`.
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
sig { returns(::Domain::UserUserFollow::PrivateCollectionProxy) }
@@ -1112,51 +1126,6 @@ class Domain::User::E621User
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search; end
sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) }
def name_for_search=(value); end
sig { returns(T::Boolean) }
def name_for_search?; end
sig { returns(T.nilable(::String)) }
def name_for_search_before_last_save; end
sig { returns(T.untyped) }
def name_for_search_before_type_cast; end
sig { returns(T::Boolean) }
def name_for_search_came_from_user?; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_change; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_change_to_be_saved; end
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_for_search_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search_in_database; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_previous_change; end
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_for_search_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search_previously_was; end
sig { returns(T.nilable(::String)) }
def name_for_search_was; end
sig { void }
def name_for_search_will_change!; end
sig { returns(T.nilable(::String)) }
def name_in_database; end
@@ -1299,9 +1268,6 @@ class Domain::User::E621User
sig { void }
def restore_name!; end
sig { void }
def restore_name_for_search!; end
sig { void }
def restore_num_other_favs_cached!; end
@@ -1368,12 +1334,6 @@ class Domain::User::E621User
sig { returns(T::Boolean) }
def saved_change_to_name?; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def saved_change_to_name_for_search; end
sig { returns(T::Boolean) }
def saved_change_to_name_for_search?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def saved_change_to_num_other_favs_cached; end
@@ -1634,9 +1594,6 @@ class Domain::User::E621User
sig { returns(T::Boolean) }
def will_save_change_to_name?; end
sig { returns(T::Boolean) }
def will_save_change_to_name_for_search?; end
sig { returns(T::Boolean) }
def will_save_change_to_num_other_favs_cached?; end

View File

@@ -616,6 +616,20 @@ class Domain::User::FaUser
sig { params(value: T::Enumerable[::Domain::UserPostFav]).void }
def user_post_favs=(value); end
sig { returns(T::Array[T.untyped]) }
def user_search_name_ids; end
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
def user_search_name_ids=(ids); end
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :user_search_names`.
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
sig { returns(::Domain::UserSearchName::PrivateCollectionProxy) }
def user_search_names; end
sig { params(value: T::Enumerable[::Domain::UserSearchName]).void }
def user_search_names=(value); end
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :user_user_follows_from`.
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
sig { returns(::Domain::UserUserFollow::PrivateCollectionProxy) }
@@ -1432,51 +1446,6 @@ class Domain::User::FaUser
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search; end
sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) }
def name_for_search=(value); end
sig { returns(T::Boolean) }
def name_for_search?; end
sig { returns(T.nilable(::String)) }
def name_for_search_before_last_save; end
sig { returns(T.untyped) }
def name_for_search_before_type_cast; end
sig { returns(T::Boolean) }
def name_for_search_came_from_user?; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_change; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_change_to_be_saved; end
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_for_search_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search_in_database; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_previous_change; end
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_for_search_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search_previously_was; end
sig { returns(T.nilable(::String)) }
def name_for_search_was; end
sig { void }
def name_for_search_will_change!; end
sig { returns(T.nilable(::String)) }
def name_in_database; end
@@ -1952,9 +1921,6 @@ class Domain::User::FaUser
sig { void }
def restore_name!; end
sig { void }
def restore_name_for_search!; end
sig { void }
def restore_num_comments_given!; end
@@ -2093,12 +2059,6 @@ class Domain::User::FaUser
sig { returns(T::Boolean) }
def saved_change_to_name?; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def saved_change_to_name_for_search; end
sig { returns(T::Boolean) }
def saved_change_to_name_for_search?; end
sig { returns(T.nilable([T.nilable(::Integer), T.nilable(::Integer)])) }
def saved_change_to_num_comments_given; end
@@ -2714,9 +2674,6 @@ class Domain::User::FaUser
sig { returns(T::Boolean) }
def will_save_change_to_name?; end
sig { returns(T::Boolean) }
def will_save_change_to_name_for_search?; end
sig { returns(T::Boolean) }
def will_save_change_to_num_comments_given?; end

View File

@@ -621,6 +621,20 @@ class Domain::User::InkbunnyUser
sig { params(value: T::Enumerable[::Domain::UserPostFav]).void }
def user_post_favs=(value); end
sig { returns(T::Array[T.untyped]) }
def user_search_name_ids; end
sig { params(ids: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
def user_search_name_ids=(ids); end
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :user_search_names`.
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
sig { returns(::Domain::UserSearchName::PrivateCollectionProxy) }
def user_search_names; end
sig { params(value: T::Enumerable[::Domain::UserSearchName]).void }
def user_search_names=(value); end
# This method is created by ActiveRecord on the `Domain::User` class because it declared `has_many :user_user_follows_from`.
# 🔗 [Rails guide for `has_many` association](https://guides.rubyonrails.org/association_basics.html#the-has-many-association)
sig { returns(::Domain::UserUserFollow::PrivateCollectionProxy) }
@@ -1202,51 +1216,6 @@ class Domain::User::InkbunnyUser
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search; end
sig { params(value: T.nilable(::String)).returns(T.nilable(::String)) }
def name_for_search=(value); end
sig { returns(T::Boolean) }
def name_for_search?; end
sig { returns(T.nilable(::String)) }
def name_for_search_before_last_save; end
sig { returns(T.untyped) }
def name_for_search_before_type_cast; end
sig { returns(T::Boolean) }
def name_for_search_came_from_user?; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_change; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_change_to_be_saved; end
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_for_search_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search_in_database; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def name_for_search_previous_change; end
sig { params(from: T.nilable(::String), to: T.nilable(::String)).returns(T::Boolean) }
def name_for_search_previously_changed?(from: T.unsafe(nil), to: T.unsafe(nil)); end
sig { returns(T.nilable(::String)) }
def name_for_search_previously_was; end
sig { returns(T.nilable(::String)) }
def name_for_search_was; end
sig { void }
def name_for_search_will_change!; end
sig { returns(T.nilable(::String)) }
def name_in_database; end
@@ -1292,9 +1261,6 @@ class Domain::User::InkbunnyUser
sig { void }
def restore_name!; end
sig { void }
def restore_name_for_search!; end
sig { void }
def restore_scanned_gallery_at!; end
@@ -1364,12 +1330,6 @@ class Domain::User::InkbunnyUser
sig { returns(T::Boolean) }
def saved_change_to_name?; end
sig { returns(T.nilable([T.nilable(::String), T.nilable(::String)])) }
def saved_change_to_name_for_search; end
sig { returns(T::Boolean) }
def saved_change_to_name_for_search?; end
sig { returns(T.nilable([T.nilable(::ActiveSupport::TimeWithZone), T.nilable(::ActiveSupport::TimeWithZone)])) }
def saved_change_to_scanned_gallery_at; end
@@ -1672,9 +1632,6 @@ class Domain::User::InkbunnyUser
sig { returns(T::Boolean) }
def will_save_change_to_name?; end
sig { returns(T::Boolean) }
def will_save_change_to_name_for_search?; end
sig { returns(T::Boolean) }
def will_save_change_to_scanned_gallery_at?; end

1273
sorbet/rbi/dsl/domain/user_search_name.rbi generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -151,6 +151,14 @@ RSpec.describe Domain::MigrateToDomain do
)
end
it "migrates search names" do
migrator.migrate_fa_users
new_user = Domain::User::FaUser.find_by(url_name: old_user.url_name)
expect(new_user.user_search_names.pluck(:name)).to match_array(
["test user full name", "test_user", "testuser"],
)
end
it "handles multiple users in batches" do
# Create a few more old users
additional_users =

View File

@@ -148,5 +148,32 @@ RSpec.describe Domain::User::FaUser, type: :model do
expect(user.posts).not_to include(faved_post1, faved_post2)
end
end
describe "#user_search_names" do
let(:user) do
create(
:domain_user_fa_user,
name: "name_attr",
url_name: "url_name_attr",
full_name: "artist name",
)
end
it "returns associated user search names" do
user.reload
expect(user.user_search_names.map(&:name)).to match_array(
["name_attr", "url_name_attr", "artist name"],
)
end
it "changes search names when name changes" do
user.name = "new_name"
user.save!
user.reload
expect(user.user_search_names.map(&:name)).to match_array(
["artist name", "new_name", "url_name_attr"],
)
end
end
end
end