Metadata-Version: 2.4
Name: lexigram-search
Version: 0.1.1
Summary: Full-text search and indexing for Lexigram Framework - Elasticsearch, Meilisearch, and Algolia
Project-URL: Homepage, https://github.com/lexigram-dev/lexigram
Project-URL: Repository, https://github.com/lexigram-dev/lexigram
Project-URL: Documentation, https://docs.lexigram.dev/search
Project-URL: Issues, https://github.com/lexigram-dev/lexigram/issues
Project-URL: Changelog, https://github.com/lexigram-dev/lexigram/blob/main/CHANGELOG.md
Author-email: Lexigram Framework Team <team@lexigram.dev>
Maintainer-email: Lexigram Framework Team <team@lexigram.dev>
License: MIT
License-File: LICENSE
Keywords: algolia,async,elasticsearch,framework,full-text,indexing,meilisearch,search
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: lexigram-contracts>=0.1.0
Requires-Dist: lexigram>=0.1.1
Requires-Dist: typing-extensions>=4.0.0
Provides-Extra: algolia
Requires-Dist: algoliasearch>=3.0.0; extra == 'algolia'
Provides-Extra: all
Requires-Dist: algoliasearch>=3.0.0; extra == 'all'
Requires-Dist: elasticsearch>=8.0.0; extra == 'all'
Requires-Dist: lexigram-testing>=0.1.1; extra == 'all'
Requires-Dist: meilisearch>=0.28.0; extra == 'all'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'all'
Requires-Dist: pytest-cov>=4.0.0; extra == 'all'
Requires-Dist: pytest-mock>=3.10.0; extra == 'all'
Requires-Dist: pytest>=8.0.0; extra == 'all'
Provides-Extra: database
Requires-Dist: aiomysql>=0.1.0; extra == 'database'
Requires-Dist: aiosqlite>=0.19.0; extra == 'database'
Requires-Dist: asyncpg>=0.29.0; extra == 'database'
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: elasticsearch
Requires-Dist: elasticsearch>=8.0.0; extra == 'elasticsearch'
Provides-Extra: meilisearch
Requires-Dist: meilisearch>=0.28.0; extra == 'meilisearch'
Provides-Extra: mongodb
Requires-Dist: motor>=3.3.0; extra == 'mongodb'
Provides-Extra: mysql
Requires-Dist: aiomysql>=0.1.0; extra == 'mysql'
Provides-Extra: postgres
Requires-Dist: asyncpg>=0.29.0; extra == 'postgres'
Provides-Extra: search-all
Requires-Dist: aiomysql>=0.1.0; extra == 'search-all'
Requires-Dist: aiosqlite>=0.19.0; extra == 'search-all'
Requires-Dist: algoliasearch>=3.0.0; extra == 'search-all'
Requires-Dist: asyncpg>=0.29.0; extra == 'search-all'
Requires-Dist: elasticsearch>=8.0.0; extra == 'search-all'
Requires-Dist: meilisearch>=0.28.0; extra == 'search-all'
Requires-Dist: motor>=3.3.0; extra == 'search-all'
Provides-Extra: sqlite
Requires-Dist: aiosqlite>=0.19.0; extra == 'sqlite'
Provides-Extra: test
Requires-Dist: lexigram-testing>=0.1.1; extra == 'test'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'test'
Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
Requires-Dist: pytest-mock>=3.10.0; extra == 'test'
Requires-Dist: pytest>=8.0.0; extra == 'test'
Description-Content-Type: text/markdown

# lexigram-search

Full-text search and indexing for Lexigram Framework — Elasticsearch, Meilisearch, and Algolia

---

## Overview

lexigram-search provides a unified `SearchEngineProtocol` interface over Meilisearch, Elasticsearch, Typesense, PostgreSQL full-text, MySQL, MongoDB, and SQLite. It supports typo-tolerant search, faceting, fuzzy matching, result caching, and analytics. All services are wired via `SearchProvider`, which registers the search engine protocol with the DI container.

---

## Install

```bash
uv add lexigram-search
# Optional extras
uv add "lexigram-search[meilisearch]"     # Meilisearch
uv add "lexigram-search[elasticsearch]"   # Elasticsearch 8.x
uv add "lexigram-search[typesense]"       # Typesense
uv add "lexigram-search[postgres,mysql,sqlite,mongodb]"  # Database backends
```

