openvibers/openvibe.live Commits

Search suggestions: one per type and title (many VODs share a title), the best-ranked

OP OpenVibers committed 26 Sept 2026 · fbc784645e · 2 files, +12 −2


Changes

ddiff --git a/server/search/routes.js b/server/search/routes.js
iindex 73829290..b743f930 100644
---- a/server/search/routes.js
++++ b/server/search/routes.js
@@@ -60,7 +60,17 @@ function createSearchRouter({ fetchImpl = globalThis.fetch, baseUrl = SEARCH_URL
const r = await fetchImpl(`${baseUrl}/api/v1/suggest?${params}`, { headers: { accept: 'application/json' }, signal: AbortSignal.timeout(2000) });
const body = r.ok ? await r.json().catch(() => null) : null;
const list = body && Array.isArray(body.suggestions) ? body.suggestions : [];
-- res.json({ suggestions: list.filter((s) => s && TYPES.has(s.type) && s.title).map((s) => ({ type: s.type, id: s.id, title: s.title, canonical_url: s.canonical_url })) });
++ // Many VODs share a title ("<name>'s Stream"): one suggestion per type and title, the best-ranked.
++ const seen = new Set();
++ const out = [];
++ for (const s of list) {
++ if (!s || !TYPES.has(s.type) || !s.title) continue;
++ const key = `${s.type}\u0000${String(s.title).toLowerCase()}`;
++ if (seen.has(key)) continue;
++ seen.add(key);
++ out.push({ type: s.type, id: s.id, title: s.title, canonical_url: s.canonical_url });
++ }
++ res.json({ suggestions: out });
} catch {
res.json({ suggestions: [] }); // typing never shows an error; submitting does
}
ddiff --git a/test/search-page.test.js b/test/search-page.test.js
iindex ea1dc92e..59eb9796 100644
---- a/test/search-page.test.js
++++ b/test/search-page.test.js
@@@ -19,7 +19,7 @@ const { createSearchRouter } = require('../server/search/routes');
const u = new URL(url);
if (u.pathname === '/api/v1/suggest') {
if (mode === 'suggest-down') throw new Error('timeout');
-- return new Response(JSON.stringify({ suggestions: [{ owner: 'live', type: 'channel', id: '7', title: 'Forum builders', canonical_url: 'https://openvibe.live/@forum' }, { owner: 'community', type: 'thread', id: 'x', title: 'Not ours' }] }), { status: 200 });
++ return new Response(JSON.stringify({ suggestions: [{ owner: 'live', type: 'channel', id: '7', title: 'Forum builders', canonical_url: 'https://openvibe.live/@forum' }, { owner: 'community', type: 'thread', id: 'x', title: 'Not ours' }, { owner: 'live', type: 'channel', id: '8', title: 'forum builders', canonical_url: 'https://openvibe.live/@forum2' }] }), { status: 200 });
}
const facets = u.searchParams.get('facets') ? { facets: { category: [{ value: 'irl', count: 12 }, { value: 'gaming', count: 3 }], channel: [{ value: 'alex', count: 9 }, { value: 42, count: 1 }], secret: [{ value: 'x', count: 1 }] } } : {};
return new Response(JSON.stringify({ ...facets, results: [{ owner: 'live', type: 'vod', id: '912', title: 'Building a forum', canonical_url: 'https://openvibe.live/vod/912', facets: { channel: 'alex' }, snippet_html: '<mark>forum</mark>', acl: ['secret'], revision: 3 }], next_cursor: 'c1' }), { status: 200 });