ICVOSS DJANGO PACKAGE REGISTRY

The package index django-icv-search PostgreSQL Backend

PostgreSQL Backend

Documentation

Zero-infrastructure search using your existing Django database. No additional services, no new dependencies beyond what Django already requires.


Overview


Installation

No extra packages required:

pip install django-icv-search

Ensure django.contrib.postgres is in INSTALLED_APPS:

INSTALLED_APPS = [
    # ...
    "django.contrib.postgres",
    "icv_search",
]

Run the icv-search migrations to create the SearchIndex and related tables:

python manage.py migrate icv_search

The icv_search_document and icv_search_index_meta tables are created automatically the first time the backend is instantiated; you do not need to add them to a migration.


Settings Reference

Setting Value Description
ICV_SEARCH_BACKEND "icv_search.backends.postgres.PostgresBackend" Required: selects this backend
ICV_SEARCH_URL (ignored) Not used by this backend
ICV_SEARCH_API_KEY (ignored) Not used by this backend
ICV_SEARCH_TIMEOUT 30 Not used (all operations are synchronous)

Example Configuration

# settings.py
ICV_SEARCH_BACKEND = "icv_search.backends.postgres.PostgresBackend"

# Optional: disable async indexing since this backend is synchronous
ICV_SEARCH_ASYNC_INDEXING = False

That is all that is required. The backend uses Django's default database connection (django.db.connection).


When to Use


How It Works

Documents are stored in icv_search_document:

CREATE TABLE icv_search_document (
    id            BIGSERIAL PRIMARY KEY,
    index_uid     VARCHAR(255) NOT NULL,
    doc_id        VARCHAR(255) NOT NULL,
    body          JSONB        NOT NULL DEFAULT '{}',
    search_vector TSVECTOR,
    created_at    TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    updated_at    TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    UNIQUE (index_uid, doc_id)
);
CREATE INDEX ON icv_search_document USING GIN (search_vector);
CREATE INDEX ON icv_search_document (index_uid);

When you index a document, the backend concatenates the values of searchableAttributes (or all string fields when unset) into a plain-text string, then stores to_tsvector('simple', text) in search_vector.

Searches use plainto_tsquery('simple', query) matched against the GIN index, with ts_rank for relevance ordering.


pg_trgm for Intelligence Features

PostgreSQL's pg_trgm extension enables trigram-based similarity search and improves partial-match performance. It is optional but recommended if you need smarter matching:

-- Run once in a migration or directly in psql
CREATE EXTENSION IF NOT EXISTS pg_trgm;

With pg_trgm installed you can add a trigram index on the body JSONB field for faster LIKE/ILIKE queries, or implement fuzzy field-level matching directly in your application queries alongside icv-search's tsvector search.


Filtering

The backend supports Django-native filter dicts:

from icv_search.services import search

results = search(
    "articles",
    "django",
    filter={"published": True, "author_id": 42},
)

Range lookups use the __gte, __gt, __lte, __lt suffixes:

results = search("products", "", filter={"price__lte": 50.0})

Raw SQL filter strings are not accepted; pass a dict of field/value pairs.


Documents need a _geo field:

{"id": "1", "name": "Coffee shop", "_geo": {"lat": 51.5074, "lng": -0.1278}}
results = search(
    "venues",
    "coffee",
    geo_point=(51.5074, -0.1278),
    geo_radius=5000,
    geo_sort="asc",
)

The geo implementation uses a pure-SQL Haversine approximation. It is accurate but not indexed, suitable for prototyping or low-volume queries. For production geo search with large datasets, install PostGIS and use its spatial indexes.


Limitations