Skip to content
GitHub

Primary backend resolves as SearchEngine; named ones by Annotated

lexigram-search supports multiple full-text search backends through a common SearchEngine protocol. Each backend implements indexing, querying, filtering, faceting, and health-checking against the same interface, so you can switch between them with a config change.

BackendExtra / PackageProduction ReadyBest For
MeiliSearch[meilisearch]YesTypo-tolerant search, e-commerce, instant search
Elasticsearch[elasticsearch]YesLarge-scale analytics, complex aggregations
OpenSearch[elasticsearch]YesAWS-compatible Elasticsearch, open-source search
Typesense[algolia]YesLow-latency typo-tolerant search, Algolia alternative
PostgreSQL FTS[postgres]YesDirect SQL full-text, no extra infra
MySQL FULLTEXT[mysql]YesMySQL-native full-text, no extra infra
SQLite FTS5[sqlite]Dev/TestEmbedded search, CI, local dev
MongoDB Text[mongodb]YesAtlas Search, MongoDB-native text search
Memory (null)(none)NoUnit tests, prototyping

Typo-tolerant search with instant response times. MeiliSearch provides out-of-the-box ranking rules (words, typo, proximity, attribute, sort, exactness), faceted search, and synonym management. Best for customer-facing search where usability matters more than raw query syntax.

search:
backend_type: meilisearch
meilisearch:
url: http://localhost:7700
api_key: "${MEILI_MASTER_KEY}"
from lexigram.search import SearchProvider, SearchConfig
config = SearchConfig(backend_type="meilisearch")
config.meilisearch.url = "http://localhost:7700"
provider = SearchProvider.with_meilisearch(url="http://localhost:7700")

The de-facto standard for search infrastructure. Elasticsearch supports complex aggregations, custom analyzers, multi-language stemming, and near-real-time indexing. Best when you need nested queries, geo-search, or large-scale log-style search. Configure shard count and replica count for throughput tuning.

search:
backend_type: elasticsearch
elasticsearch:
hosts: ["http://localhost:9200"]
index_prefix: myapp_search_
number_of_shards: 3
number_of_replicas: 1

A fork of Elasticsearch favoured in AWS environments. The config model mirrors Elasticsearch with an additional timeout field. Use this backend when deploying on Amazon OpenSearch Service or when you need an ALv2-licensed Elasticsearch alternative.

search:
backend_type: opensearch
opensearch:
hosts: ["http://localhost:9200"]
username: "${OPENSEARCH_USER}"
password: "${OPENSEARCH_PASS}"
use_ssl: true
timeout: 30

An open-source alternative to Algolia focused on low-latency typo-tolerant search. Typesense uses a node-based cluster configuration with built-in health checking. Best for search-as-you-type experiences with minimal operational overhead.

search:
backend_type: typesense
typesense:
nodes:
- host: localhost
port: "8108"
protocol: http
api_key: "${TYPESENSE_API_KEY}"

SQL-Native Backends (PostgreSQL, MySQL, SQLite)

Section titled “SQL-Native Backends (PostgreSQL, MySQL, SQLite)”

These backends use your existing relational database for full-text search, eliminating the need for a separate search cluster. PostgreSQL uses pg_trgm for fuzzy matching and tsvector for ranking. MySQL uses FULLTEXT indexes. SQLite uses FTS5 virtual tables.

search:
backend_type: postgres
postgres:
connection_string: "${DATABASE_URL}"
text_search_config: english
enable_trigram: true

Uses MongoDB text indexes or Atlas Search. Configured with a connection string, database name, and an optional flag to enable Atlas Search for more advanced NLP-powered queries.

search:
backend_type: mongodb
mongo:
connection_string: "${MONGO_URI}"
database_name: search
use_atlas_search: false

An in-memory backend that stores documents in a dict. All data is lost on process restart. Use for unit tests and local prototyping.

from lexigram.search import SearchProvider
provider = SearchProvider.with_memory()

When you need to query multiple backends in one request, use FederatedSearchEngine. Configured via the backends list in SearchConfig, each backend receives a Named() DI binding and can be targeted individually or merged through a federated coordinator.

search:
backends:
- name: products
primary: true
backend_type: meilisearch
meilisearch:
url: http://localhost:7700
- name: docs
backend_type: elasticsearch
elasticsearch:
hosts: ["http://localhost:9200"]
from lexigram.search import SearchProvider, SearchConfig
from typing import Annotated
from lexigram.di.markers import Named
# Primary backend resolves as SearchEngine; named ones by Annotated
products: Annotated[SearchEngine, Named("products")]
docs: Annotated[SearchEngine, Named("docs")]

Ranking behaviour is backend-specific. MeiliSearch uses configurable ranking rules (words, typo, proximity, attribute, sort, exactness). Elasticsearch/OpenSearch use BM25 similarity with custom boosting via function_score. PostgreSQL uses ts_rank with optional trigram similarity boosts. Typesense uses a built-in scoring formula tuned for typo tolerance.

If you need…Choose…
Fast typo-tolerant search, minimal opsMeiliSearch
Complex aggregations, large-scale analyticsElasticsearch / OpenSearch
Low-latency search, Algolia alternativeTypesense
No extra infrastructurePostgreSQL FTS or MySQL FULLTEXT
Embedded / CI testingSQLite FTS5
Unit tests onlyMemory (null)
search:
backends:
- name: primary
primary: true
backend_type: meilisearch
meilisearch:
url: http://localhost:7700
- name: audit_log
backend_type: postgres
database: audit
postgres:
connection_string: "${DATABASE_URL}"
from lexigram.search import SearchProvider
# Arrange
provider = SearchProvider.with_memory()
# Act — provider registers an in-memory NullBackend
# All search operations work identically to production