perf: avoid full archive scans in statistics
This commit is contained in:
@@ -11,7 +11,7 @@ async function loadStatsPage() {
|
||||
.map(r => `<tr><td>${escapeHtml(r.source)}</td><td style="text-align:right; padding-left:24px">${r.n.toLocaleString()}</td></tr>`).join("");
|
||||
|
||||
document.getElementById("statusTable").innerHTML = data.byStatus
|
||||
.map(r => `<tr><td>${badgeHtml(r.status === "null" ? null : r.status)}</td><td style="text-align:right; padding-left:24px">${r.n.toLocaleString()}</td></tr>`).join("");
|
||||
.map(r => `<tr><td><span class="badge ${r.status === "ready" ? "ok" : "pending"}">${escapeHtml(r.status)}</span></td><td style="text-align:right; padding-left:24px">${r.n.toLocaleString()}</td></tr>`).join("");
|
||||
|
||||
document.getElementById("rate-ingested").textContent = (data.ingestedPerHour || 0).toLocaleString();
|
||||
document.getElementById("rate-content").textContent = (data.contentPerHour || 0).toLocaleString();
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Duriin Admin — Stats</title>
|
||||
<link rel="stylesheet" href="/admin/assets/css/base.css?v=20260804-3">
|
||||
<link rel="stylesheet" href="/admin/assets/css/layout.css?v=20260804-3">
|
||||
<link rel="stylesheet" href="/admin/assets/css/components.css?v=20260804-3">
|
||||
<link rel="stylesheet" href="/admin/assets/css/intel.css?v=20260804-3">
|
||||
<link rel="stylesheet" href="/admin/assets/css/base.css?v=20260804-4">
|
||||
<link rel="stylesheet" href="/admin/assets/css/layout.css?v=20260804-4">
|
||||
<link rel="stylesheet" href="/admin/assets/css/components.css?v=20260804-4">
|
||||
<link rel="stylesheet" href="/admin/assets/css/intel.css?v=20260804-4">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="section-heading">By content status</div>
|
||||
<div class="section-heading">By archive readiness</div>
|
||||
<div class="table-wrap" style="width:auto; min-width:180px">
|
||||
<table style="width:auto">
|
||||
<thead><tr><th>Status</th><th style="text-align:right">Count</th></tr></thead>
|
||||
@@ -74,7 +74,7 @@
|
||||
|
||||
<div id="toast"><span class="toast-dot"></span><span id="toast-msg"></span></div>
|
||||
|
||||
<script src="/admin/assets/js/app.js?v=20260804-3"></script>
|
||||
<script src="/admin/assets/js/stats.js?v=20260804-3"></script>
|
||||
<script src="/admin/assets/js/app.js?v=20260804-4"></script>
|
||||
<script src="/admin/assets/js/stats.js?v=20260804-4"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -5,20 +5,23 @@ try {
|
||||
const db = new Database(workerData.databasePath, { readonly: true, fileMustExist: true });
|
||||
const counts = db.prepare(`
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM articles) AS total,
|
||||
COALESCE((SELECT seq FROM sqlite_sequence WHERE name='articles'), 0) AS total,
|
||||
(SELECT COUNT(*) FROM article_embedding_meta) AS withContent,
|
||||
(SELECT COUNT(*) FROM article_embedding_meta) AS withEmbedding,
|
||||
(SELECT COUNT(*) FROM events) AS eventCount,
|
||||
COALESCE((SELECT seq FROM sqlite_sequence WHERE name='events'), 0) AS eventCount,
|
||||
(SELECT COUNT(*) FROM articles WHERE ingested_at >= datetime('now', '-1 hour')) AS ingestedPerHour,
|
||||
(SELECT COUNT(*) FROM article_embedding_meta WHERE embedded_at >= datetime('now', '-1 hour')) AS contentPerHour
|
||||
`).get();
|
||||
const bySource = db.prepare(`
|
||||
SELECT source, COUNT(*) AS n FROM articles GROUP BY source ORDER BY n DESC
|
||||
`).all();
|
||||
const byStatus = db.prepare(`
|
||||
SELECT COALESCE(content_status, 'null') AS status, COUNT(*) AS n
|
||||
FROM articles GROUP BY content_status ORDER BY n DESC
|
||||
`).all();
|
||||
// Avoid scanning the 3.7 GB article table for legacy content-status values.
|
||||
// Readiness is the useful operational distinction and is available from the
|
||||
// compact embedding metadata table.
|
||||
const byStatus = [
|
||||
{ status: 'ready', n: counts.withEmbedding },
|
||||
{ status: 'unprocessed', n: Math.max(0, counts.total - counts.withEmbedding) },
|
||||
];
|
||||
let embeddingsPerHour = 0;
|
||||
try {
|
||||
embeddingsPerHour = db.prepare(`
|
||||
|
||||
Reference in New Issue
Block a user