# Basic filtering

Filter data using conditions with addCondition(). The foundation of dcupl queries - add conditions to narrow down results based on field values, types, and boolean logic.

## Syntax

```typescript
list.catalog.query.addCondition({
  attribute: string,
  operator: Operator,
  value: any,
  options?: ConditionOptions
})
```

## Parameters

- `attribute` (`string`, required) — The property to filter on (supports dot notation for deep queries)
- `operator` (`Operator`, required) — Comparison operator: eq, in, typeof, isTruthy, size (see other pages for find, gt/gte/lt/lte)
- `value` (`any`, required) — Value to compare against. Type depends on operator (in needs an array).
- `options.transform` (`('lowercase' | 'trim' | 'removeWhitespace')[]`, optional) — Transform both attribute and search value before comparison
- `options.invert` (`boolean`, optional) — Negate the operator result (NOT logic). An item without a value (undefined/null) matches neither way, except with eq, in, typeof and isTruthy
- `options.arrayValueHandling` (`'some' | 'every'`, optional) — For array attributes: 'some' matches any element (default), 'every' requires all elements to match (and at least one element)

## Return type

Returns the updated query. Call items() to get matching results.

```typescript
// All query operators
type Operator = 'eq' | 'in' | 'find' | 'gt' | 'gte' | 'lt' | 'lte' | 'typeof' | 'isTruthy' | 'size';

// Options interface
interface ConditionOptions {
  transform?: ('lowercase' | 'trim' | 'removeWhitespace')[];
  invert?: boolean;
  arrayValueHandling?: 'some' | 'every';
  regexFlags?: string; // find only, e.g. 'i' (see Text Search)
}
```

## Usage notes

- Call addCondition() multiple times to add more conditions
- Conditions on different attributes use AND logic; conditions on the same attribute use OR (see Query Logic below)
- Use in to match any of several values (invert it for "not in")
- Use eq for exact matches, isTruthy for boolean-like checks
- typeof 'undefined' finds absent values, isTruthy false also finds null and ''; for wrongly typed source data, use quality checks (see Quality & Validation)
- Use options.invert to negate any operator (eq + invert = 'not equal')
- Transform option applies to BOTH the data value and your search value
- For text search, see Text Search. For ranges, see Range Queries.

## Related guides/docs

- [Text search](/sdk/queries/query-search)
- [Range queries](/sdk/queries/query-range)
- [Query logic](/sdk/queries/query-logic)
- [Results & Pagination](/sdk/queries/query-results)
- [Query operators reference](/sdk/api/query-operators)
- [Quality & validation](/sdk/advanced/quality-validation)

## Examples

**Filter by category**

Find all products in a specific category using exact match.

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

productList.catalog.query.addCondition({
  attribute: 'category',
  operator: 'eq',
  value: 'Electronics',
});

const electronics = productList.catalog.query.items();
console.log('Electronics products:', electronics.length);
```

**Filter active products**

Find products that are currently active using boolean filter.

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

productList.catalog.query.addCondition({
  attribute: 'status',
  operator: 'eq',
  value: 'active',
});

const activeProducts = productList.catalog.query.items();
```

**Match any of several values**

Use the in operator to match any value from a list. The value must be an array: anything else throws "invalid query - in operator needs an array value" and the condition is not added.

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

// in matches if the value equals any listed value
productList.catalog.query.addCondition({
  attribute: 'category',
  operator: 'in',
  value: ['Electronics', 'Computers'],
});

// Add options: { invert: true } for "not in"
const results = productList.catalog.query.items();
```

**Check in-stock items**

Use isTruthy to find items with truthy stock values (> 0, true, etc.).

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

// isTruthy matches: true, 1, 5, 100, "yes", non-empty values
// isTruthy excludes: false, 0, null, undefined, ""
productList.catalog.query.addCondition({
  attribute: 'inStock',
  operator: 'isTruthy',
  value: true,
});

const inStockProducts = productList.catalog.query.items();
```

