id); } /** * Queue an artwork for re-indexing after an update. */ public function update(Artwork $artwork): void { IndexArtworkJob::dispatch($artwork->id); } /** * Queue removal of an artwork from the index. */ public function delete(int $id): void { DeleteArtworkFromIndexJob::dispatch($id); } /** * Rebuild the entire artworks index in background chunks. * Run via: php artisan artworks:search-rebuild * * @param Closure(int, int, int, int, int, int): void|null $onChunk * @return array{total:int, dispatched:int, chunks:int} */ public function rebuildAll(int $chunkSize = 500, ?Closure $onChunk = null, bool $reverse = false, ?int $limit = null): array { $query = Artwork::query() ->public() ->published(); if ($reverse) { $query->orderByDesc('id'); } else { $query->orderBy('id'); } if ($limit !== null) { $query->limit($limit); } $total = (clone $query)->count(); $dispatched = 0; $chunks = 0; $query ->with(['user', 'tags', 'categories', 'stats', 'awardStat']) ->chunk($chunkSize, function ($artworks) use (&$chunks, &$dispatched, $total, $onChunk): void { $chunks++; $count = $artworks->count(); $firstId = (int) ($artworks->first()?->id ?? 0); $lastId = (int) ($artworks->last()?->id ?? 0); foreach ($artworks as $artwork) { IndexArtworkJob::dispatch($artwork->id); $dispatched++; } if ($onChunk !== null) { $onChunk($chunks, $count, $dispatched, $total, $firstId, $lastId); } }); Log::info('ArtworkSearchIndexer::rebuildAll — jobs dispatched', [ 'total' => $total, 'dispatched' => $dispatched, 'chunks' => $chunks, 'chunk_size' => $chunkSize, 'reverse' => $reverse, 'limit' => $limit, ]); return [ 'total' => $total, 'dispatched' => $dispatched, 'chunks' => $chunks, ]; } }