Curated Search¶
A small search engine over a hand-picked set of RSS/Atom feeds. Feeds are polled for new entries, the full article text is extracted, and everything is indexed with PostgreSQL full-text search.
Stack: Python 3.12+, Django 6.1, PostgreSQL 17, Celery + Redis, htmx, managed with uv. It is self-hosted with Docker behind Caddy.
Features¶
- Full-text search with stemming,
"exact phrases",-excludedwords andOR, ranked by where the match is (title > summary > body), with highlighted snippets. - English and Swedish stemming: each feed's language is read from its
<language>tag (or set in the admin), and queries match both. - Live search as you type (htmx), filter by topic or feed, sort by relevance or date.
- Topics group the feeds ("Python", "Databases"): each has a page with its feeds and newest articles, and an Atom feed. Importing OPML turns a feed reader's folders into topics.
- "Did you mean …?" for misspelled queries, using the words that actually occur in the indexed articles.
- Feeds are polled politely (conditional GETs) every 30 minutes by Celery beat;
the article text is extracted with trafilatura,
and fetched again when the feed marks the article as updated (articles
without an updated date are refetched daily during their first week,
ARTICLE_REFETCH_DAYS). - A "Latest" page with the newest articles by day (all feeds or one topic), and an Atom feed of everything new.
- Keyboard shortcuts:
/to search,j/kto move through results,Enter/oto open one,bto bookmark it, and?for the list. - Subscribe to any search as an Atom feed (
/feeds/search/atom/?q=...), and add the site as a search engine in your browser (OpenSearch). - Readers can suggest feeds; staff approve them in the admin, which adds and fetches the feed.
- Import/export the feed list as OPML, the format every feed reader speaks.
- Every article has a page with "more like this": other articles ranked by how many of its key terms they share.
- Readers with an account can bookmark articles and save searches; the library page shows how many new matches each saved search has, and each saved search can send a daily or weekly email digest of new matches.
- Search insights for staff: the most common searches, and the ones that found nothing (topics the feeds may be missing). Only daily counts per query are kept, nothing about who searched.
- Accounts via django-allauth: email and password, an emailed sign-in code, or a passkey; optional two-factor authentication (required for staff). Readers can download everything stored about them as JSON, and delete their account.
Where to go next¶
- Development: run the stack locally, with or without Docker.
- Managing feeds: add, import and fetch feeds; demo data.
- Configuration: every environment variable.
- Deploying: the production stack on one server.
- How it works: search, ingestion, caching and security.