## Quick Start

```python
from lexigram import Application
from lexigram.di.module import Module, module

# Import the module from the package
from lexigram.search import SearchModule

@module(imports=[SearchModule.configure(...)])
class AppModule(Module):
    pass

app = Application(modules=[AppModule])
if __name__ == "__main__":
    app.run()
```

## Configuration

> **Zero-config usage:** Call `SearchModule.configure()` with no arguments to use defaults.

### Option 1 — YAML file

```yaml
# application.yaml
search:
  backend_type: meilisearch
  meilisearch:
    url: http://localhost:7700
    api_key: "${MEILI_API_KEY}"
  query:
    strategy: fuzzy
    default_limit: 10
```

### Option 2 — Profiles + Environment Variables *(recommended)*

```bash
export LEX_SEARCH__ENABLED=true
# Environment variables for each field
```

### Option 3 — Python

```python
from lexigram.search.config import SearchConfig, BackendType
from lexigram.search import SearchModule

config = SearchConfig(backend_type=BackendType.MEILISEARCH, ...)
SearchModule.configure(config)
```

### Config reference

| Field | Default | Env var | Description |
|-------|---------|---------|-------------|
| `backend_type` | `memory` | `LEX_SEARCH__BACKEND_TYPE` | Active backend (`meilisearch`, `elasticsearch`, `typesense`, `postgres`, `mysql`, `sqlite`, `memory`) |
| `timeout` | `30.0` | `LEX_SEARCH__TIMEOUT` | Default request timeout in seconds |
| `query.strategy` | `fuzzy` | `LEX_SEARCH__QUERY__STRATEGY` | Query strategy (`fuzzy`, `exact`, `semantic`, `hybrid`) |
| `query.default_limit` | `10` | `LEX_SEARCH__QUERY__DEFAULT_LIMIT` | Default number of results returned |
| `query.max_limit` | `100` | `LEX_SEARCH__QUERY__MAX_LIMIT` | Maximum allowed result limit |
| `query.fuzzy_threshold` | `0.8` | `LEX_SEARCH__QUERY__FUZZY_THRESHOLD` | Fuzzy match threshold (0–1; 1 = exact) |
| `meilisearch.url` | `http://localhost:7700` | `LEX_SEARCH__MEILISEARCH__URL` | MeiliSearch server URL |
| `meilisearch.api_key` | `null` | `LEX_SEARCH__MEILISEARCH__API_KEY` | MeiliSearch authentication key |
| `elasticsearch.hosts` | `[http://localhost:9200]` | `LEX_SEARCH__ELASTICSEARCH__HOSTS` | Elasticsearch cluster hosts |
| `operations.bulk_chunk_size` | `500` | `LEX_SEARCH__OPERATIONS__BULK_CHUNK_SIZE` | Documents per bulk index request |

## Module Factory Methods

| Method | Description |
|--------|-------------|
| `SearchModule.configure(config, enable_facets)` | Configure with explicit SearchConfig |
| `SearchModule.stub()` | Minimal config for testing |

## Key Features

- **Protocol abstraction** — Swap backends without changing application code
- **Meilisearch** — Typo-tolerant, instant search with faceting
- **Elasticsearch** — Full Lucene query DSL; aggregations; multi-index
- **Typesense** — Fast, schema-enforced search with scoped API keys
- **PostgreSQL FTS** — `tsvector`/`tsquery` via `lexigram-sql`; no extra infra
- **MySQL FTS** — `FULLTEXT` index support for MySQL / MariaDB
- **MongoDB Text** — Native `$text` operator with language stemming
- **SQLite FTS5** — Local development with zero dependencies
- **Cached search** — Transparent result caching via `CacheBackend`
- **Analytics** — Query recording and hit-rate analytics for ranking improvement

## Testing

```python
async with Application.boot(modules=[SearchModule.stub()]) as app:
    # your test code
    ...
```

## Key Source Files

| File | What it contains |
|------|----------------|
| `src/lexigram/search/module.py` | `SearchModule` class with factory methods |
| `src/lexigram/search/di/provider.py` | `SearchProvider` — wires search protocols into DI container |
| `src/lexigram/search/config.py` | `SearchConfig` and sub-config classes |
| `src/lexigram/search/engine/` | Search engine implementations for each backend |
| `src/lexigram/search/indexer/` | Index management and document indexing |