mirror of
https://gitverse.ru/kpa39l/email-local-archive.git
synced 2026-09-29 09:15:11 +00:00
Initial commit: Hermes skill email-local-archive
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
# SQLite-индекс архива писем
|
||||
|
||||
## Назначение
|
||||
|
||||
Быстрый поиск и трекинг по архиву email.md без grep-а по всем папкам.
|
||||
Основа для: contacts_extractor (знает какие письма обработаны), sqlite_search (FTS5), digest (выборка по дате).
|
||||
|
||||
## Компоненты
|
||||
|
||||
| Компонент | Путь | Назначение |
|
||||
|-----------|------|------------|
|
||||
| Индексатор | `/opt/hermes/email-assistant/scripts/mail_index.py` | Сканирует email.md → SQLite |
|
||||
| Поиск | `/opt/hermes/email-assistant/scripts/sqlite_search.py` | FTS5-поиск по индексу |
|
||||
| База | `/opt/hermes/email/mail_index.db` | SQLite (WAL mode) |
|
||||
|
||||
## Схема БД
|
||||
|
||||
### emails — основная таблица
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS emails (
|
||||
path TEXT PRIMARY KEY, -- относительный путь от EMAIL_ROOT
|
||||
uid INTEGER, -- числовой UID из пути
|
||||
folder TEXT, -- INBOX, INBOX/!Scan, Sent...
|
||||
date TEXT, -- дата из frontmatter (ISO)
|
||||
from_addr TEXT, -- отправитель
|
||||
to_addrs TEXT, -- получатели
|
||||
subject TEXT, -- тема
|
||||
body_preview TEXT, -- первые 500 символов тела (без HTML)
|
||||
contacts_extracted INTEGER DEFAULT 0, -- 0/1 — обработано contacts_extractor
|
||||
contacts_skipped INTEGER DEFAULT 0, -- 0/1 — нет подписи / LLM error
|
||||
first_seen TEXT, -- когда проиндексировано
|
||||
last_scanned TEXT, -- последняя проверка contacts
|
||||
file_mtime REAL -- mtime файла для инкрементальной проверки
|
||||
);
|
||||
```
|
||||
|
||||
### email_fts — FTS5 virtual table
|
||||
|
||||
```sql
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS email_fts USING fts5(
|
||||
subject, from_addr, to_addrs, body_preview,
|
||||
content='emails',
|
||||
content_rowid='rowid',
|
||||
tokenize='unicode61'
|
||||
);
|
||||
```
|
||||
|
||||
Синхронизация через триггеры INSERT/UPDATE/DELETE + FTS5 rebuild.
|
||||
|
||||
### Индексы
|
||||
|
||||
- `idx_emails_folder` — быстрая фильтрация по папке
|
||||
- `idx_emails_uid` — lookup по UID
|
||||
- `idx_emails_contacts` — необработанные письма (contacts_extracted=0)
|
||||
- `idx_emails_date` — сортировка по дате
|
||||
|
||||
## Контракт между mail_index.py и contacts_extractor.py
|
||||
|
||||
**mail_index.py** владеет схемой и создаёт таблицы. **contacts_extractor.py** — только читает/пишет поля `contacts_extracted`, `contacts_skipped`, `last_scanned`.
|
||||
|
||||
```sql
|
||||
-- contacts_extractor берёт необработанные письма:
|
||||
SELECT rowid, path, uid, folder, from_addr, subject
|
||||
FROM emails
|
||||
WHERE contacts_extracted = 0 AND contacts_skipped = 0
|
||||
ORDER BY folder, uid
|
||||
LIMIT ?
|
||||
|
||||
-- После обработки:
|
||||
UPDATE emails SET contacts_extracted=1, last_scanned=? WHERE rowid=?
|
||||
UPDATE emails SET contacts_skipped=1, last_scanned=? WHERE rowid=?
|
||||
```
|
||||
|
||||
## Быстродействие
|
||||
|
||||
- 2073 письма → полная индексация ~9 секунд
|
||||
- FTS5-поиск — мгновенно (<100ms)
|
||||
- WAL mode — конкурентные чтения не блокируют запись
|
||||
- Инкрементальная индексация по mtime — доли секунды
|
||||
|
||||
## Использование sqlite_search.py
|
||||
|
||||
```bash
|
||||
# Простой поиск (AND по умолчанию)
|
||||
python3 sqlite_search.py 'Стороженко'
|
||||
python3 sqlite_search.py 'битрикс OR контрагент'
|
||||
python3 sqlite_search.py '"точечная фраза"'
|
||||
|
||||
# Фильтры
|
||||
python3 sqlite_search.py --folder 'INBOX/!Отчеты'
|
||||
python3 sqlite_search.py --limit 20
|
||||
|
||||
# Специальные префиксы (точно в поле from_addr/subject через LIKE)
|
||||
python3 sqlite_search.py 'from:example@mail'
|
||||
python3 sqlite_search.py 'subject:отчёт'
|
||||
|
||||
# Вкл. тело письма в FTS5 (медленнее, но находит больше)
|
||||
python3 sqlite_search.py --body
|
||||
```
|
||||
|
||||
## Операторы FTS5
|
||||
|
||||
- `AND` — по умолчанию между словами
|
||||
- `OR` — `'битрикс OR контрагент'`
|
||||
- `"точная фраза"` — кавычки для точного совпадения
|
||||
- `-исключить` — минус перед словом
|
||||
- `prefix*` — wildcard (звёздочка на конце)
|
||||
|
||||
## mail_index.py — ключевые параметры
|
||||
|
||||
```bash
|
||||
python3 mail_index.py # полная переиндексация
|
||||
python3 mail_index.py --incremental # только новые (по mtime)
|
||||
python3 mail_index.py --search "..." # поиск (встроенный, без FTS5)
|
||||
python3 mail_index.py --stats # статистика
|
||||
```
|
||||
|
||||
## Важные детали
|
||||
|
||||
- **body_preview** — только первые 500 символов. Для full-text search с телом используй `sqlite_search.py --body` (FTS5 на preview).
|
||||
- **contacts_extracted/contacts_skipped** — взаимоисключающие флаги. Если ни один не 1 — письмо не обработано.
|
||||
- **file_mtime** — для инкрементальной индексации. Если mtime файла > last_mtime в БД — переиндексировать.
|
||||
- **FTS5 rebuild** — вызывается после каждой полной индексации для согласованности.
|
||||
- Исключает папки `contacts/` и `state/` из сканирования.
|
||||
Reference in New Issue
Block a user