Algolia Core Workflow A — Search & Filtering

SkillSearch

Lets your agent add Algolia search to an app with filters, facets, highlighting, and pagination.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Algolia Core Workflow A — Search & Filtering skill

About this capability

Implement or review a single-index Algolia search contract with filters, facets, pagination, and highlighting. Use when building product or content search against the JavaScript v5 client. Trigger with "Algolia search workflow", "add facets", or "searchSingleIndex".

What this skill tells your AI

The instructions your AI receives, as published by jeremylongshore/tons-of-skills-marketplace in skills/.curated/algolia-core-workflow-a/SKILL.md and read by ahel’s review.

Overview

Primary Algolia workflow: full-text search with filters, faceted navigation, hit highlighting, and pagination. Uses searchSingleIndex (v5) with real Algolia search parameters.

Prerequisites

  • Completed algolia-install-auth and algolia-hello-world setup
  • Index populated with records (see algolia-hello-world)
  • Index settings configured with searchableAttributes and attributesForFaceting

Instructions

Step 1: Configure Index for Filtering

import { algoliasearch } from 'algoliasearch';

const client = algoliasearch(process.env.ALGOLIA_APP_ID!, process.env.ALGOLIA_ADMIN_KEY!);

await client.setSettings({
  indexName: 'products',
  indexSettings: {
    // What to search (ordered = priority matters)
    searchableAttributes: ['name', 'description', 'brand', 'category'],

    // What to filter/facet on — prefix with filterOnly() if no facet counts needed
    attributesForFaceting: [
      'searchable(brand)',           // Searchable facet: users can search within brand values
      'category',                     // Regular facet: shown in facet panels
      'filterOnly(price)',           // Filter only: no counts computed, saves CPU
      'filterOnly(in_stock)',
    ],

    // Custom ranking: tie-breaker after Algolia's relevance ranking
    customRanking: ['desc(sales_count)', 'desc(rating)'],

    // What comes back in hits
    attributesToRetrieve: ['name', 'brand', 'price', 'image_url', 'category'],
    attributesToHighlight: ['name', 'description'],
    attributesToSnippet: ['description:30'],  // 30-word snippet
  },
});

Step 2: Search with Filters

// Algolia filter syntax uses SQL-like expressions
const { hits, nbHits, facets } = await client.searchSingleIndex({
  indexName: 'products',
  searchParams: {
    query: 'running shoes',

    // Numeric/boolean/string filters
    filters: 'price < 150 AND in_stock = true',

    // OR: facetFilters for UI-driven filtering (array = OR, nested = AND)
    // facetFilters: [['category:shoes', 'category:sneakers'], ['brand:Nike']],
    //   ^ shoes OR sneakers, AND brand is Nike

    // Numeric range filters
    numericFilters: ['price >= 50', 'price <= 150'],

    // Request facet counts for these attributes
    facets: ['category', 'brand'],

    // Pagination
    hitsPerPage: 20,
    page: 0,

    // Highlighting
    highlightPreTag: '<mark>',
    highlightPostTag: '</mark>',
  },
});

console.log(`${nbHits} results found`);

// Access facet counts for building filter UI
// facets = { category: { shoes: 42, sneakers: 18 }, brand: { Nike: 30, Adidas: 25 } }
for (const [facetName, values] of Object.entries(facets || {})) {
  console.log(`${facetName}:`);
  for (const [value, count] of Object.entries(values)) {
    console.log(`  ${value}: ${count}`);
  }
}

Step 3: Display Highlighted Results

hits.forEach(hit => {
  // _highlightResult contains highlighted versions of each field
  const highlighted = hit._highlightResult;
  const name = highlighted?.name?.value || hit.name;
  const snippet = hit._snippetResult?.description?.value || '';

  console.log(`${name} — $${hit.price}`);
  if (snippet) console.log(`  ${snippet}`);
});

Step 4: Implement Pagination

async function paginatedSearch(query: string, page: number = 0) {
  const { hits, nbHits, nbPages, hitsPerPage } = await client.searchSingleIndex({
    indexName: 'products',
    searchParams: {
      query,
      hitsPerPage: 20,
      page,
    },
  });

  return {
    hits,
    totalHits: nbHits,
    totalPages: nbPages,
    currentPage: page,
    hasMore: page < nbPages - 1,
  };
}

Output

The configured index returns paginated, highlighted search hits with the requested filters and facets. The examples also show how to extend that baseline to federated and optional-filter search without changing the indexing contract.

Error Handling

ErrorCauseSolution
Invalid filter syntaxMalformed filters stringCheck filter syntax: field:value, field < N, use AND/OR/NOT
Attribute not valid for filteringField not in attributesForFacetingAdd field to attributesForFaceting in settings
0 results unexpectedlyTypo tolerance may be disabledCheck typoTolerance setting; verify data is indexed
Stale results after updateDidn't wait for taskUse await client.waitForTask() after indexing

Examples

Multi-Index Search (Federated)

const { results } = await client.search({
  requests: [
    { indexName: 'products', query: 'laptop', hitsPerPage: 5 },
    { indexName: 'articles', query: 'laptop', hitsPerPage: 3 },
  ],
});

// results[0].hits = product hits, results[1].hits = article hits

Search with Optional Filters (boost, not require)

const { hits } = await client.searchSingleIndex({
  indexName: 'products',
  searchParams: {
    query: 'shoes',
    optionalFilters: ['brand:Nike'],  // Nike products ranked higher but not required
  },
});

Resources

Next Steps

For indexing and data sync workflows, see algolia-core-workflow-b.

Signals

GitHub stars
3k
Forks
396
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
algolia-core-workflow-a
Source
github.com/jeremylongshore/tons-of-skills-marketplace