ExplainVisualizer.RowCountIndicator.tsx 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import { ArrowRight } from 'lucide-react'
  2. import { Tooltip, TooltipContent, TooltipTrigger } from 'ui'
  3. interface RowCountIndicatorProps {
  4. actualRows?: number
  5. estimatedRows?: number
  6. rowsRemovedByFilter?: number
  7. }
  8. function formatRowCount(rows: number | undefined): string {
  9. if (rows === undefined) return '-'
  10. return rows.toLocaleString()
  11. }
  12. export function RowCountIndicator({
  13. actualRows,
  14. estimatedRows,
  15. rowsRemovedByFilter,
  16. }: RowCountIndicatorProps) {
  17. const hasActualRows = actualRows !== undefined
  18. const hasEstimatedRows = estimatedRows !== undefined
  19. const hasFilterData = rowsRemovedByFilter !== undefined && hasActualRows
  20. const totalRowsScanned = hasFilterData ? actualRows + rowsRemovedByFilter : undefined
  21. // Show filter flow: scanned → filtered out → remaining
  22. if (hasFilterData && totalRowsScanned !== undefined) {
  23. return (
  24. <Tooltip>
  25. <TooltipTrigger asChild>
  26. <div className="flex items-center gap-1.5 cursor-help">
  27. <span className="text-foreground-light">{formatRowCount(totalRowsScanned)}</span>
  28. <ArrowRight size={10} className="text-foreground-muted" />
  29. <span className="text-destructive-600 font-medium">
  30. -{formatRowCount(rowsRemovedByFilter)}
  31. </span>
  32. <ArrowRight size={10} className="text-foreground-muted" />
  33. <span className="text-brand font-medium">
  34. {formatRowCount(actualRows)} {actualRows === 1 ? 'row' : 'rows'}
  35. </span>
  36. </div>
  37. </TooltipTrigger>
  38. <TooltipContent side="top" className="max-w-xs font-sans">
  39. <p className="font-medium">Filter applied</p>
  40. <p className="text-foreground-lighter text-xs mt-1">
  41. {formatRowCount(totalRowsScanned)} rows were scanned,{' '}
  42. {formatRowCount(rowsRemovedByFilter)} were filtered out, leaving{' '}
  43. {formatRowCount(actualRows)} rows. Consider adding an index to reduce rows scanned.
  44. </p>
  45. </TooltipContent>
  46. </Tooltip>
  47. )
  48. }
  49. // Simple row count with actual rows
  50. if (hasActualRows) {
  51. return (
  52. <Tooltip>
  53. <TooltipTrigger asChild>
  54. <span className="text-foreground-light cursor-help">
  55. {formatRowCount(actualRows)} {actualRows === 1 ? 'row' : 'rows'}
  56. </span>
  57. </TooltipTrigger>
  58. <TooltipContent side="top" className="max-w-xs font-sans">
  59. <p className="font-medium">Rows returned</p>
  60. <p className="text-foreground-lighter text-xs mt-1">
  61. This operation processed and returned {formatRowCount(actualRows)} rows.
  62. </p>
  63. </TooltipContent>
  64. </Tooltip>
  65. )
  66. }
  67. // Only estimated rows available (EXPLAIN without ANALYZE)
  68. if (hasEstimatedRows) {
  69. return (
  70. <Tooltip>
  71. <TooltipTrigger asChild>
  72. <span className="text-foreground-muted cursor-help">
  73. est. {formatRowCount(estimatedRows)} {estimatedRows === 1 ? 'row' : 'rows'}
  74. </span>
  75. </TooltipTrigger>
  76. <TooltipContent side="top" className="max-w-xs font-sans">
  77. <p className="font-medium">Estimated rows</p>
  78. <p className="text-foreground-lighter text-xs mt-1">
  79. The planner estimates this operation will return {formatRowCount(estimatedRows)} rows.
  80. Run with EXPLAIN ANALYZE to see actual row counts.
  81. </p>
  82. </TooltipContent>
  83. </Tooltip>
  84. )
  85. }
  86. return <span className="text-foreground-muted">-</span>
  87. }