Simon-Pierre Boucher
All apps & projects
Web Platform
Python + TypeScript

Ora·Ka

Five aggregators, 400,498 listings, one search bar: Ora·Ka merges the whole Ka data family into a single hybrid search platform.

Live at www.ora-ka.com, it mounts Immo·Ka, Lou·Ka, Fabri·Ka, Auto·Ka, and Food·Ka under one roof without modifying a single line of their backends.

400,498

Listings across five universes

5

Aggregators unified

362,508

Semantic embedding vectors

~1.4 GB

SQLite databases

2,090

Lines of Python in the engine

65,852

Backend lines embedded unchanged

Overview

Ora·Ka unifies the five automated aggregators of the Ka family (Immo·Ka for Québec properties for sale, Lou·Ka for rentals, Fabri·Ka for Québec-made products, Auto·Ka for used vehicles, and Food·Ka for grocery price comparison) into one platform totalling 400,498 listings. Each original app is mounted as-is under its own prefix (/immo/, /lou/, /fabri/, /auto/, /food/) with the same listing pages, cards, and filters, while a shared landing page presents a single search bar, a live ticker, and aggregate stats served by /api/overview.

The heart of the platform is a hybrid search engine at /api/search that scores every candidate with one unified formula rather than an exact-then-semantic cascade. Candidates come from exact AND-token matching (LIKE/FTS5) union top-k semantic hits per universe; the score combines cosine similarity over 362,508 OpenAI text-embedding-3-small vectors (256-dimension float16, about 164 MB of npz files) with token-coverage, phrase, and exact bonuses plus data-driven universe affinity. A dynamic cutoff and a diversity guarantee pin every competitive universe into the top 12, and the in-RAM numpy index hot-reloads whenever an npz file's mtime changes.

A 2,090-line Python engine (8 modules, FastAPI) orchestrates everything: run.py serves the platform, syncs the five apps on their native ingestion cadences (immo every 180 minutes, lou 60, auto 120, fabri and food 360), and rebuilds the semantic index in about 10 minutes for roughly $0.25. The embedded backends (339 Python files, 65,852 lines) run unchanged over roughly 1.4 GB of SQLite, and the five React 18 + Vite SPAs were only rebuilt under their prefixes. Production runs on MacLustr node M4M64a port 8200 under pm2, exposed by ngrok at www.ora-ka.com.

Key Features

One hybrid search bar

A single /api/search endpoint queries all five universes at once, merging exact AND-token matches with top-k semantic hits under one unified score per candidate.

Semantic index

362,508 OpenAI text-embedding-3-small vectors at 256 dimensions in float16, about 164 MB of npz files, one index per universe.

Five apps, zero backend changes

Each original app is mounted under its own prefix with its full tree intact; 339 Python files and 65,852 lines run completely unmodified.

Automated ingestion

run.py watch keeps every universe fresh on its native cadence: immo every 180 minutes, lou every 60, auto every 120, fabri and food every 360.

Hot-reloading in-RAM index

Brute-force numpy search over npz files held in RAM, reloaded automatically on mtime change, so a simple rsync updates the index with no restart.

KA ID SSO and hub favorites

Signs members in through the groupe-ka.com hub's KA ID single sign-on and syncs favorites back to the central hub store.

How It Works

  1. Ingestion

    run.py watch runs a permanent loop that syncs each of the five apps on its native cadence (immo 180 min, lou 60, auto 120, fabri and food 360), using the apps' own unmodified ingestion code.

  2. Storage

    Each universe keeps its original SQLite database (immoka.db, louka.db, fabrika.db, autoka.db, foodka.db, plus two neighborhood databases), about 1.4 GB in total with no database server.

  3. Indexing

    run.py index incrementally rebuilds one npz semantic index per universe with OpenAI text-embedding-3-small at 256 dimensions, taking about 10 minutes and roughly $0.25 per run.

  4. Search

    /api/search unions exact AND-token candidates with per-universe semantic top-k, scores everything with cosine plus 0.22 token-coverage, 0.10 phrase, and 0.05 exact bonuses, then applies a dynamic cutoff and a diversity guarantee.

  5. Serving

    The FastAPI master app in oraka/web.py serves the landing, /api/overview, and /healthz, and mounts the five apps under their prefixes; pm2 runs web, sync, and ngrok processes on M4M64a:8200 behind www.ora-ka.com.

Tech Stack

Engine & Backend

Python 3.14
FastAPI (5 mounted sub-apps)
numpy brute-force vector search
OpenAI text-embedding-3-small (256d)

Data & Frontend

SQLite (~1.4 GB, 5 databases + 2 neighborhood databases)
npz semantic indexes (~164 MB)
5 React 18 + Vite SPAs rebuilt under prefixes
LIKE/FTS5 exact matching

Deployment

MacLustr cluster node M4M64a, port 8200
pm2 (web, sync, and ngrok processes)
ngrok tunnel
www.ora-ka.com

Highlights

  • 400,498 listings unified: 291,132 Québec products, 48,487 properties, 26,319 grocery items, 17,997 vehicles, and 16,563 rentals.
  • Not a single line of the five embedded backends was modified: 339 Python files and 65,852 lines run as-is under their prefixes.
  • One unified score per candidate (cosine + 0.22 token coverage + 0.10 phrase + 0.05 exact) replaces the usual exact-then-semantic cascade.
  • A diversity guarantee pins every universe scoring within 0.30 of the best hit into the top 12 results.
  • The semantic index hot-reloads on file mtime, so rsyncing fresh npz files updates search without restarting the server.
  • GET /healthz verifies all five SQLite databases and GET /api/overview serves live aggregate statistics.

Explore Ora·Ka

Five Ka aggregators behind one search bar — the full source is on GitHub.