# 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:

```typescript
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):

```typescript
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:

```typescript
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](/sdk/advanced/caching#cache-configuration)), so a debounce also keeps that cache small.

```typescript
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:

```typescript
// 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):

```typescript
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):

```typescript
// 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

```typescript
// 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:

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

## Next steps

- [suggest()](/sdk/fn/suggest) — Suggest function reference
- [Query Filter](/sdk/queries/query-filter) — Filter operators
- [Faceted search](/sdk/advanced/faceted-search) — Combine search with facets
