# Text search

Search text fields with the find operator. Wrap the search term in slashes, like /shoe/, to match it anywhere in the value (the text between the slashes is a regular expression), and set regexFlags to i for case-insensitive search. Without slashes, find compares the whole value exactly. Essential for search bars, autocomplete, and text filtering.

## Syntax

```typescript
list.catalog.query.addCondition({
  attribute: string,
  operator: 'find',
  value: string | object, // '/term/' = contains, 'term' = exact, { brand: 'Apple' } = partial object match
  options?: SearchOptions
})
```

## Parameters

- `attribute` (`string`, required) — The text field to search in
- `operator` (`'find'`, required) — The find operator for text and pattern matching
- `value` (`string | object`, required) — /term/ matches the term anywhere in the value (regex); a bare term without slashes must equal the whole value. An object matches part of a json value ({ brand: 'Apple' }), see the Query Operators Reference
- `options.regexFlags` (`string`, optional) — Regex flags for a '/term/' value, e.g. 'i' for case-insensitive. Put flags here, not after the closing slash ('/term/i' is an exact match)
- `options.transform` (`('lowercase' | 'trim' | 'removeWhitespace')[]`, optional) — Transform both data and search value before comparison
- `options.invert` (`boolean`, optional) — Exclude matches (find items NOT containing the term). Items without a value are left out too
- `options.arrayValueHandling` (`'some' | 'every'`, optional) — For array fields: 'some' (any element) or 'every' (all elements)

## Return type

addCondition() returns the updated query (DcuplGlobalQueryOptions); call items() for the matching items:

```typescript
// list.catalog.query
addCondition(condition: DcuplQuery | DcuplQuery[]): DcuplGlobalQueryOptions<any>; // the list's query
items(options?: DcuplListQueryOptions<T>): T[];
```

## Usage notes

- '/phone/' matches "Smartphone" and "headphones", not "Phone Case"; with regexFlags: 'i' it matches all three. '/^run/' with 'i' matches "Running Shoes" and "runner", not "outrun". 'Phone Case' (no slashes) matches only that whole value
- A /regex/ is tested against non-string values as strings: /^1/ on a number attribute matches 150 and 100
- Transforms: 'lowercase', 'trim' (leading/trailing whitespace) and 'removeWhitespace' (all whitespace)
- Wrap the term in slashes (/term/) for substring search; a bare string must match the whole value
- Set regexFlags to i for case-insensitive, user-facing search
- Escape regex characters (. \* + ? ^ $ { } ( ) | \[ \] \\) in user input before wrapping it in slashes
- Transforms apply to BOTH the data value AND your search term
- Use multiple find conditions on same field for OR logic
- Combine with other operators (eq, gte) for filtered search
- For suggestions/autocomplete, see catalog.fn.suggest()

## Related methods

- `catalog.fn.suggest()` — Get value suggestions for autocomplete (case-insensitive prefix match, or a /regex/)

## Related guides/docs

