.md

Search & autocomplete

Build search with autocomplete using suggest() for suggestions and setCondition() for filtering results.

Key dcupl functions

Function Purpose
catalog.fn.suggest() Get autocomplete suggestions for an attribute
query.setCondition() Set filter condition (replaces the conditions on that attribute)
query.addCondition() Add filter condition (ORed with others on the same attribute)

Get suggestions

Use suggest() to get autocomplete suggestions as the user types:

const suggestions = productList.catalog.fn.suggest({
  attribute: 'name',
  value: 'lap', // User input
  max: 5, // Limit results
});

// [{ value: 'Laptop Pro', keys: ['p1'] }, { value: 'Laptop Air', keys: ['p2'] }, { value: 'Laptop Stand', keys: ['p8'] }]

Each suggestion contains:

  • value - The suggested term
  • keys - Item keys that match this value

A plain search term matches the start of the value, ignoring case: 'lap' suggests "Laptop Pro" but not "Gaming Laptop".

Match anywhere in the value

Wrap the term in slashes for a regular expression and set regexFlags: 'i' to ignore case ('/lap/i' would be compared as a plain value and match nothing):

const suggestions = productList.catalog.fn.suggest({
  attribute: 'name',
  value: '/lap/',
  regexFlags: 'i',
  max: 5,
});

// Also suggests "Gaming Laptop"

Escape regex characters in user input before wrapping it in slashes.

Suggest from multiple attributes

Get suggestions from different attributes and combine them:

const nameSuggestions = productList.catalog.fn.suggest({
  attribute: 'name',
  value: userInput,
  max: 5,
});

const brandSuggestions = productList.catalog.fn.suggest({
  attribute: 'brand',
  value: userInput,
  max: 3,
});

// One dropdown: label each entry with the attribute it came from
const combined = [
  ...nameSuggestions.map((s) => ({ ...s, source: 'name' })),
  ...brandSuggestions.map((s) => ({ ...s, source: 'brand' })),
];
// [{ value: 'Laptop Pro', keys: ['p1'], source: 'name' }, ..., { value: 'Lenovo', keys: ['p4', 'p9'], source: 'brand' }]

The source tells you which condition to set when the user picks an entry.

Handling input

max defaults to 20. In a search box, wait for a minimum length (for example 2 characters) and debounce the input (for example 150 ms) before you call suggest(). Each distinct term is cached on the list until the next update that affects it (see Caching), so a debounce also keeps that cache small.

let timer: ReturnType<typeof setTimeout> | undefined;

function onSearchInput(userInput: string) {
  clearTimeout(timer);
  if (userInput.length < 2) return;
  timer = setTimeout(() => {
    const suggestions = productList.catalog.fn.suggest({ attribute: 'name', value: userInput, max: 5 });
    // render suggestions
  }, 150);
}

Apply search filter

When user selects a suggestion or submits search, filter the results. Wrap the term in slashes ('/term/') for substring search; a bare string must match the whole value. Set regexFlags: 'i' for case-insensitive search:

// Text search with 'find' operator: names containing "laptop" in any case
productList.catalog.query.setCondition({
  operator: 'find',
  attribute: 'name',
  value: '/laptop/',
  options: { regexFlags: 'i' },
});

const results = productList.catalog.query.items();

Filter by exact match

When user selects a specific suggestion (e.g., a brand):

productList.catalog.query.setCondition({
  operator: 'eq',
  attribute: 'brand',
  value: 'Apple',
});

const results = productList.catalog.query.items();

Clear and re-apply

setCondition() replaces the previous condition on the same attribute, so a new search term needs no clearing. Call clear() only to drop all other filters as well (it also resets sort and paging):

// Optional: remove all conditions and options
productList.catalog.query.clear();

// Apply new search (escape regex characters in user input)
const escaped = newSearchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

productList.catalog.query.setCondition({
  operator: 'find',
  attribute: 'name',
  value: `/${escaped}/`,
  options: { regexFlags: 'i' },
});

Complete flow

// 1. User types "lap"
const suggestions = productList.catalog.fn.suggest({
  attribute: 'name',
  value: 'lap',
  max: 5,
});

// 2. User selects "Laptop Pro" from suggestions (a bare string matches the whole value)
productList.catalog.query.setCondition({
  operator: 'find',
  attribute: 'name',
  value: 'Laptop Pro',
});

// 3. Get filtered results
const results = productList.catalog.query.items();

suggest() draws from the list's current result by default (relevantData: 'filtered'), so once the search condition on name is set, the next suggest() on name only returns "Laptop Pro". To keep suggesting every name while a search is active, leave the search's group out:

const suggestions = productList.catalog.fn.suggest({
  attribute: 'name',
  value: userInput,
  max: 5,
  relevantData: 'excludeQuery',
  excludeQuery: { groupKey: 'name' }, // the group setCondition() created for 'name'
});