[object Object]

← back to Ventura Corridor

iter 172: /api/magazine/similar/:id — pg_trgm word_similarity over first 480 chars; surfaces editorial-body templating beyond headline-level

317fdcfc39e70ef911a2b4b551997e756e9e86b8 · 2026-05-06 20:19:05 -0700 · SteveStudio2

Files touched

Diff

commit 317fdcfc39e70ef911a2b4b551997e756e9e86b8
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 6 20:19:05 2026 -0700

    iter 172: /api/magazine/similar/:id — pg_trgm word_similarity over first 480 chars; surfaces editorial-body templating beyond headline-level
---
 src/server/index.ts | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/src/server/index.ts b/src/server/index.ts
index 2876f1e..4d430a9 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1934,6 +1934,43 @@ app.get('/api/magazine/random', async (req, res) => {
   }
 });
 
+// Editorial similarity — finds features whose bodies most resemble the given feature.
+// Uses pg_trgm word_similarity over the first 480 chars (avoids huge cross-products).
+// Use case: detect when qwen3 reuses similar sentence structure across features
+// (deeper signal than exact-duplicate or trigram detection).
+app.get('/api/magazine/similar/:id', async (req, res) => {
+  try {
+    const id = parseInt(req.params.id, 10);
+    const limit = Math.min(20, Math.max(1, parseInt(String(req.query.limit || 5), 10) || 5));
+    if (!Number.isFinite(id)) return res.status(400).json({ error: 'bad id' });
+    const seed = await query(`SELECT editorial, headline FROM magazine_features WHERE id = $1`, [id]);
+    if (seed.rowCount === 0) return res.status(404).json({ error: 'feature not found' });
+    const seedText = String(seed.rows[0].editorial || '').slice(0, 480);
+    if (!seedText) return res.json({ id, neighbors: [] });
+
+    const r = await query(`
+      SELECT mf.id, mf.headline, mf.subhead, mf.category_tag, mf.status,
+             b.name AS biz_name,
+             round(similarity(LEFT(mf.editorial, 480), $2)::numeric, 3) AS similarity
+      FROM magazine_features mf
+      JOIN businesses b ON b.id = mf.business_id
+      WHERE mf.id != $1
+        AND mf.editorial IS NOT NULL
+        AND length(mf.editorial) > 100
+      ORDER BY LEFT(mf.editorial, 480) <-> $2 ASC
+      LIMIT $3
+    `, [id, seedText, limit]);
+
+    res.json({
+      id,
+      seed_headline: seed.rows[0].headline,
+      neighbors: r.rows.map((n: any) => ({ ...n, permalink: `/magazine/${n.id}` })),
+    });
+  } catch (e: any) {
+    res.status(500).json({ error: e.message });
+  }
+});
+
 // Calendar — features generated/published per day for the last N days.
 // Powers /calendar.html GitHub-style heatmap.
 app.get('/api/magazine/calendar', async (req, res) => {

← a55be3f iter 171: SESSION_LOG_2026-05-06.md — 40-iter overnight debr  ·  back to Ventura Corridor  ·  iter 173: auto-dedupe now also catches trigram-templated hea 88857c9 →