ActivityFox Dev

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:

  1. Direct sync — when sessions are published/updated/deleted via the Express API, Meilisearch is updated inline
  2. n8n full sync — cron workflow reads all published sessions from Supabase, rebuilds both Meilisearch indexes
  3. n8n seat tracking — triggered by Sharetribe transaction events, updates seatsRemaining in Supabase + Meilisearch
  4. n8n like count — periodic aggregation of child_likes count per activity, updates likeCount in activities index

Environment Variables

VariableSideDescription
MEILI_HOSTServerMeilisearch endpoint
MEILI_MASTER_KEYServerAdmin key for index management and writes
REACT_APP_MEILI_SEARCH_KEYClientRead-only search key for frontend queries

Server Utilities

  • server/api-util/meilisearchClient.js — client factory + initializeIndexes() for setting filterable/sortable/searchable attributes
  • server/scripts/init-meilisearch.js — standalone script to create/configure indexes
  • server/api/search/sync-to-meilisearch.js — document-building logic for both indexes
  • server/api/search/search-sessions.js — optional server-side search proxy

On this page