fix(guild): single-pass bucket filter in GuildRepository.SearchAsync
Replace double-correlated COUNT subqueries in the member-bucket switch with a project-filter-project pattern that computes the count once per row. Add SearchAsync_bucket_2_matches_guilds_with_11_to_25_members to cover the previously untested medium-bucket range. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -53,18 +53,18 @@ public sealed class GuildRepository : IGuildRepository
|
|||||||
if (joinCondition != 0)
|
if (joinCondition != 0)
|
||||||
q = q.Where(g => (int)g.JoinCondition == joinCondition);
|
q = q.Where(g => (int)g.JoinCondition == joinCondition);
|
||||||
|
|
||||||
if (memberBucket != 0)
|
if (memberBucket is 1 or 2 or 3)
|
||||||
{
|
{
|
||||||
q = memberBucket switch
|
var (lo, hi) = memberBucket switch
|
||||||
{
|
{
|
||||||
1 => q.Where(g => _db.GuildMembers.Count(m => m.GuildId == g.GuildId) >= 1
|
1 => (1, 10),
|
||||||
&& _db.GuildMembers.Count(m => m.GuildId == g.GuildId) <= 10),
|
2 => (11, 25),
|
||||||
2 => q.Where(g => _db.GuildMembers.Count(m => m.GuildId == g.GuildId) >= 11
|
_ => (26, maxMemberCap),
|
||||||
&& _db.GuildMembers.Count(m => m.GuildId == g.GuildId) <= 25),
|
|
||||||
3 => q.Where(g => _db.GuildMembers.Count(m => m.GuildId == g.GuildId) >= 26
|
|
||||||
&& _db.GuildMembers.Count(m => m.GuildId == g.GuildId) <= maxMemberCap),
|
|
||||||
_ => q,
|
|
||||||
};
|
};
|
||||||
|
q = q
|
||||||
|
.Select(g => new { Guild = g, MemberCount = _db.GuildMembers.Count(m => m.GuildId == g.GuildId) })
|
||||||
|
.Where(x => x.MemberCount >= lo && x.MemberCount <= hi)
|
||||||
|
.Select(x => x.Guild);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await q.Take(resultCap).ToListAsync(ct);
|
return await q.Take(resultCap).ToListAsync(ct);
|
||||||
|
|||||||
@@ -68,4 +68,29 @@ public class GuildRepositoryTests
|
|||||||
var small = await repo.SearchAsync("", 0, 0, 1, 30, 50);
|
var small = await repo.SearchAsync("", 0, 0, 1, 30, 50);
|
||||||
Assert.That(small.Select(x => x.GuildId), Has.No.Member(bigId));
|
Assert.That(small.Select(x => x.GuildId), Has.No.Member(bigId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task SearchAsync_bucket_2_matches_guilds_with_11_to_25_members()
|
||||||
|
{
|
||||||
|
await using var db = Db();
|
||||||
|
var repo = new GuildRepository(db);
|
||||||
|
|
||||||
|
// Guild with 15 members — should appear in bucket 2
|
||||||
|
var midId = 10;
|
||||||
|
await repo.AddAsync(new GuildEntity { GuildId = midId, Name = "Mid", Activity = GuildActivity.All, JoinCondition = GuildJoinCondition.Free, CreatedAt = DateTime.UtcNow });
|
||||||
|
for (int i = 0; i < 15; i++)
|
||||||
|
db.GuildMembers.Add(new GuildMember { GuildId = midId, ViewerId = 2000 + i, Role = GuildRole.Regular, JoinedAt = DateTime.UtcNow });
|
||||||
|
|
||||||
|
// Guild with 5 members — should appear in bucket 1 only
|
||||||
|
var smallId = 11;
|
||||||
|
await repo.AddAsync(new GuildEntity { GuildId = smallId, Name = "Small", Activity = GuildActivity.All, JoinCondition = GuildJoinCondition.Free, CreatedAt = DateTime.UtcNow });
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
db.GuildMembers.Add(new GuildMember { GuildId = smallId, ViewerId = 3000 + i, Role = GuildRole.Regular, JoinedAt = DateTime.UtcNow });
|
||||||
|
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
var bucket2 = await repo.SearchAsync("", 0, 0, 2, 30, 50);
|
||||||
|
Assert.That(bucket2.Select(x => x.GuildId), Has.Member(midId), "15-member guild should be in bucket 2");
|
||||||
|
Assert.That(bucket2.Select(x => x.GuildId), Has.No.Member(smallId), "5-member guild must not appear in bucket 2");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user