- Add relationships() to Seeder base class; DiscussionSeeder implements it to
return tags/recipientUsers/recipientGroups for eager loading
- UpdateSearchJob calls loadMissing(relationships()) before the map loop,
eliminating N×R lazy-load queries per batch
- Remove refresh:true from bulk call — synchronous ES refresh was the primary
throughput bottleneck during indexing
- Cache viewForum permissions in DiscussionSeeder per job instance, replacing
N×2 Permission queries per document with a single cached query
- Increase seeder batch size from 1000 to 2500
- Switch to ES parent/child join_field: discussion docs hold all metadata,
post docs hold content only. Routing ensures parent and child land on the
same shard. Removes the old flat-document approach where post data was
duplicated onto every comment document.
- Add HasChildQuery with inner_hits so the best-matching post ID is
surfaced as mostRelevantPost without a second DB query.
- Blue-green index rebuilds: --recreate writes into a timestamped pending
index, --swap atomically promotes it via alias. Interrupted builds are
resumable with --recreate --continue. --swap requires confirmation before
proceeding.
- Index hidden posts; non-moderators are filtered at query time via
is_hidden on the has_child clause. Core\Hidden and Core\Restored trigger
re-indexing so moderators with post.hide can search hidden posts.
- Rename SavingJob to UpdateSearchJob
- Add ViewsSearchJob for lightweight partial view_count updates
- Add probabilistic sync throttle in DiscussionSeeder::viewingOn()
to avoid an ES update on every page view
- Add view_count to mapping, DiscussionSeeder::toDocument(), and
translateSort
- Log a warning and skip instead of 500ing on unknown sort fields
content_partial was introduced alongside a partialMatch() query that
targeted it. That query was later switched to use the content field
instead, but content_partial was never removed from the seeders,
causing every document to store and index its content twice.