- [Basic filtering](/sdk/queries/query-filter)
- [Results & Pagination](/sdk/queries/query-results)
- [Query Operators Reference: find](/sdk/api/query-operators#find)

## Examples

**Substring search**

Wrap the term in slashes to find products whose name contains "Bike" (case-sensitive by default).

```typescript
const productList = dcupl.lists.create({ modelKey: 'Product' });

productList.catalog.query.addCondition({
  attribute: 'name',
  operator: 'find',
  value: '/Bike/',
});

// Matches: "Mountain Bike", "Bike Helmet"
// No match: "bike lock" (case-sensitive), "Bicycle"
const results = productList.catalog.query.items();
```

**Case-insensitive search**

Add regexFlags: 'i' to ignore case (recommended for user-facing search).

```typescript
const productList = dcupl.lists.create({ modelKey: 'Product' });

productList.catalog.query.addCondition({
  attribute: 'name',
  operator: 'find',
  value: '/running/',
  options: { regexFlags: 'i' },
});

// Matches: "Running Shoes", "Trail Running Shoes", "running socks"
const results = productList.catalog.query.items();
```

**Search with user input**

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

```typescript
const productList = dcupl.lists.create({ modelKey: 'Product' });

const escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, (char) => '\\' + char);
const userInput = '  shoe ';

productList.catalog.query.addCondition({
  attribute: 'name',
  operator: 'find',
  value: `/${escapeRegExp(userInput.trim())}/`,
  options: { regexFlags: 'i' },
});

// Matches: "Running Shoes", "Trail Running Shoes", "Snowshoes"
const results = productList.catalog.query.items();
```

**Pattern search**

The text between the slashes is a JavaScript regular expression, so anchors and alternation work too.

```typescript
const productList = dcupl.lists.create({ modelKey: 'Product' });

// Names that start with "trail" or "mountain", any case
productList.catalog.query.addCondition({
  attribute: 'name',
  operator: 'find',
  value: '/^(trail|mountain)/',
  options: { regexFlags: 'i' },
});

// Matches: "Trail Running Shoes", "Mountain Bike"
// No match: "Bike Trailer" (does not start with it)
const results = productList.catalog.query.items();
```

**Exact match (no slashes)**

A bare string compares the whole value. Transforms normalize both sides before comparing.

```typescript
const productList = dcupl.lists.create({ modelKey: 'Product' });

// trim: remove leading/trailing whitespace
// removeWhitespace: remove ALL whitespace
productList.catalog.query.addCondition({
  attribute: 'sku',
  operator: 'find',
  value: 'ABC123',
  options: { transform: ['trim', 'removeWhitespace'] },
});

// Matches: " ABC 123 ", "ABC123", "A B C 1 2 3"
// No match: "ABC1234" (a bare string never matches part of a value)
const results = productList.catalog.query.items();
```

**Search multiple fields**

Search name OR description by putting both conditions in one or-group.

```typescript
const productList = dcupl.lists.create({ modelKey: 'Product' });

// Conditions on different attributes are combined with AND,
// so group them with groupType 'or'
productList.catalog.query.addGroup({
  groupKey: 'search',
  groupType: 'or',
  queries: [
    { attribute: 'name', operator: 'find', value: '/waterproof/', options: { regexFlags: 'i' } },
    { attribute: 'description', operator: 'find', value: '/waterproof/', options: { regexFlags: 'i' } },
  ],
});

// Matches: "Waterproof Jacket", and "Hiking Boots" (described as waterproof)
const results = productList.catalog.query.items();
```

**Exclude search terms**

Find products NOT containing "refurbished" using invert.

```typescript
const productList = dcupl.lists.create({ modelKey: 'Product' });

productList.catalog.query.addCondition({
  attribute: 'name',
  operator: 'find',
  value: '/refurbished/',
  options: {
    regexFlags: 'i',
    invert: true, // Exclude matches
  },
});

// Returns products without "refurbished" in the name
const newProducts = productList.catalog.query.items();
```

**Search in array fields**

Find products where ANY tag contains the search term.

```typescript
const productList = dcupl.lists.create({ modelKey: 'Product' });

productList.catalog.query.addCondition({
  attribute: 'tags',
  operator: 'find',
  value: '/sale/',
  options: {
    regexFlags: 'i',
    arrayValueHandling: 'some', // Match if ANY element contains "sale"
  },
});

// Matches: tags = ["summer-sale", "clearance"]
// Matches: tags = ["new", "SALE-2024"]
const saleProducts = productList.catalog.query.items();
```

**Search with category filter**

Combine text search with other filters.

```typescript
const productList = dcupl.lists.create({ modelKey: 'Product' });

// Text search
productList.catalog.query.addCondition({
  attribute: 'name',
  operator: 'find',
  value: '/pro/',
  options: { regexFlags: 'i' },
});

// Combined with category filter (AND logic)
productList.catalog.query.addCondition({
  attribute: 'category',
  operator: 'eq',
  value: 'Electronics',
});

// Result: Electronics products with "pro" in the name
const results = productList.catalog.query.items();
```

**SDK**

```typescript
// Case-insensitive substring search
const list = dcupl.lists.create({ modelKey: 'Article' });

// Find products whose name contains "shoe" (any case)
list.catalog.query.addCondition({
  attribute: 'productName',
  operator: 'find',
  value: '/shoe/',
  options: { regexFlags: 'i' },
});

// Get search results
const results = list.catalog.query.items();
console.log('Found:', results.length, 'matching products');
```
