anishfyi.com Projects All writings

December 15, 20258 min readElasticsearch, Search, Django

Full-Text Search with Elasticsearch and Django

Adding fast full-text search to a Django app by syncing models into Elasticsearch and querying them well.

Search that runs a database LIKE over a text column works until it does not. It is slow once the table is large, and it has no idea which result is more relevant than another. Elasticsearch is built for exactly this job, and Django integrates with it cleanly.

Why not just the database

A wildcard text match cannot use a normal index, so it scans. It also treats every match as equal: a term in the title ranks the same as a term buried in a description. Real search needs tokenizing, ranking, and tolerance for typos, and that is a search engine, not a column.

Define a document

With django-elasticsearch-dsl, a document declares which model fields get indexed and how.

from django_elasticsearch_dsl import Document, fields
from django_elasticsearch_dsl.registries import registry
from products.models import Product


@registry.register_document
class ProductDocument(Document):
    vendor = fields.TextField(attr="vendor.name")

    class Index:
        name = "products"

    class Django:
        model = Product
        fields = ["name", "sku", "description"]

Keep the index in sync

The library hooks model save and delete to update the index automatically. The gap is bulk operations: a bulk_update does not fire those hooks, so anything bulk needs an explicit reindex afterwards.

Querying with relevance in mind

A multi-match query searches several fields at once, and a boost makes a hit in the name count for more than a hit in the description.

results = ProductDocument.search().query(
    "multi_match",
    query=term,
    fields=["name^3", "sku^2", "description"],
    fuzziness="AUTO",
)

Relevance is the whole point

Field boosts and fuzziness are not extras. They are the reason to use a search engine at all. A user who types a slightly wrong SKU should still find the product, and the closest match should sit at the top.

Elasticsearch is a secondary store

The database remains the source of truth. The index can lag, and it can be thrown away and rebuilt at any time. Never make a write decision based on a search result. Read from search, write against the database.

Operational notes

Keep a management command that rebuilds the index from scratch. Any change to the mapping, such as a new field or a changed analyzer, means creating a fresh index and reindexing into it. Plan for that rebuild rather than being surprised by it.

Working on something in this territory? Book thirty minutes.