Skip to content

Commit 2cbf68d

Browse files
committed
fix: harden related concept suggestions
1 parent 720d6ae commit 2cbf68d

4 files changed

Lines changed: 40 additions & 16 deletions

File tree

backend/main.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,7 +1556,7 @@ def related(
15561556
q: str = Query(..., min_length=1, max_length=200),
15571557
limit: int = Query(8, ge=1, le=20),
15581558
):
1559-
"""Find concepts that co-occur with the query term."""
1559+
"""Find recognized concepts that co-occur with the query term."""
15601560
con = get_db()
15611561
try:
15621562
clean_q = re.sub(r'[^\w\u4e00-\u9fff\s]', '', q).strip()
@@ -1565,7 +1565,7 @@ def related(
15651565

15661566
# Get text chunks matching the query
15671567
rows = con.execute("""
1568-
SELECT c.text
1568+
SELECT c.subject, c.text
15691569
FROM chunks c
15701570
JOIN chunks_fts f ON c.id = f.rowid
15711571
WHERE chunks_fts MATCH ?
@@ -1575,26 +1575,26 @@ def related(
15751575
if not rows:
15761576
return []
15771577

1578-
# Extract Chinese word candidates (2-4 char sequences) from matching chunks
1578+
# Aggregate only exact concept hits already recognized by concept_map.
15791579
word_counter = Counter()
15801580
query_chars = set(clean_q)
15811581
for r in rows:
15821582
text = r["text"] or ""
1583-
# Find Chinese word-like sequences (2-4 chars)
1584-
words = re.findall(r'[\u4e00-\u9fff]{2,4}', text)
1585-
for w in words:
1586-
# Skip if the word is part of the query or too generic
1583+
subject = r["subject"] or ""
1584+
for concept in _match_concepts(text, subject, con):
1585+
w = concept["concept"]
15871586
if w == clean_q or w in clean_q or clean_q in w:
15881587
continue
1588+
if w in GRAPH_GENERIC_TERMS:
1589+
continue
15891590
if len(w) < 2:
15901591
continue
15911592
word_counter[w] += 1
15921593

1593-
# Return top co-occurring terms (appearing in multiple chunks)
15941594
candidates = [
15951595
{"term": term, "count": count}
15961596
for term, count in word_counter.most_common(limit * 3)
1597-
if count >= 2 # must appear in at least 2 chunks
1597+
if count >= 2
15981598
][:limit]
15991599

16001600
return candidates

frontend/assets/app.js

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ async function doSearch(q) {
609609
currentData = await res.json();
610610
renderResults(currentData);
611611
// Load related concepts
612-
loadRelated(q);
612+
loadRelated(currentData, q);
613613
// Refresh trending after search (new query logged)
614614
setTimeout(() => loadTrending(), 500);
615615
// Show concept subgraph for the search term
@@ -620,10 +620,29 @@ async function doSearch(q) {
620620
}
621621

622622
// ── Related Concepts ──────────────────────────────────────
623-
async function loadRelated(q) {
623+
async function loadRelated(searchData, q) {
624624
try {
625-
const res = await fetch(`${API}/api/related?q=${encodeURIComponent(q)}&limit=10`);
626-
const data = await res.json();
625+
const conceptCounts = new Map();
626+
const groups = Array.isArray(searchData?.groups) ? searchData.groups : [];
627+
for (const item of groups) {
628+
const concepts = Array.isArray(item?.matched_concepts) ? item.matched_concepts : [];
629+
for (const concept of concepts) {
630+
const term = String(concept || '').trim();
631+
if (!term || term === q || term.includes(q) || q.includes(term)) continue;
632+
conceptCounts.set(term, (conceptCounts.get(term) || 0) + 1);
633+
}
634+
}
635+
636+
let data = Array.from(conceptCounts.entries())
637+
.sort((a, b) => b[1] - a[1] || a[0].length - b[0].length)
638+
.slice(0, 10)
639+
.map(([term, count]) => ({ term, count }));
640+
641+
if (data.length === 0) {
642+
const res = await fetch(`${API}/api/related?q=${encodeURIComponent(q)}&limit=10`);
643+
data = await res.json();
644+
}
645+
627646
if (data.length > 0) {
628647
relatedBarEl.innerHTML = `
629648
<span class="related-label">🔗 相关概念:</span>

frontend/assets/version.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
{
2-
"frontend_refactor_version": "2026.03.06-r9",
2+
"frontend_refactor_version": "2026.03.06-r10",
33
"updated_at": "2026-03-06",
44
"history": [
5+
{
6+
"version": "2026.03.06-r10",
7+
"date": "2026-03-06",
8+
"summary": "search related concepts now aggregate recognized matched_concepts instead of broken 2-4 character fragments"
9+
},
510
{
611
"version": "2026.03.06-r9",
712
"date": "2026-03-06",

frontend/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<script defer src="https://unpkg.com/katex@0.16.9/dist/contrib/auto-render.min.js"></script>
2424
<script src="https://d3js.org/d3.v7.min.js"></script>
2525

26-
<link rel="stylesheet" href="assets/style.css?v=20260306h">
26+
<link rel="stylesheet" href="assets/style.css?v=20260306i">
2727
</head>
2828

2929
<body>
@@ -286,7 +286,7 @@ <h3>🔓 开源与反馈</h3>
286286
<p id="footer-version-line">AI 高中教材 · 开源项目 · MIT License · 前端版本加载中…</p>
287287
</footer>
288288
</div>
289-
<script src="assets/app.js?v=20260306h"></script>
289+
<script src="assets/app.js?v=20260306i"></script>
290290
</body>
291291

292292
</html>

0 commit comments

Comments
 (0)