**Find items with a price**

dcupl converts typed properties when it loads them: '49.99' becomes 49.99 and 'abc' becomes undefined. On a float property, typeof 'number' therefore separates items with a valid price from items without one. To find wrongly typed source data, use quality checks.

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

// typeof checks JavaScript types: 'string', 'number', 'boolean', 'object', 'undefined'
productList.catalog.query.addCondition({
  attribute: 'price',
  operator: 'typeof',
  value: 'number',
});

const pricedProducts = productList.catalog.query.items();
console.log('Products with a price:', pricedProducts.length);
```

**Filter by array length**

size compares the length of an array (or a string) with the value. Items without a value never match, not even size 0.

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

// Products with exactly two tags
productList.catalog.query.addCondition({
  attribute: 'tags',
  operator: 'size',
  value: 2,
});

const twoTagProducts = productList.catalog.query.items();
```

**Find missing data**

Find products without a description (data quality check). typeof "undefined" matches an absent value only: an explicit null or an empty string is a value. isTruthy false finds all three.

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

// Absent only: null is 'object' and '' is 'string'
productList.catalog.query.addCondition({
  attribute: 'description',
  operator: 'typeof',
  value: 'undefined',
});
// Absent, null or '': { attribute: 'description', operator: 'isTruthy', value: false }

const missingDescriptions = productList.catalog.query.items();
console.log('Products needing descriptions:', missingDescriptions.length);
```

**Exclude values with invert**

Find all products except discontinued ones using invert option.

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

// invert: true negates the operator (eq + invert = not equal)
productList.catalog.query.addCondition({
  attribute: 'status',
  operator: 'eq',
  value: 'discontinued',
  options: { invert: true },
});

const availableProducts = productList.catalog.query.items();
```

**Case-insensitive match**

Match brand name regardless of case using transform option.

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

// transform applies to both attribute value and search value
productList.catalog.query.addCondition({
  attribute: 'brand',
  operator: 'eq',
  value: 'nike',
  options: { transform: ['lowercase'] },
});

// Matches: "Nike", "NIKE", "nike", "NiKe"
const nikeProducts = productList.catalog.query.items();
```

**Filter array values**

Find products tagged with "featured" using array value handling.

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

// arrayValueHandling: 'some' (default) - match if ANY element matches
// arrayValueHandling: 'every' - match only if ALL elements match
//                               (an empty array never matches)
productList.catalog.query.addCondition({
  attribute: 'tags',
  operator: 'eq',
  value: 'featured',
  options: { arrayValueHandling: 'some' },
});

const featuredProducts = productList.catalog.query.items();
```

**Multiple conditions (AND)**

Combine filters on different attributes - all must match (AND logic).

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

// Conditions on different attributes are combined with AND
// (conditions on the same attribute are combined with OR)
productList.catalog.query.addCondition({
  attribute: 'status',
  operator: 'eq',
  value: 'active',
});

productList.catalog.query.addCondition({
  attribute: 'category',
  operator: 'eq',
  value: 'Electronics',
});

productList.catalog.query.addCondition({
  attribute: 'inStock',
  operator: 'isTruthy',
  value: true,
});

// Result: active AND Electronics AND in stock
const results = productList.catalog.query.items();
```

**Array of conditions**

Pass multiple conditions as an array for cleaner code.

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

productList.catalog.query.addCondition([
  { attribute: 'status', operator: 'eq', value: 'active' },
  { attribute: 'category', operator: 'eq', value: 'Electronics' },
  { attribute: 'inStock', operator: 'isTruthy', value: true },
]);

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

**SDK**

```typescript
// Basic filtering example
const list = dcupl.lists.create({ modelKey: 'Article' });

// Filter by category
list.catalog.query.addCondition({
  attribute: 'category',
  operator: 'eq',
  value: 'Running',
});

// Only in-stock items
list.catalog.query.addCondition({
  attribute: 'inStock',
  operator: 'isTruthy',
  value: true,
});

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