This commit is contained in:
Ross
2026-07-11 10:13:32 +01:00
parent 91633deb97
commit 36ca884b4f
461 changed files with 13276 additions and 299 deletions
+169
View File
@@ -6,6 +6,7 @@ Usage:
python tools/search_md.py --root docs_md/articles --query "breadcrumbs:Brain>Diagnosis" --out results.json
Supported query keys: breadcrumbs, authors, pageKeywords, category, title, enhancedTitle, type
You can also use key `global` to search across all relevant fields.
Breadcrumbs query format: 'A>B>C' (matches files whose breadcrumbs contain that sequence in order)
Authors query: substring match against author.value
Keywords: substring match against pageKeywords
@@ -101,6 +102,105 @@ def run_search(root: str, qkey: str, qval: str) -> List[Dict[str, Any]]:
if not fm:
continue
matched = False
# Helper to test non-breadcrumb simple fields using the same semantics
def match_field_value(v, qval_local: str) -> bool:
if v is None:
return False
# If v is a list, check each element for a match
if isinstance(v, list):
for elem in v:
s = str(elem)
if '*' in qval_local:
if fnmatch.fnmatchcase(s.lower(), qval_local.lower()):
return True
else:
if qval_local.strip().lower() in s.lower():
return True
return False
else:
s = str(v)
if '*' in qval_local:
return fnmatch.fnmatchcase(s.lower(), qval_local.lower())
else:
return qval_local.strip().lower() in s.lower()
# Helper to evaluate a single clause (key, value) against this file's frontmatter
def match_clause(cl_key: str, cl_val: str) -> bool:
cl_key = (cl_key or '').strip()
cl_val = (cl_val or '').strip()
if not cl_key:
cl_key = 'global'
if cl_key == 'breadcrumbs':
query_parts = [p.strip() for p in cl_val.split('>') if p.strip()]
bcs = fm.get('breadcrumbs') or []
if bcs and isinstance(bcs, list) and bcs and isinstance(bcs[0], dict):
bcs_list = [d.get('name') or d.get('slug') or '' for d in bcs]
else:
bcs_list = bcs
return match_breadcrumbs(bcs_list, query_parts)
elif cl_key == 'authors':
authors = fm.get('authors') or []
return match_author(authors, cl_val)
elif cl_key == 'pageKeywords':
pk = fm.get('pageKeywords') or ''
if isinstance(pk, list):
tokens = [str(x) for x in pk]
else:
tokens = [t.strip() for t in str(pk).split(',') if t.strip()]
if '*' in cl_val:
for t in tokens:
if fnmatch.fnmatchcase(t.lower(), cl_val.lower()):
return True
return False
else:
for t in tokens:
if cl_val.strip().lower() in t.lower():
return True
return False
elif cl_key == 'global':
q = cl_val or ''
# breadcrumbs
bcs = fm.get('breadcrumbs') or []
if isinstance(bcs, list) and bcs and isinstance(bcs[0], dict):
bcs_list = [d.get('name') or d.get('slug') or '' for d in bcs]
else:
bcs_list = bcs
if '>' in q:
query_parts = [p.strip() for p in q.split('>') if p.strip()]
if match_breadcrumbs(bcs_list, query_parts):
return True
else:
for b in bcs_list:
try:
if q.strip().lower() in str(b).lower():
return True
except Exception:
pass
# authors
if match_author(fm.get('authors') or [], q):
return True
# pageKeywords
pk = fm.get('pageKeywords') or ''
if isinstance(pk, list):
tokens = [str(x) for x in pk]
else:
tokens = [t.strip() for t in str(pk).split(',') if t.strip()]
if '*' in q:
for t in tokens:
if fnmatch.fnmatchcase(t.lower(), q.lower()):
return True
else:
for t in tokens:
if q.strip().lower() in t.lower():
return True
# other fields
for field in ['category', 'title', 'enhancedTitle', 'type', 'docid']:
if match_field_value(fm.get(field), q):
return True
return False
else:
return match_field_value(fm.get(cl_key), cl_val)
if qkey == 'breadcrumbs':
# qval format: A>B>C
query_parts = [p.strip() for p in qval.split('>') if p.strip()]
@@ -111,6 +211,75 @@ def run_search(root: str, qkey: str, qval: str) -> List[Dict[str, Any]]:
else:
bcs_list = bcs
matched = match_breadcrumbs(bcs_list, query_parts)
elif qkey == 'or':
# qval is a set of clauses separated by '||', '|', or ';' where each clause is key:val
# Example: "title:stroke|authors:Smith|global:keyword"
sep_norm = (qval or '').replace('||', '|').replace(';', '|')
clauses = [c.strip() for c in sep_norm.split('|') if c.strip()]
for cl in clauses:
if ':' in cl:
ck, cv = cl.split(':', 1)
else:
ck, cv = 'global', cl
try:
if match_clause(ck, cv):
matched = True
break
except Exception:
continue
elif qkey == 'global':
q = qval or ''
# breadcrumbs: if query contains '>' treat as sequence, else substring match
bcs = fm.get('breadcrumbs') or []
if isinstance(bcs, list) and bcs and isinstance(bcs[0], dict):
bcs_list = [d.get('name') or d.get('slug') or '' for d in bcs]
else:
bcs_list = bcs
if '>' in q:
query_parts = [p.strip() for p in q.split('>') if p.strip()]
if match_breadcrumbs(bcs_list, query_parts):
matched = True
else:
# substring match on breadcrumb names
for b in bcs_list:
try:
if q.strip().lower() in str(b).lower():
matched = True
break
except Exception:
pass
# authors
if not matched:
authors = fm.get('authors') or []
if match_author(authors, q):
matched = True
# pageKeywords
if not matched:
pk = fm.get('pageKeywords') or ''
tokens: List[str]
if isinstance(pk, list):
tokens = [str(x) for x in pk]
else:
tokens = [t.strip() for t in str(pk).split(',') if t.strip()]
if '*' in q:
for t in tokens:
if fnmatch.fnmatchcase(t.lower(), q.lower()):
matched = True
break
else:
for t in tokens:
if q.strip().lower() in t.lower():
matched = True
break
# other simple fields
if not matched:
for field in ['category', 'title', 'enhancedTitle', 'type', 'docid']:
if match_field_value(fm.get(field), q):
matched = True
break
elif qkey == 'authors':
authors = fm.get('authors') or []
matched = match_author(authors, qval)