discourse/spec/jobs
Joffrey JAFFEUX 7ecb945ec4
FEATURE: Add full-text search for chat messages (#34704)
## Overview

This PR introduces comprehensive search functionality for chat messages,
enabling users to search through their chat history both globally across
all accessible channels and within specific channels.

### Search Capabilities

**All-Channel Search**: When no channel is specified, users can search
across all channels they have access to. The search respects channel
permissions through `ChannelFetcher.all_secured_channel_ids`, ensuring
users only see results from channels they can view.

**Per-Channel Search**: Users can scope their search to a specific
channel by providing a `channel_id` parameter, useful for finding
messages within a particular conversation context.

**Search Features**:
- Full-text search using PostgreSQL's tsvector/tsquery
- Advanced filters: `@username` to filter by author, `#channel` to
filter by channel slug
- Sort options: relevance (default) or latest
- Pagination support
- Search data weighted by relevance

## Site Setting: `chat_search_enabled`

This feature is gated behind the `chat_search_enabled` site setting,
which is currently:
- **Default**: `false`
- **Hidden**: `true`
- **Client-accessible**: `true`

### Deployment Strategy

Due to the need for chat messages to be indexed before search becomes
useful, we're implementing a two-phase deployment:

**Phase 1 (Initial Merge)**:
- `chat_search_enabled` remains `false` and hidden
- The `register_search_index` uses default (true) instead of `chat_search_enabled` value
- This allows the reindexing infrastructure to begin indexing existing
chat messages even if we don't show the UI yet

**Wait Period**:
- Wait at least one week after Phase 1 deployment
- `Jobs::ReindexSearch` runs every 2 hours and will progressively index
all chat messages
- This ensures most sites have a significant part of their chat history indexed

**Phase 2 (Follow-up Merge)**:
- Set `chat_search_enabled` default to `true` and unhide it
- Update the `register_search_index` enabled proc uses the default
(true) instead of using the `chat_search_enabled` setting
- Users can now access search with pre-indexed data

**Rationale**: Without this phased approach, users would see the search
UI immediately but receive no results until the reindexing job runs,
creating a confusing experience. By pre-indexing while the UI is hidden,
we ensure search works immediately when enabled.

## New Plugin API: `register_search_index`

This PR introduces a new plugin API that allows plugins to register
custom search indexes that integrate seamlessly with Discourse's search
infrastructure.

### API Signature

```ruby
register_search_index(
  model_class:,              # The ActiveRecord model to index
  search_data_class:,        # The model for storing search data
  index_version:,            # Version number for re-indexing
  search_data:,              # Proc that returns weighted search data
  load_unindexed_record_ids:,# Proc that finds records needing indexing
  enabled:                   # Optional proc to enable/disable (default: -> { true })
)
```

### How It Works

**Integration with SearchIndexer**: When `SearchIndexer.index(obj)` is
called, it checks registered search handlers for the object's type. If a
handler matches, it:
1. Calls the `search_data` proc with the object and an `IndexerHelper`
instance
2. Receives weighted search data (`:a_weight`, `:b_weight`, `:c_weight`,
`:d_weight`)
3. Updates the corresponding search data table with PostgreSQL's
tsvector

**Integration with Jobs::ReindexSearch**: The scheduled job (runs every
2 hours) calls `rebuild_registered_search_handlers`, which:
1. Iterates through all registered search handlers
2. Skips handlers where `enabled` proc returns `false`
3. Calls `load_unindexed_record_ids` to find records needing indexing
4. Indexes up to `limit` records per handler (default: 10,000)

### Chat Implementation Example

```ruby
register_search_index(
  model_class: Chat::Message,
  search_data_class: Chat::MessageSearchData,
  index_version: 1,
  search_data: proc { |message, indexer_helper|
    {
      a_weight: message.message,
      d_weight: indexer_helper.scrub_html(message.cooked)[0..600_000]
    }
  },
  load_unindexed_record_ids: proc { |limit:, index_version:|
    Chat::Message
      .joins("LEFT JOIN chat_message_search_data ON chat_message_id = chat_messages.id")
      .where(
        "chat_message_search_data.locale IS NULL OR 
         chat_message_search_data.locale != ? OR 
         chat_message_search_data.version != ?",
        SiteSetting.default_locale,
        index_version
      )
      .order("chat_messages.id ASC")
      .limit(limit)
      .pluck(:id)
  }
)
```

Co-authored-by: Martin Brennan <mjrbrennan@gmail.com>
Co-authored-by: Loïc Guitaut <5648+Flink@users.noreply.github.com>
2025-10-22 11:30:35 +02:00
..
regular FIX: Timeout issue when updating a large collection of users when changing the default_categories_* and default_tags_* SiteSettings (#33665) 2025-08-20 12:55:53 -05:00
scheduled DEV: Add back lost admin problem notification (#35146) 2025-10-03 16:14:28 +08:00
about_stats_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
activation_reminder_emails_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
auto_queue_handler_spec.rb DEV: Convert min_trust_to_flag_posts setting to groups (#24864) 2023-12-13 17:18:42 +08:00
automatic_group_membership_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
backfill_sidebar_site_settings_spec.rb FIX: Set Jobs::BackfillSidebarSiteSettings cluster concurrency to 1 (#22316) 2023-06-28 13:07:46 +08:00
bookmark_reminder_notifications_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
bulk_grant_trust_level_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
bulk_invite_spec.rb DEV: Update bulk-invite logs and PM template (#27057) 2024-05-17 12:21:21 -06:00
bump_topic_spec.rb DEV: Fix various rubocop lints (#24749) 2023-12-06 23:25:00 +01:00
call_discourse_hub_spec.rb FEATURE: call hub API to update Discourse discover enrollment. (#25634) 2024-02-23 11:42:28 +05:30
change_display_name_spec.rb UX: Remove loading="lazy" from avatars for improved UX (#30897) 2025-01-21 14:06:45 +00:00
check_new_features_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
check_translation_overrides_spec.rb DEV: Refresh translation override status when updating (#31233) 2025-02-07 14:12:28 +08:00
clean_dismissed_topic_users_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
clean_up_associated_accounts_spec.rb DEV: Update the rubocop-discourse gem 2023-06-26 11:41:52 +02:00
clean_up_crawler_stats_spec.rb DEV: Update the rubocop-discourse gem 2023-06-26 11:41:52 +02:00
clean_up_email_change_requests_spec.rb Add RSpec 4 compatibility (#17652) 2022-07-28 10:27:38 +08:00
clean_up_email_logs_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
clean_up_inactive_users_spec.rb FIX: consider users.created_at for inactive cleanup (#21688) 2023-05-23 13:41:23 +05:30
clean_up_post_reply_keys_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
clean_up_tags_spec.rb DEV: Remove unnecessary rails_helper requiring (#26364) 2024-03-26 11:32:01 +01:00
clean_up_unused_staged_users_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
clean_up_unused_user_api_key_clients_spec.rb Require permitted scopes when registering a client (#29718) 2024-11-19 15:28:04 -05:00
clean_up_unused_user_api_keys_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
clean_up_uploads_spec.rb FIX: twitter_summary_large_image is renamed to x (#31870) 2025-03-19 11:55:11 +11:00
clean_up_user_api_keys_max_life_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
clean_up_user_export_topics_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
cleanup_imap_sync_log_spec.rb Add RSpec 4 compatibility (#17652) 2022-07-28 10:27:38 +08:00
cleanup_redelivering_web_hook_events_spec.rb FEATURE: Add Mechanism to redeliver all failed webhook events (#27609) 2024-07-08 15:43:16 -05:00
close_topic_spec.rb DEV: rename topic_id to timerable_id for BaseTimer (#34667) 2025-09-17 13:19:17 +08:00
correct_missing_dualstack_urls_spec.rb DEV: Use Discourse::SYSTEM_USER_ID in fixtures/009_users (#24743) 2023-12-07 09:04:45 +10:00
crawl_topic_link_spec.rb DEV: Clean up scope resolution operators in plugins (#34979) 2025-09-30 14:36:34 +02:00
create_linked_topic_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
create_missing_avatars_spec.rb Add RSpec 4 compatibility (#17652) 2022-07-28 10:27:38 +08:00
create_recent_post_search_indexes_spec.rb DEV: Fix random typos (#24756) 2023-12-06 22:25:26 +01:00
create_user_reviewable_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
delete_replies_spec.rb FIX: DeleteReplies should use the guardian instead of checking for staff (#35443) 2025-10-16 18:26:41 +02:00
delete_topic_spec.rb DEV: Fix various rubocop lints (#24749) 2023-12-06 23:25:00 +01:00
disable_bootstrap_mode_spec.rb DEV: Log site setting changes from the rails console (#31353) 2025-02-24 14:57:01 -07:00
download_avatar_from_url_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
download_backup_email_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
download_profile_background_from_url_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
emit_web_hook_event_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
enable_bootstrap_mode_spec.rb DEV: Log site setting changes from the rails console (#31353) 2025-02-24 14:57:01 -07:00
enqueue_digest_emails_spec.rb FIX: send activity summaries based on "last seen" (#27035) 2024-05-22 10:23:03 +02:00
enqueue_suspect_users_spec.rb DEV: Update the rubocop-discourse gem 2023-06-26 11:41:52 +02:00
ensure_db_consistency_spec.rb FIX: we introduced a Jobs::UserEmail which broke consistency checks (#30409) 2024-12-22 21:33:47 +11:00
ensure_s3_uploads_existence_multisite_spec.rb FIX: Jobs::EnsureS3UploadsExistence broken for multisite (#27401) 2024-06-10 16:26:39 +08:00
ensure_s3_uploads_existence_spec.rb DEV: Introduce hidden s3_inventory_bucket site setting (#27304) 2024-06-10 13:16:00 +08:00
export_csv_file_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
export_user_archive_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
feature_topic_users_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
fix_out_of_sync_user_uploaded_avatar_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
fix_primary_emails_for_staged_users_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
fix_s3_etags_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
fix_user_usernames_and_groups_names_clash_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
grant_anniversary_badges_spec.rb DEV: extract anniversary badge query (#19716) 2023-01-16 11:55:00 +01:00
grant_new_user_of_the_month_badges_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
ignored_users_summary_spec.rb DEV: Update the rubocop-discourse gem 2023-06-26 11:41:52 +02:00
index_user_fields_for_search_spec.rb FIX: Move user reindexing into a job (#26753) 2024-04-25 20:58:34 +08:00
invalidate_inactive_admins_spec.rb DEV: Update the rubocop-discourse gem 2023-06-26 11:41:52 +02:00
invite_email_spec.rb DEV: Fix various rubocop lints (#24749) 2023-12-06 23:25:00 +01:00
jobs_base_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
jobs_scheduled_spec.rb DEV: Support a perform_when_readonly option for Jobs::Scheduled (#30478) 2024-12-27 09:42:40 +08:00
jobs_spec.rb FIX: Redo Sidekiq monitoring to restart stuck sidekiq processes (#30198) 2024-12-18 12:48:50 +08:00
mass_award_badge_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
notify_category_change_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
notify_mailing_list_subscribers_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
notify_moved_posts_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
notify_reviewable_spec.rb FEATURE: Support designating multiple groups as mods on category (#28655) 2024-09-04 04:38:46 +03:00
notify_tag_change_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
old_keys_reminder_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
open_topic_spec.rb DEV: rename topic_id to timerable_id for BaseTimer (#34667) 2025-09-17 13:19:17 +08:00
pending_queued_posts_reminder_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
pending_reviewables_reminder_spec.rb DEV: Convert min_trust_to_flag_posts setting to groups (#24864) 2023-12-13 17:18:42 +08:00
pending_users_reminder_spec.rb DEV: Remove full group refreshes from tests (#25414) 2024-01-25 14:28:26 +08:00
periodical_updates_spec.rb DEV: ensure rebaking works even when some users have inconsistent data (#30261) 2024-12-16 19:48:25 +08:00
poll_mailbox_spec.rb DEV: Database backed admin notices (#26192) 2024-05-23 09:29:08 +08:00
post_update_topic_tracking_state_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
post_uploads_recovery_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
process_bulk_invite_emails_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
process_email_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
process_localized_cooked_spec.rb FEATURE: Also process manually updated translations (#35276) 2025-10-09 00:33:36 +08:00
process_post_spec.rb DEV: Clean up scope resolution operators in plugins (#34979) 2025-09-30 14:36:34 +02:00
process_shelved_notifications_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
publish_topic_to_category_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
pull_hotlinked_images_spec.rb DEV: Clean up scope resolution operators in plugins (#34979) 2025-09-30 14:36:34 +02:00
pull_user_profile_hotlinked_images_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
purge_expired_ignored_users_spec.rb DEV: Update the rubocop-discourse gem 2023-06-26 11:41:52 +02:00
push_notification_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
rebake_custom_emoji_posts_spec.rb DEV: Apply syntax_tree formatting to spec/* 2023-01-09 11:49:28 +00:00
redeliver_web_hook_events_spec.rb DEV: Specs for redeliver_web_hook_events job (#27779) 2024-07-09 10:35:10 -05:00
refresh_users_reviewable_counts_spec.rb FEATURE: Support designating multiple groups as mods on category (#28655) 2024-09-04 04:38:46 +03:00
reindex_search_spec.rb FEATURE: Add full-text search for chat messages (#34704) 2025-10-22 11:30:35 +02:00
remove_banner_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
reviewable_priorities_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
run_problem_check_spec.rb DEV: Fix job serialization warnings (#30735) 2025-01-13 13:35:40 +01:00
run_problem_checks_spec.rb FIX: Run scheduled problem checks even when no tracker exists yet (#35102) 2025-10-01 10:21:44 -03:00
send_push_notification_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
send_system_message_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
suspicious_login_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00
sync_access_control_for_uploads_spec.rb DEV: Add setting to tag s3 objects enabling tag based access control policies (#32988) 2025-06-16 13:26:18 +08:00
sync_topic_user_bookmarked_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
tl3_promotions_spec.rb DEV: Add tl3_custom_promotions plugin modifier to tl3_promotions.rb (#29834) 2024-11-22 15:28:43 -05:00
toggle_topic_closed_spec.rb DEV: rename topic_id to timerable_id for BaseTimer (#34667) 2025-09-17 13:19:17 +08:00
topic_timer_enqueuer_spec.rb DEV: Update rubocop-discourse to 3.13 and autofix issues (#35073) 2025-10-06 16:11:01 +02:00
truncate_user_flag_stats_spec.rb DEV: Convert min_trust_to_flag_posts setting to groups (#24864) 2023-12-13 17:18:42 +08:00
unsilence_users_spec.rb Add RSpec 4 compatibility (#17652) 2022-07-28 10:27:38 +08:00
update_animated_uploads_spec.rb Add RSpec 4 compatibility (#17652) 2022-07-28 10:27:38 +08:00
update_gravatar_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
update_topic_hot_scores_spec.rb DEV: Clean up scope resolution operators in plugins (#34979) 2025-09-30 14:36:34 +02:00
update_username_spec.rb DEV: Allow fab! without block (#24314) 2023-11-09 16:47:59 -06:00
user_email_spec.rb DEV: add shortcut fab!(:variable, :fabricator) to specs (#33577) 2025-07-11 11:16:34 -03:00