#!/usr/bin/env python3 """Reproducible offline query of one downloaded Magalia corpus SQLite snapshot. gzip -dk texts.sqlite.gz python3 query.py texts.sqlite '"δημου"' --layer conservative --format jsonl No SQL fragments, regex, stemming or hidden cross-corpus deduplication. """ import argparse,csv,json,sqlite3,sys,unicodedata,re from pathlib import Path def normalize(s): return ' '.join(re.findall(r'[^\W_]+',''.join(c for c in unicodedata.normalize('NFD',s.replace('\ufffc',' zzmagaliaboundaryzz ').lower()) if unicodedata.category(c)!='Mn').replace('ς','σ'))) def main(): p=argparse.ArgumentParser(description=__doc__) p.add_argument('database',type=Path);p.add_argument('query');p.add_argument('--layer',choices=['reading','conservative'],default='reading');p.add_argument('--format',choices=['jsonl','csv'],default='jsonl');p.add_argument('--limit',type=int,default=1000) a=p.parse_args() if 'zzmagaliaboundaryzz' in a.query.lower() or '\ufffc' in a.query or a.query.count('"')%2:p.error('Close quoted phrases and do not query reserved boundary markers.') if not a.database.is_file() or not 1<=a.limit<=100000: p.error('Use an existing database and a limit between 1 and 100000.') clauses=[] for match in re.finditer(r'(-?)(?:"([^"]+)"|([^\s"]+))',a.query): phrase=normalize(match[2] or match[3]) if phrase:clauses.append((bool(match[1]),'"'+phrase.replace('"','""')+'"')) positive=[v for neg,v in clauses if not neg] if not positive:p.error('At least one positive word or quoted phrase is required.') expression=' AND '.join(positive) for neg,v in clauses: if neg:expression+=' NOT '+v expression=a.layer+' : ('+expression+')' con=sqlite3.connect(a.database.resolve().as_uri()+'?mode=ro',uri=True) rows=con.execute('SELECT r.record_json FROM text_fts f JOIN records r ON r.record_id=f.record_id WHERE text_fts MATCH ? ORDER BY r.record_id LIMIT ?',(expression,a.limit+1)).fetchall() if len(rows)>a.limit:print('Result truncated at requested limit.',file=sys.stderr) records=[json.loads(r[0]) for r in rows[:a.limit]] if a.format=='jsonl': for rec in records:print(json.dumps(rec,ensure_ascii=False)) else: fields=['record_id','corpus','source_id','edition','title','language','date','source_sha256','source_url','license',a.layer] w=csv.DictWriter(sys.stdout,fieldnames=fields,extrasaction='ignore');w.writeheader() for rec in records: row={k:rec.get(k) for k in fields} for k,v in row.items(): if isinstance(v,str) and v.startswith(('=','+','-','@','\t','\r')):row[k]="'"+v w.writerow(row) return 0 if __name__=='__main__':raise SystemExit(main())