Agent Skills: Algolia Core Workflow B — Indexing & Data Sync

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/algolia-core-workflow-b

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/tree/HEAD/plugins/saas-packs/algolia-pack/skills/algolia-core-workflow-b

Skill Files

Browse the full folder contents for algolia-core-workflow-b.

Download Skill

Loading file tree…

plugins/saas-packs/algolia-pack/skills/algolia-core-workflow-b/SKILL.md

Skill Metadata

Name
algolia-core-workflow-b
Description
|

Algolia Core Workflow B — Indexing & Data Sync

Overview

Keep your Algolia index synchronized with your source database. Covers full reindex, incremental updates, partial updates, synonyms, and query rules.

Prerequisites

  • Completed algolia-install-auth setup
  • Familiarity with algolia-core-workflow-a (search)
  • Source database or API with change tracking (timestamps, events)

Instructions

Step 1: Full Reindex with replaceAllObjects

import { algoliasearch } from 'algoliasearch';

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

// replaceAllObjects atomically swaps index content
// Internally: creates temp index → indexes all records → moves temp to target
// Search continues on old data until swap is complete — zero downtime
async function fullReindex(records: Record<string, any>[]) {
  const { taskID } = await client.replaceAllObjects({
    indexName: 'products',
    objects: records,
    batchSize: 1000,  // Records per batch (default 1000)
  });
  await client.waitForTask({ indexName: 'products', taskID });
  console.log(`Full reindex complete: ${records.length} records`);
}

Step 2: Incremental Updates with partialUpdateObject

// Only update changed fields — much faster than full saveObjects
async function updateProductPrice(objectID: string, newPrice: number) {
  await client.partialUpdateObject({
    indexName: 'products',
    objectID,
    attributesToUpdate: {
      price: newPrice,
      updated_at: new Date().toISOString(),
    },
    createIfNotExists: false,  // Don't create if missing
  });
}

// Batch partial updates
async function syncPriceChanges(changes: { id: string; price: number }[]) {
  const { taskID } = await client.partialUpdateObjects({
    indexName: 'products',
    objects: changes.map(c => ({
      objectID: c.id,
      price: c.price,
      updated_at: new Date().toISOString(),
    })),
    createIfNotExists: false,
  });
  await client.waitForTask({ indexName: 'products', taskID });
}

Step 3: Manage Synonyms

// Synonyms help users find products with different terminology
await client.saveSynonyms({
  indexName: 'products',
  synonymHit: [
    // Two-way synonym: any of these terms match each other
    {
      objectID: 'syn-1',
      type: 'synonym',
      synonyms: ['laptop', 'notebook', 'portable computer'],
    },
    // One-way synonym: "phone" also searches for "smartphone" but not reverse
    {
      objectID: 'syn-2',
      type: 'oneWaySynonym',
      input: 'phone',
      synonyms: ['smartphone', 'mobile phone', 'cell phone'],
    },
    // Alt correction: minor typos/variations
    {
      objectID: 'syn-3',
      type: 'altCorrection1',
      word: 'color',
      corrections: ['colour'],
    },
    // Placeholder: replace pattern with alternatives
    {
      objectID: 'syn-4',
      type: 'placeholder',
      placeholder: '<size>',
      replacements: ['small', 'medium', 'large', 'XL'],
    },
  ],
  forwardToReplicas: true,
  replaceExistingSynonyms: false,  // true = wipe existing first
});

Step 4: Configure Query Rules

// Rules let you pin, hide, boost, or filter results for specific queries
await client.saveRule({
  indexName: 'products',
  objectID: 'rule-sale-banner',
  rule: {
    conditions: [{
      anchoring: 'contains',
      pattern: 'sale',
    }],
    consequence: {
      // Pin a specific record to position 1
      promote: [{ objectID: 'promo-banner-sale', position: 0 }],

      // Add automatic filter
      params: {
        filters: 'on_sale = true',
      },
    },
    description: 'When user searches "sale", filter to sale items and pin banner',
    enabled: true,
  },
});

// Hide a product from search results
await client.saveRule({
  indexName: 'products',
  objectID: 'rule-hide-discontinued',
  rule: {
    conditions: [{ anchoring: 'is', pattern: '' }],  // Matches all queries
    consequence: {
      hide: [{ objectID: 'discontinued-product-123' }],
    },
    description: 'Hide discontinued product from all searches',
    enabled: true,
  },
});

Error Handling

| Error | Cause | Solution | |-------|-------|----------| | Record is too big (limit: 10KB) | Object exceeds free-tier limit | Strip unnecessary fields; paid plans allow 100KB | | Synonym already exists | Duplicate objectID | Use replaceExistingSynonyms: true or unique IDs | | Invalid rule condition | Wrong anchoring value | Use is, startsWith, endsWith, or contains | | Partial update creates new record | createIfNotExists default is true | Set createIfNotExists: false |

Examples

Database Change Listener → Algolia Sync

// Listen for DB changes and push to Algolia
import { getClient } from './algolia/client';

async function onDatabaseChange(event: { type: string; record: any }) {
  const client = getClient();
  const idx = 'products';

  switch (event.type) {
    case 'INSERT':
    case 'UPDATE':
      await client.saveObject({ indexName: idx, body: event.record });
      break;
    case 'DELETE':
      await client.deleteObject({ indexName: idx, objectID: event.record.id });
      break;
  }
}

Search for Synonyms

// List all synonyms matching a query
const { hits } = await client.searchSynonyms({
  indexName: 'products',
  searchSynonymsParams: { query: 'phone', type: 'synonym' },
});
console.log(`Found ${hits.length} synonym sets matching "phone"`);

Resources

Next Steps

For common errors, see algolia-common-errors.