Search System
Meilisearch indexes, Algolia InstantSearch adapter, sync pipeline, and search frontend integration.
Search System
ActivityFox uses Meilisearch for fast, faceted activity/session search. The frontend uses Algolia's react-instantsearch library with the @meilisearch/instant-meilisearch adapter.
Meilisearch Indexes
sessions index
One document per published session. Primary search index for parents.
Searchable attributes: title, description, organizationName, category, label, locationAddress
Filterable attributes: category, daysOfWeek, ageGroupMin, ageGroupMax, priceSubUnits, status, seatsRemaining, startDate, endDate, _geo
Sortable attributes: priceSubUnits, startDate, seatsRemaining, createdAt
Document shape:
{
"id": "session-uuid",
"activityId": "activity-uuid",
"title": "After School Soccer",
"description": "...",
"category": "sports-athletics",
"label": "Term 1 Mon/Wed 3-5pm",
"organizationName": "City Sports Club",
"locationAddress": "123 Main St",
"_geo": { "lat": -33.8688, "lng": 151.2093 },
"ageGroupMin": 5,
"ageGroupMax": 12,
"daysOfWeek": ["MO", "WE"],
"startDate": "2026-04-01",
"endDate": "2026-06-30",
"startTime": "15:00",
"endTime": "17:00",
"totalSeats": 20,
"seatsRemaining": 15,
"priceSubUnits": 15000,
"currency": "AUD",
"status": "published",
"createdAt": "2026-03-18T00:00:00Z"
}activities index
One document per activity. Aggregated view for browse/discovery.
Searchable attributes: title, description, organizationName, category
Filterable attributes: category, ageGroupMin, ageGroupMax, status, hasAvailability, _geo
Sortable attributes: minPrice, likeCount, sessionCount, createdAt
Document shape:
{
"id": "activity-uuid",
"title": "After School Soccer",
"description": "...",
"category": "sports-athletics",
"organizationName": "City Sports Club",
"locationAddress": "123 Main St",
"_geo": { "lat": -33.8688, "lng": 151.2093 },
"ageGroupMin": 5,
"ageGroupMax": 12,
"imageUrls": ["https://..."],
"minPrice": 15000,
"sessionCount": 3,
"hasAvailability": true,
"likeCount": 12,
"status": "published",
"createdAt": "2026-03-18T00:00:00Z"
}Frontend Integration
ActivitySearchPage (/activities)
Uses react-instantsearch with @meilisearch/instant-meilisearch adapter:
import { instantMeiliSearch } from '@meilisearch/instant-meilisearch';
import { InstantSearch, SearchBox, Hits } from 'react-instantsearch';
const searchClient = instantMeiliSearch(MEILI_HOST, MEILI_SEARCH_KEY);
<InstantSearch indexName="sessions" searchClient={searchClient}>
<SearchBox />
<RefinementList attribute="category" />
<RangeInput attribute="ageGroupMin" />
<Hits hitComponent={ActivityCard} />
<Pagination />
</InstantSearch>Search state is managed by InstantSearch internally — no Redux needed for search queries/results.
SearchFilters Component
Faceted filter panel with:
- Category — checkbox list (10 categories)
- Age Range — min/max number inputs
- Price Range — min/max
- Location — geo-based filtering with radius
ActivityCard Component
Search result card displaying: category badge (colour-coded), title, organisation name, description preview, location, age range, price, session count.
Sync Pipeline
Data flows from Supabase → Meilisearch via:
- Direct sync — when sessions are published/updated/deleted via the Express API, Meilisearch is updated inline
- n8n full sync — cron workflow reads all published sessions from Supabase, rebuilds both Meilisearch indexes
- n8n seat tracking — triggered by Sharetribe transaction events, updates
seatsRemainingin Supabase + Meilisearch - n8n like count — periodic aggregation of
child_likescount per activity, updateslikeCountin activities index
Environment Variables
| Variable | Side | Description |
|---|---|---|
MEILI_HOST | Server | Meilisearch endpoint |
MEILI_MASTER_KEY | Server | Admin key for index management and writes |
REACT_APP_MEILI_SEARCH_KEY | Client | Read-only search key for frontend queries |
Server Utilities
server/api-util/meilisearchClient.js— client factory +initializeIndexes()for setting filterable/sortable/searchable attributesserver/scripts/init-meilisearch.js— standalone script to create/configure indexesserver/api/search/sync-to-meilisearch.js— document-building logic for both indexesserver/api/search/search-sessions.js— optional server-side search proxy