feat(search): Elasticsearch searcher (name+content) and oo search (#37)
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 4s
Tests / Test (Go 1.25) (pull_request) Successful in 18s
Tests / Test (Go stable) (pull_request) Successful in 20s
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 4s
Tests / Test (Go 1.25) (pull_request) Successful in 18s
Tests / Test (Go stable) (pull_request) Successful in 20s
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
---
|
||||
type: reference
|
||||
status: current
|
||||
related:
|
||||
- README.md
|
||||
- file_es.go
|
||||
---
|
||||
|
||||
# Elasticsearch — полнотекстовый поиск OnlyOffice
|
||||
|
||||
## Что это
|
||||
|
||||
Полнотекстовый поиск OnlyOffice Workspace работает на **Elasticsearch**.
|
||||
Клиент на сервере — NEST. Индекс — имя таблицы.
|
||||
|
||||
Для файлов индекс `files_file`:
|
||||
|
||||
| поле | тип | смысл |
|
||||
|------|-----|-------|
|
||||
| `id` | integer | id файла (тот же, что в REST/Documents) |
|
||||
| `title` | text (`whitespacecustom`) | имя файла |
|
||||
| `tenantId` | integer | тенант (портал) |
|
||||
| `folders` | nested | список папок: `folderId` (строка), `id`, `tenantId` |
|
||||
| `document.attachment.content` | text (`document`) | извлеченный текст (ingest-attachment) |
|
||||
| `document.attachment.content_type` | text | MIME |
|
||||
|
||||
Важно:
|
||||
- Живой сервер — **Elasticsearch 7.16.3**, кластер `elasticsearch`.
|
||||
- REST `GET /api/2.0/files/@search/{query}` ищет **только по имени в БД**
|
||||
(`fileDao.Search`), ES не задействует. Для поиска по содержимому нужен
|
||||
прямой ES — это и делает `oo search`.
|
||||
- `title` analyzer `whitespacecustom` режет по пробелам и lower-case. Полное
|
||||
имя файла — один токен (`Rechnung-4711.pdf`), поэтому поиск по имени ищет
|
||||
слово целиком, а не подстроку.
|
||||
- `document.attachment.content` заполняется **только для Office-форматов**
|
||||
(docx / xlsx / pptx). У PDF/txt, залитых через API, контент не извлекается.
|
||||
- Индексация асинхронная (TeamLabSvc) — файл появляется в ES не мгновенно.
|
||||
|
||||
## Доступ
|
||||
|
||||
ES слушает `127.0.0.1:9200` **внутри** VM OnlyOffice. Снаружи порт закрыт,
|
||||
SSH в VM открыт на хосте как `127.0.0.1:32` (контейнер `onlyoffice-v2`,
|
||||
QEMU). Схема — SSH-туннель.
|
||||
|
||||
```bash
|
||||
# из корня go-onlyoffice (ключ и хост — как в infra-доках)
|
||||
ssh -f -N -o ControlMaster=no -o ControlPath=none \
|
||||
-p 32 -i ~/.ssh/id_ed25519 \
|
||||
-L 9200:127.0.0.1:9200 root@127.0.0.1
|
||||
|
||||
curl -s http://127.0.0.1:9200/ | head # tagline + version
|
||||
curl -s 'http://127.0.0.1:9200/_cat/indices?h=index,docs.count'
|
||||
```
|
||||
|
||||
`-o ControlMaster=no -o ControlPath=none` обязательны: иначе forward уходит
|
||||
в persistent master-соединение из `~/.ssh/config` и порт остаётся занят.
|
||||
|
||||
Проверить, что туннель жив:
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:9200/files_file/_count
|
||||
```
|
||||
|
||||
## Переменные
|
||||
|
||||
| env | default | смысл |
|
||||
|-----|---------|-------|
|
||||
| `ONLYOFFICE_ES_URL` | — (обязателен) | `scheme://host:port` ES |
|
||||
| `ONLYOFFICE_ES_INDEX` | `files_file` | индекс |
|
||||
| `ONLYOFFICE_TENANT` | пусто (все) | фильтр `tenantId` |
|
||||
|
||||
Имена — в [`.env.example`](../.env.example). Секретов нет: ES без пароля.
|
||||
|
||||
## CLI
|
||||
|
||||
```bash
|
||||
ONLYOFFICE_ES_URL=http://127.0.0.1:9200 oo search "Rechnung"
|
||||
ONLYOFFICE_ES_URL=http://127.0.0.1:9200 oo search "Mahngebühr" --content
|
||||
oo search "Rechnung" --folder 649 --limit 50 --json
|
||||
```
|
||||
|
||||
Флаги: `--content` (искать и по тексту), `--folder ID` (папка
|
||||
`folders.folderId`), `--limit N` (по умолчанию 20, максимум 200),
|
||||
`--json` = `-o json`.
|
||||
|
||||
## Библиотека
|
||||
|
||||
`file_es.go` — `ESSearcher` (`Name() = "elasticsearch"`), прямой ES REST на
|
||||
stdlib `net/http`:
|
||||
|
||||
```go
|
||||
es, _ := onlyoffice.NewESSearcher(onlyoffice.ESConfigFromEnv())
|
||||
hits, _ := es.Search(ctx, onlyoffice.SearchQuery{
|
||||
Text: "Rechnung", InContent: true, Limit: 20,
|
||||
})
|
||||
```
|
||||
|
||||
Запрос: `multi_match` по `title^2` (+ `document.attachment.content` при
|
||||
`InContent`), фильтры `tenantId` и `folders.folderId`, `_source`
|
||||
id/title/folders, `highlight` для фрагмента. Ответ → `[]SearchHit` (модель из
|
||||
эпика #34; пока объявлена в `file_es.go`, переедет в `file_core.go` с F1 #35).
|
||||
|
||||
## Тесты
|
||||
|
||||
```bash
|
||||
# unit — чистые builders/парсеры, без сети
|
||||
go test ./ -run ES
|
||||
|
||||
# integration — нужен ONLYOFFICE_ES_URL (+ креды REST для залива)
|
||||
set -a; . .env; set +a
|
||||
ONLYOFFICE_ES_URL=http://127.0.0.1:9200 ONLYOFFICE_TENANT=1 \
|
||||
go test -tags=integration -run TestIntegrationESSearch -v .
|
||||
```
|
||||
|
||||
Интеграционный тест заливает временный xlsx (в имени и в ячейке — уникальные
|
||||
токены), ждёт индексации, проверяет поиск по имени и по содержимому, затем
|
||||
удаляет проект.
|
||||
|
||||
## Грабли
|
||||
|
||||
- `locale`/версия ES: 7.16.3, `_search` совместим с REST 7.x.
|
||||
- ES без auth и слушает только localhost — туннель обязателен.
|
||||
- Фильтр `tenantId` сузит выдачу; без него видны документы всех тенантов.
|
||||
- Поиск по содержимому PDF, залитых через API, не работает (нет
|
||||
`attachment.content`) — только Office-форматы.
|
||||
Reference in New Issue
Block a user