Skip to content

Sourcerer Integration

toSearchFn() adapts a ScoutIndex to the searchFn signature expected by @vielzeug/sourcerer's createLocalSource.

ts
import { createIndex, toSearchFn } from '@vielzeug/scout';
import { createLocalSource } from '@vielzeug/sourcerer';

type User = { email: string; id: number; name: string; role: string };

const users: User[] = [
  { email: 'alice@example.com', id: 1, name: 'Alice Johnson', role: 'admin' },
  { email: 'bob@example.com', id: 2, name: 'Bob Smith', role: 'editor' },
  { email: 'charlie@example.com', id: 3, name: 'Charlie Brown', role: 'viewer' },
  { email: 'dave@example.com', id: 4, name: 'Dave Alison', role: 'viewer' },
];

// Build the index once — at module load time, not inside render
const index = createIndex(users, {
  fields: [
    { field: 'name', weight: 2 },
    { field: 'email' },
    { field: 'role' },
  ],
  threshold: 0.25,
});

// Plug into sourcerer — the source delegates all search queries to Scout
const source = createLocalSource(users, {
  pageSize: 10,
  searchFn: toSearchFn(index),
});

// Subscribe to reactive data changes
const sub = source.subscribe(({ data, meta }) => {
  console.log(`${meta.totalItems} result(s):`);
  data.forEach(u => console.log(`  ${u.name} (${u.email})`));
});

// Scout handles the query — sourcerer handles paging and filtering
source.patch({ search: 'alice' });
// → 2 result(s):
//   Alice Johnson (alice@example.com)
//   Dave Alison (dave@example.com)

source.patch({ search: '' });
// → 4 result(s): (all users)

// Keep the index in sync with mutations
const newUser: User = { email: 'eve@example.com', id: 5, name: 'Eve Adams', role: 'admin' };
users.push(newUser);
index.add(newUser);

// Update the source with the new corpus
const updatedSource = createLocalSource(users, { searchFn: toSearchFn(index) });

// Cleanup
sub.dispose();
source.dispose();
updatedSource.dispose();