Deep Dive ยท Search Algorithm

3 Stages, No Vector DB

Weighting logic, stopword filter, snippet extraction and BFS path finding in detail.

01
Stage 1: Quick Search โ€” Weighted Metadata

Tag matches ร—3, title matches ร—2, substring ร—1/ร—0.5. Relevance boost: critical +30%, high +20%. Stopwords (de+en) removed before searching.

# Quick Search Scoring
score = 0.0
tag_matches = query_words & tags # ร— 3.0
title_matches = query_words & title_words # ร— 2.0
partial_tag # ร— 1.0
partial_title # ร— 0.5

boost = {"critical": 1.5, "high": 1.0, "medium": 0.5}
score *= (1 + boost[relevance] * 0.2)
02
Stage 2: Deep Search โ€” Full-Text with Diminishing Returns

Only activated when Quick Search finds fewer than --limit results. Counts word occurrences, capped at 5. Phrase bonus when query words appear adjacent.

for qw in query_words:
  count = content_lower.count(qw)
  score += min(count, 5) * 0.5

# Phrase bonus
if "database performance" in content_lower:
  score += 2.0
03
Stage 3: Relations โ€” The Graph Answers Too

With --with-relations, cm_search imports get_node_relations from cm_relate. Each result shows its incoming and outgoing edges. Max 5 relations per result.

if args.with_relations:
  from cm_relate import get_node_relations
  rels = get_node_relations(ws, result_id)
  for rel in rels["outgoing"][:5]:
    print(f" {icon} โ”€โ”€{label}โ”€โ”€โ†’ [{rel.to}]")
04
BFS Path Finding โ€” Shortest Route Through the Graph

find_path() builds an adjacency list and runs standard BFS. Special: also traverses reverse direction (marked with ~). Max depth: 6 hops.

# BFS in cm_relate.py
adj = {}
for edge in edges:
  adj[edge["from"]].append((edge["to"], rel))
  adj[edge["to"]].append((edge["from"], f"~{rel}"))

queue = deque([(from_id, [(from_id, None)])])
while queue:
  current, path = queue.popleft()
  if current == to_id: return path
  for neighbor, rel in adj[current]:
    queue.append(...)