diff --git a/app/features/builds/BuildRepository.server.ts b/app/features/builds/BuildRepository.server.ts index 332b829ff..0441ef29a 100644 --- a/app/features/builds/BuildRepository.server.ts +++ b/app/features/builds/BuildRepository.server.ts @@ -320,65 +320,23 @@ export async function allByWeaponId( const { limit, sortAbilities: shouldSortAbilities = false } = options; const weaponIds = weaponIdToArrayWithAlts(weaponId); - let rows: Awaited>; + // For weapons with alts, run separate queries and merge. + // This allows each query to use the covering index for ordering, + // which is ~6x faster than using IN with multiple values. + const allResults = await Promise.all( + weaponIds.map((id) => buildsByWeaponIdQuery(id, limit)), + ); - if (weaponIds.length === 1) { - rows = await buildsByWeaponIdQuery(weaponIds[0], limit); - } else { - // For weapons with alts, run separate queries and merge. - // This allows each query to use the covering index for ordering, - // which is ~6x faster than using IN with multiple values. - const allResults = await Promise.all( - weaponIds.map((id) => buildsByWeaponIdQuery(id, limit)), - ); - - const seenBuildIds = new Set(); - type BuildRow = Awaited>[number]; - const merged: BuildRow[] = []; - - // Merge results maintaining sort order (tier asc, isTop500 desc, updatedAt desc) - // Since each query returns sorted results, we can interleave them - const pointers = allResults.map(() => 0); - - while (merged.length < limit) { - let bestIdx = -1; - let bestRow: BuildRow | null = null; - - for (let i = 0; i < allResults.length; i++) { - while ( - pointers[i] < allResults[i].length && - seenBuildIds.has(allResults[i][pointers[i]].id) - ) { - pointers[i]++; - } - - if (pointers[i] >= allResults[i].length) continue; - - const row = allResults[i][pointers[i]]; - - if ( - !bestRow || - row.bwTier < bestRow.bwTier || - (row.bwTier === bestRow.bwTier && - row.bwIsTop500 > bestRow.bwIsTop500) || - (row.bwTier === bestRow.bwTier && - row.bwIsTop500 === bestRow.bwIsTop500 && - row.bwUpdatedAt > bestRow.bwUpdatedAt) - ) { - bestIdx = i; - bestRow = row; - } - } - - if (bestIdx === -1 || !bestRow) break; - - seenBuildIds.add(bestRow.id); - merged.push(bestRow); - pointers[bestIdx]++; - } - - rows = merged; - } + const rows = R.pipe( + allResults.flat(), + R.sortBy( + (row) => row.bwTier, + [(row) => row.bwIsTop500, "desc"], + [(row) => row.bwUpdatedAt, "desc"], + ), + R.uniqueBy((row) => row.id), + R.take(limit), + ); return rows.map((row) => { const abilities = dbAbilitiesToArrayOfArrays(row.abilities);