.md

Query logic

Combine conditions with AND/OR logic. Conditions on different attributes are combined with AND, conditions on the same attribute with OR. For any other combination, use addGroup with an explicit groupType. Build complex boolean expressions for advanced filtering with faceted search patterns.

Syntax

list.catalog.query.addGroup({
  groupKey?: string,
  groupType?: 'and' | 'or',
  queries: Array<Condition | Group>
})

Parameters

groupKeystringOptional
Names the group. Adding a group with an existing groupKey extends it; setGroup() replaces it; removeGroup(groupKey) removes it. The keyed group is found at any nesting depth (a top-level match first) and changed where it is. Removing a group that leaves its wrapper group empty removes the wrapper too (and every wrapper above it that becomes empty). addGroup() always adds a group without a key as a new group; setGroup() without a key replaces the first keyless group.
groupType'and' | 'or'Optional
Logic within the group. Set it explicitly: a group without groupType is stored as 'or', whether it is new or replaces an existing group with setGroup() (addGroup() without groupType extending a keyed group keeps that group's groupType), while a nested group inside queries without groupType is evaluated as 'and'.
queriesArray<Condition | Group>Required
Conditions or nested groups in this group

Return type

addGroup(), setGroup() and addCondition() return the updated query (DcuplGlobalQueryOptions). Use current() to inspect it, clear() to reset:

// Shape of the returned query, e.g. after addCondition() on category and brand
type DcuplGlobalQueryOptions<T> = {
  modelKey: string;
  groupKey: 'root';
  groupType: 'and'; // top-level groups are ANDed
  queries: Array<DcuplQuery | DcuplQueryGroup>; // here: [{ groupKey: 'category', groupType: 'or', queries: [...] },
                                                //        { groupKey: 'brand', groupType: 'or', queries: [...] }]
};

Usage notes

  • Default: addCondition() puts each condition into a group named after its attribute, so different attributes use AND and the same attribute uses OR
  • Use addGroup with groupType: 'or' for OR across different attributes
  • Use addGroup with groupType: 'and' for ranges on one attribute
  • Different groups are ANDed together at the top level
  • Use setCondition() for single-select dropdowns (replaces the conditions on that attribute)
  • Use addCondition() for multi-select checkboxes (accumulates, OR within the attribute)
  • removeCondition() removes one condition (pass the full condition); removeGroup(groupKey) removes a whole group
  • clear() removes all conditions

Related methods

  • catalog.query.addGroup() — Add or extend a group of conditions
  • catalog.query.setGroup() — Replace a group by its groupKey
  • catalog.query.current() — Get current query structure
  • catalog.query.isEmpty() — Check if any conditions exist
  • catalog.query.clear() — Remove all conditions
  • catalog.query.removeCondition() — Remove specific condition

Examples

Default AND logic

Multiple conditions on different attributes use AND by default.

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

// These conditions are ANDed automatically
productList.catalog.query.addCondition({
  attribute: 'category',
  operator: 'eq',
  value: 'Electronics',
});

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

// Result: Electronics AND in stock
const results = productList.catalog.query.items();
OR logic with same attribute

Multiple values for same attribute create OR logic by default.

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

// Same attribute = OR logic
productList.catalog.query.addCondition({
  attribute: 'category',
  operator: 'eq',
  value: 'Electronics',
});

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

// Result: Electronics OR Computers
const results = productList.catalog.query.items();
Explicit OR group

Use addGroup with groupType: "or" for OR logic across different attributes.

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

// Conditions on different attributes are combined with AND,
// so put them in one group with groupType 'or'
productList.catalog.query.addGroup({
  groupKey: 'special',
  groupType: 'or',
  queries: [
    { attribute: 'featured', operator: 'eq', value: true },
    { attribute: 'onSale', operator: 'eq', value: true },
  ],
});

// Result: featured OR on sale
const specialProducts = productList.catalog.query.items();
Multiple OR groups (AND between groups)

Combine multiple OR groups with AND logic between them.

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

// Category group (OR): Electronics OR Computers
productList.catalog.query.addGroup({
  groupKey: 'category-group',
  groupType: 'or',
  queries: [
    { attribute: 'category', operator: 'eq', value: 'Electronics' },
    { attribute: 'category', operator: 'eq', value: 'Computers' },
  ],
});

// Brand group (OR): Apple OR Samsung
productList.catalog.query.addGroup({
  groupKey: 'brand-group',
  groupType: 'or',
  queries: [
    { attribute: 'brand', operator: 'eq', value: 'Apple' },
    { attribute: 'brand', operator: 'eq', value: 'Samsung' },
  ],
});

// Result: (Electronics OR Computers) AND (Apple OR Samsung)
const results = productList.catalog.query.items();
Faceted search pattern

Build a typical e-commerce filter with multiple facets.

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

function applyFacetedFilters(filters: {
  categories?: string[];
  brands?: string[];
  priceMin?: number;
  priceMax?: number;
}) {
  const query = productList.catalog.query;
  query.clear();

  // Categories (OR within group). Key the group by its attribute:
  // fn.facets({ attribute: 'category' }) leaves the group with that key out when counting
  if (filters.categories?.length) {
    query.addGroup({
      groupKey: 'category',
      groupType: 'or',
      queries: filters.categories.map((cat) => ({
        attribute: 'category',
        operator: 'eq' as const,
        value: cat,
      })),
    });
  }

  // Brands: the 'in' operator matches any of the listed values
  if (filters.brands?.length) {
    query.addCondition({ attribute: 'brand', operator: 'in', value: filters.brands });
  }

  // Price range (AND within group)
  const price = [];
  if (filters.priceMin !== undefined) {
    price.push({ attribute: 'price', operator: 'gte' as const, value: filters.priceMin });
  }
  if (filters.priceMax !== undefined) {
    price.push({ attribute: 'price', operator: 'lte' as const, value: filters.priceMax });
  }
  if (price.length) {
    query.addGroup({ groupKey: 'price', groupType: 'and', queries: price });
  }

  // Result: (cat1 OR cat2) AND (brand1 OR brand2) AND (price range)
  return query.items();
}
Replace vs. add

Control whether a new condition replaces or adds to the existing ones on the same attribute.

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

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

// setCondition replaces the conditions on 'category'
productList.catalog.query.setCondition({
  attribute: 'category',
  operator: 'eq',
  value: 'Books',
});

// Now only Books products match
// (addCondition here would give Electronics OR Books)
const results = productList.catalog.query.items();
Remove specific condition

Remove a specific condition while keeping others.

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

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

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

// Remove just the Electronics filter (pass the full condition)
productList.catalog.query.removeCondition({
  attribute: 'category',
  operator: 'eq',
  value: 'Electronics',
});

// Now only Books products match
const results = productList.catalog.query.items();

// Remove all conditions on 'category'
productList.catalog.query.removeGroup('category');
Query state inspection

Check current query state and reset.

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

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

// Check current query state
const currentQuery = productList.catalog.query.current();
console.log('Current query:', currentQuery);

// Check if query has any conditions
const isEmpty = productList.catalog.query.isEmpty();
console.log('Is empty:', isEmpty); // false

// Check for a specific group (addCondition groups are keyed by attribute)
const hasCategory = productList.catalog.query.has({ groupKey: 'category' });

// Clear all conditions
productList.catalog.query.clear();

// Now returns all products
const allProducts = productList.catalog.query.items();
Conditional UI based on query state

Use isEmpty() and has() to drive UI decisions.

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

// Check if query has any conditions
if (productList.catalog.query.isEmpty()) {
  // Show all products view
  showAllProductsMessage();
}

// Check if a specific filter is active
if (productList.catalog.query.has({ groupKey: 'category' })) {
  // Show reset button for category filter
  showCategoryResetButton();
}

// Combine with faceted search
function updateFilterUI() {
  const query = productList.catalog.query;

  // Show "clear all" only when filters are active
  clearAllButton.hidden = query.isEmpty();

  // Show individual reset buttons per filter
  categoryReset.hidden = !query.has({ groupKey: 'category' });
  brandReset.hidden = !query.has({ groupKey: 'brand' });
  priceReset.hidden = !query.has({ groupKey: 'price' });
}
// Faceted search: (category1 OR category2) AND (brand1)
const list = dcupl.lists.create({ modelKey: 'Article' });

// Conditions on the same attribute are combined with OR
list.catalog.query.addCondition([
  { attribute: 'category', operator: 'eq', value: 'Running' },
  { attribute: 'category', operator: 'eq', value: 'Training' },
]);

// Conditions on different attributes are combined with AND
list.catalog.query.addCondition({
  attribute: 'brand',
  operator: 'eq',
  value: 'Nike',
});

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