From 01f0ad3a40c0071a23239a346566c0a021d3cce9 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Tue, 12 May 2026 17:32:55 +0200 Subject: [PATCH] feat(diag): Advanced Search mit AND/OR + mehrere Begriffe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Klappbares Panel unter dem Suchbalken — Stefan kann bis zu 3 Begriffe eingeben und mit AND/OR verknuepfen, links nach rechts ausgewertet. Backend bleibt simpel: pro Begriff einmal /memory/search-text aufgerufen, die Treffer-Set-IDs werden client-seitig per AND (intersect) oder OR (union) kombiniert. UI: - "⌃ Erweitert" Button rechts neben ✕ klappt das Panel auf - 3 Eingabefelder mit 2 Operator-Dropdowns dazwischen (UND/ODER) - "Suchen"-Button im Panel - "Felder leeren" reseted - Leere Felder werden ignoriert — sind nur 2 belegt, gibt's nur 1 Operator - Typ-Filter aus dem Hauptbalken wird mit angewandt - Info-Banner zeigt die kombinierte Suchformel zurueck Co-Authored-By: Claude Opus 4.7 (1M context) --- diagnostic/index.html | 116 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/diagnostic/index.html b/diagnostic/index.html index ad4224f..d510269 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -852,8 +852,32 @@ + +
@@ -3451,6 +3475,98 @@ const p = document.getElementById('brain-filter-pinned'); if (p) p.value = 'all'; const info = document.getElementById('brain-search-info'); if (info) info.style.display = 'none'; brainSearchIds = null; + clearAdvancedSearch(); + } + + function toggleAdvancedSearch() { + const panel = document.getElementById('brain-advanced-panel'); + const btn = document.getElementById('btn-advanced-search'); + if (!panel) return; + const open = panel.style.display !== 'none'; + panel.style.display = open ? 'none' : 'block'; + if (btn) btn.textContent = open ? '⌃ Erweitert' : '⌄ Einklappen'; + } + + function clearAdvancedSearch() { + ['adv-term-1','adv-term-2','adv-term-3'].forEach(id => { + const el = document.getElementById(id); if (el) el.value = ''; + }); + } + + /** Mehrere Volltext-Suchen + Boolean-Kombination (links nach rechts). + * Backend bleibt simpel — wir machen N parallele search-text-Calls + * und kombinieren die ID-Mengen client-seitig per AND/OR. */ + async function runAdvancedSearch() { + const terms = [ + document.getElementById('adv-term-1')?.value?.trim() || '', + document.getElementById('adv-term-2')?.value?.trim() || '', + document.getElementById('adv-term-3')?.value?.trim() || '', + ]; + const ops = [ + document.getElementById('adv-op-1')?.value || 'AND', + document.getElementById('adv-op-2')?.value || 'AND', + ]; + const info = document.getElementById('brain-search-info'); + // Felder mit Inhalt zusammen mit dem ops-Op DAVOR sammeln + const active = []; + for (let i = 0; i < 3; i++) { + if (terms[i]) active.push({ term: terms[i], op: i === 0 ? null : ops[i - 1] }); + } + if (active.length === 0) { + if (info) info.style.display = 'none'; + loadBrainMemoryList(); + return; + } + + const typeFilter = document.getElementById('brain-filter-type').value; + const baseParams = { k: '500', include_pinned: 'true' }; + if (typeFilter) baseParams.type = typeFilter; + + try { + // Pro Begriff einmal Backend fragen, dann Map + Set + const sets = []; + for (const a of active) { + const params = new URLSearchParams({ ...baseParams, q: a.term }); + const r = await fetch('/api/brain/memory/search-text?' + params.toString()); + if (!r.ok) throw new Error('HTTP ' + r.status); + const hits = await r.json(); + hits.forEach(m => { brainMemoryCache[m.id] = m; }); + sets.push(new Set(hits.map(m => m.id))); + } + + // Links-nach-rechts kombinieren mit den Operatoren + let combined = sets[0]; + for (let i = 1; i < sets.length; i++) { + const op = active[i].op; + if (op === 'AND') { + combined = new Set([...combined].filter(id => sets[i].has(id))); + } else { + combined = new Set([...combined, ...sets[i]]); + } + } + + const hits = Array.from(combined).map(id => brainMemoryCache[id]).filter(Boolean); + brainSearchIds = hits.map(m => m.id); + const desc = active.map((a, i) => i === 0 ? `"${a.term}"` : ` ${a.op} "${a.term}"`).join(''); + if (info) { + info.style.display = 'block'; + if (hits.length === 0) { + info.innerHTML = `🔍 Keine Treffer fuer ${escapeHtml(desc)}` + + (typeFilter ? ` · Typ=${escapeHtml(typeFilter)}` : '') + + ` · 📝 wortlich, Boolean-Kombi`; + } else { + info.innerHTML = `🔍 ${hits.length} Treffer fuer ${escapeHtml(desc)}` + + (typeFilter ? ` · Typ=${escapeHtml(typeFilter)}` : '') + + ` · 📝 wortlich, Boolean-Kombi`; + } + } + renderBrainList(hits, true); + } catch (e) { + if (info) { + info.style.display = 'block'; + info.innerHTML = `🔴 Erweiterte Suche fehlgeschlagen: ${escapeHtml(e.message)}`; + } + } } async function runBrainSearch() {