Dedup discover pool with aggregate to satisfy strict group by

pull/7465/head
Your Name 1 month ago
parent 2d263507ed
commit 71e8a93a71

@ -10,17 +10,22 @@ class DiscoverService
public static function getDailyIdPool()
{
$min_id = SnowflakeService::byDate(now()->subMonths(3));
$sqld = db_is_mysql_maria();
// One post per author (the newest, MAX(id)) so the pool is not
// dominated by prolific accounts. Selecting the aggregated MAX(id) with
// GROUP BY profile_id is valid under ONLY_FULL_GROUP_BY and portable
// across every driver — the previous `select id ... group by profile_id`
// threw 1055 on strict MySQL/MariaDB, and was gated off entirely on
// other drivers (no dedup at all). inRandomOrder() still shuffles the
// pool. This mirrors the MAX(id)+GROUP BY dedup used elsewhere.
return DB::table('statuses')
->selectRaw('MAX(id) as id')
->whereNull('uri')
->whereType('photo')
->whereIsNsfw(false)
->whereVisibility('public')
->when($sqld, function ($q, $sqld) {
return $q->groupBy('profile_id');
})
->where('id', '>', $min_id)
->groupBy('profile_id')
->inRandomOrder()
->take(300)
->pluck('id');

@ -0,0 +1,85 @@
<?php
use App\Models\Status;
use App\Models\User;
use App\Services\DiscoverService;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\Redis;
uses(LazilyRefreshDatabase::class);
/*
|--------------------------------------------------------------------------
| DiscoverService daily id pool
|--------------------------------------------------------------------------
|
| getDailyIdPool() de-duplicates to one photo per author. The old query did
| `select id ... group by profile_id`, which throws ERROR 1055 under
| ONLY_FULL_GROUP_BY (every strict MySQL/MariaDB deployment) and, on drivers
| where it did run, was gated off entirely so prolific authors flooded the
| pool. It now uses MAX(id)+GROUP BY: valid on every driver, one newest post
| per author.
|
*/
beforeEach(function () {
Redis::spy();
});
function publicPhoto(int $profileId): Status
{
return Status::factory()->create([
'profile_id' => $profileId,
'type' => 'photo',
'scope' => 'public',
'visibility' => 'public',
'is_nsfw' => false,
'uri' => null,
'local' => true,
]);
}
it('returns exactly one id per author', function () {
$author = User::factory()->create();
$author->refresh();
// Several public photos by the same author.
publicPhoto($author->profile_id);
publicPhoto($author->profile_id);
publicPhoto($author->profile_id);
$pool = DiscoverService::getDailyIdPool();
expect($pool)->toHaveCount(1);
});
it('keeps the newest post per author', function () {
$author = User::factory()->create();
$author->refresh();
$ids = collect([
publicPhoto($author->profile_id)->id,
publicPhoto($author->profile_id)->id,
publicPhoto($author->profile_id)->id,
]);
$newestId = $ids->max();
$pool = DiscoverService::getDailyIdPool();
expect($pool->map(fn ($id) => (int) $id)->all())->toBe([(int) $newestId]);
});
it('includes one id from each of several authors', function () {
$a = User::factory()->create();
$a->refresh();
$b = User::factory()->create();
$b->refresh();
publicPhoto($a->profile_id);
publicPhoto($a->profile_id);
publicPhoto($b->profile_id);
$pool = DiscoverService::getDailyIdPool()->map(fn ($id) => (int) $id);
expect($pool)->toHaveCount(2);
});
Loading…
Cancel
Save