Reverse-geocode a general area label for logged searches
Each search now stores an `area_label` (district/town) reverse-geocoded from its coarsened ~1km lat/lng bucket via postcodes.io, surfaced in the Filament Searches admin as a sortable/searchable column plus an area filter. Geocoding is cached 30 days per bucket, queries a 2km radius so low-density buckets still match the default 100m miss, and fails gracefully to null. Adds `searches:backfill-areas` (scheduled hourly) to label existing rows and retry stragglers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
54
app/Console/Commands/BackfillSearchAreas.php
Normal file
54
app/Console/Commands/BackfillSearchAreas.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Search;
|
||||
use App\Services\PostcodeService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class BackfillSearchAreas extends Command
|
||||
{
|
||||
protected $signature = 'searches:backfill-areas {--limit=0 : Max distinct areas to resolve this run (0 = no limit)}';
|
||||
|
||||
protected $description = 'Reverse-geocode searches that have no area_label yet';
|
||||
|
||||
public function handle(PostcodeService $postcodes): int
|
||||
{
|
||||
$limit = (int) $this->option('limit');
|
||||
|
||||
$buckets = Search::query()
|
||||
->whereNull('area_label')
|
||||
->select('lat_bucket', 'lng_bucket')
|
||||
->distinct()
|
||||
->when($limit > 0, fn ($query) => $query->limit($limit))
|
||||
->get();
|
||||
|
||||
if ($buckets->isEmpty()) {
|
||||
$this->info('No searches need an area label.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$resolved = 0;
|
||||
$rowsUpdated = 0;
|
||||
|
||||
foreach ($buckets as $bucket) {
|
||||
$label = $postcodes->reverseResolve((float) $bucket->lat_bucket, (float) $bucket->lng_bucket);
|
||||
|
||||
if ($label === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$resolved++;
|
||||
$rowsUpdated += Search::query()
|
||||
->whereNull('area_label')
|
||||
->where('lat_bucket', $bucket->lat_bucket)
|
||||
->where('lng_bucket', $bucket->lng_bucket)
|
||||
->update(['area_label' => $label]);
|
||||
}
|
||||
|
||||
$this->info("Resolved {$resolved} of {$buckets->count()} areas, updated {$rowsUpdated} search rows.");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user