Functions API Reference

Complete reference for the dcupl SDK analytical functions (dcupl.fn).


Overview

The Functions API provides data analysis capabilities including aggregations, facets, suggestions, grouping, and pivot tables.

// Access via dcupl instance
dcupl.fn.facets({ ... });
dcupl.fn.aggregate({ ... });
dcupl.fn.suggest({ ... });
dcupl.fn.groupBy({ ... });
dcupl.fn.pivot({ ... });
dcupl.fn.metadata({ ... });

// Or via list catalog
list.catalog.fn.facets({ ... });

facets()

Generates faceted filter options with counts for building filter UIs.

Signature:

facets(options: FacetOptions): FacetResult[]

Parameters:

Parameter Type Required Description
options.attribute string Yes Property to generate facets for
options.count number No Maximum number of facets
options.excludeZeros boolean No Exclude facets with zero count
options.excludeUndefineds boolean No Exclude facets with undefined values
options.calculateResults boolean No Calculate result keys for each facet

Returns: DcuplFacet[]

type DcuplFacet = {
  key: string; // Unique facet key
  enabled: boolean; // Whether facet is enabled
  selected: boolean; // Whether facet is currently selected
  size: number; // Number of items with this value
  value: any; // The facet value
  entry?: any; // Referenced entry if applicable
  resultKeys?: string[]; // Keys of matching items (if calculateResults is true)
  children?: DcuplFacet[]; // Nested facets for hierarchical data
};

Examples:

Basic facets
const categories = dcupl.fn.facets({
  modelKey: 'Product',
  options: { attribute: 'category' },
});
// Returns:
// [
//   { key: 'electronics', value: 'electronics', size: 45, enabled: true, selected: false },
//   { key: 'clothing', value: 'clothing', size: 32, enabled: true, selected: false },
//   { key: 'books', value: 'books', size: 28, enabled: true, selected: false }
// ]
Facets with options
const topBrands = dcupl.fn.facets({
  modelKey: 'Product',
  options: {
    attribute: 'brand',
    count: 10,
    excludeZeros: true,
    excludeUndefineds: true,
  },
});
Facets via list catalog
const list = dcupl.lists.create({ modelKey: 'Product' });
list.catalog.query.addCondition({
  operator: 'eq',
  attribute: 'status',
  value: 'active',
});

// Facets reflect filtered data
const facets = list.catalog.fn.facets({
  attribute: 'category',
});
Reference facets
const suppliers = dcupl.fn.facets({
  modelKey: 'Product',
  options: { attribute: 'supplier' },
});
// Returns reference objects with entry:
// [{ key: 's1', value: 's1', size: 20, enabled: true, selected: false, entry: { key: 's1', name: 'Supplier A' } }]

aggregate()

Performs aggregation calculations on numeric properties.

Signature:

aggregate(options: AggregateOptions): AggregateResult

Parameters:

Parameter Type Required Description
options.attribute string Yes Numeric property to aggregate
options.types AggregationType[] Yes Aggregation types to calculate

Aggregation Types: 'sum', 'avg', 'min', 'max', 'count'

Returns: AggregateResult

type AggregateResult = {
  sum?: number;
  avg?: number;
  min?: number;
  max?: number;
  count?: number;
};

Examples:

Basic aggregation
const priceStats = dcupl.fn.aggregate({
  modelKey: 'Product',
  options: {
    attribute: 'price',
    types: ['sum', 'avg', 'min', 'max', 'count'],
  },
});
// Returns:
// {
//   sum: 25000,
//   avg: 250,
//   min: 9.99,
//   max: 999.99,
//   count: 100
// }
Aggregation with query filter
const expensiveStats = dcupl.fn.aggregate({
  modelKey: 'Product',
  query: {
    modelKey: 'Product',
    queries: [{ operator: 'gte', attribute: 'price', value: 100 }],
  },
  options: {
    attribute: 'price',
    types: ['avg', 'count'],
  },
});
Aggregation via list
const list = dcupl.lists.create({ modelKey: 'Order' });
const orderStats = list.catalog.fn.aggregate({
  attribute: 'total',
  types: ['sum', 'avg'],
});

suggest()

Provides autocomplete suggestions based on partial text input.

Signature:

suggest(options: SuggestOptions): string[]

Parameters:

Parameter Type Required Description
options.attribute string Yes Property to search
options.value any Yes Search term to match against
options.max number No Maximum suggestions (default: 10)

Returns: string[] - Array of matching values

Examples:

Basic suggestions
const suggestions = dcupl.fn.suggest({
  modelKey: 'Product',
  options: {
    attribute: 'name',
    value: 'lap',
    max: 5,
  },
});
// Returns: ['Laptop', 'Laptop Pro', 'Laptop Stand', 'Lapel Pin']
Suggestions with query context
const suggestions = dcupl.fn.suggest({
  modelKey: 'Product',
  query: {
    modelKey: 'Product',
    queries: [{ operator: 'eq', attribute: 'category', value: 'electronics' }],
  },
  options: {
    attribute: 'name',
    value: 'phone',
  },
});
// Only suggests from electronics category
Suggestions via list
const list = dcupl.lists.create({ modelKey: 'Product' });
list.catalog.query.addCondition({
  operator: 'eq',
  attribute: 'status',
  value: 'active',
});

const suggestions = list.catalog.fn.suggest({
  attribute: 'name',
  value: 'wire',
});
// Suggestions from active products only

groupBy()

Groups items by a property value.

Signature:

groupBy(options: GroupByOptions): Record<string, T[]>

Parameters:

Parameter Type Required Description
options.attribute string Yes Property to group by

Returns: Record - Object with group keys and item arrays

Examples:

Basic grouping
const grouped = dcupl.fn.groupBy({
  modelKey: 'Product',
  options: { attribute: 'category' },
});
// Returns:
// {
//   'electronics': [{ key: 'p1', ... }, { key: 'p2', ... }],
//   'clothing': [{ key: 'p3', ... }],
//   'books': [{ key: 'p4', ... }, { key: 'p5', ... }]
// }
Grouping with query
const grouped = dcupl.fn.groupBy({
  modelKey: 'Order',
  query: {
    modelKey: 'Order',
    queries: [{ operator: 'eq', attribute: 'status', value: 'completed' }],
  },
  options: { attribute: 'customer.key' },
});
// Groups completed orders by customer
Grouping via list
const list = dcupl.lists.create({ modelKey: 'Product' });
const byBrand = list.catalog.fn.groupBy({
  attribute: 'brand',
});

pivot()

Creates pivot table aggregations with row and column dimensions.

Signature:

pivot(options: PivotOptions): PivotResult

Parameters:

Parameter Type Required Description
options.rows PivotRowOption[] Yes Row dimension configurations
options.columns PivotColumnOption[] Yes Column dimension configurations
options.values AggregationOptions[] Yes Value aggregation configurations

Type definitions:

type PivotRowOption = {
  attribute: string; // Property to use as row dimension
};

type PivotColumnOption = {
  attribute: string; // Property to use as column dimension
};

type AggregationOptions = {
  attribute: string; // Property to aggregate
  type: AggregationType; // 'sum' | 'avg' | 'min' | 'max' | 'count'
};

Returns: PivotResult - Pivot table data structure

Examples:

Sales pivot table
const salesPivot = dcupl.fn.pivot({
  modelKey: 'Order',
  options: {
    rows: [{ attribute: 'customer.region' }],
    columns: [{ attribute: 'orderDate' }],
    values: [{ attribute: 'total', type: 'sum' }],
  },
});
// Returns pivot with regions as rows, dates as columns, summed totals
Product analysis pivot
const productPivot = dcupl.fn.pivot({
  modelKey: 'OrderItem',
  options: {
    rows: [{ attribute: 'product.category' }, { attribute: 'product.brand' }],
    columns: [{ attribute: 'order.year' }],
    values: [
      { attribute: 'quantity', type: 'sum' },
      { attribute: 'revenue', type: 'sum' },
    ],
  },
});
Pivot with query filter
const filteredPivot = dcupl.fn.pivot({
  modelKey: 'Order',
  query: {
    modelKey: 'Order',
    queries: [{ operator: 'gte', attribute: 'orderDate', value: new Date('2024-01-01') }],
  },
  options: {
    rows: [{ attribute: 'customer.tier' }],
    columns: [{ attribute: 'status' }],
    values: [{ attribute: 'total', type: 'avg' }],
  },
});

metadata()

Returns metadata about a query result set.

Signature:

metadata(options?: MetadataOptions): MetadataResult

Parameters:

Parameter Type Required Description
options MetadataOptions No Optional query context

Returns: MetadataResult

type MetadataResult = {
  count: number; // Items matching current query
  totalCount: number; // Total items in model
};

Examples:

Basic metadata
const meta = dcupl.fn.metadata({
  modelKey: 'Product',
});
// Returns: { count: 100, totalCount: 100 }
Metadata with query
const meta = dcupl.fn.metadata({
  modelKey: 'Product',
  query: {
    modelKey: 'Product',
    queries: [{ operator: 'eq', attribute: 'status', value: 'active' }],
  },
});
// Returns: { count: 85, totalCount: 100 }
Metadata via list
const list = dcupl.lists.create({ modelKey: 'Product' });
list.catalog.query.addCondition({
  operator: 'gte',
  attribute: 'price',
  value: 50,
});

const meta = list.catalog.fn.metadata();
// Returns count of products >= $50 vs total

Custom Operators

Register custom query operators for specialized filtering logic.

Signature:

dcupl.query.registerCustomOperator(
  operator: string,
  fn: (query: DcuplQuery, listItem: ListItem) => boolean
): void

Parameters passed to custom operator function:

Parameter Type Description
query DcuplQuery The query object containing attribute, operator, and value
listItem ListItem The list item being evaluated, with data property containing item values

Examples:

Between operator
dcupl.query.registerCustomOperator('$between', (query, listItem) => {
  const value = listItem.data[query.attribute];
  const [min, max] = query.value as [number, number];
  return value >= min && value <= max;
});

// Usage
dcupl.query.execute({
  modelKey: 'Product',
  queries: [{ attribute: 'price', operator: '$between', value: [100, 500] }],
});
Fuzzy match operator
dcupl.query.registerCustomOperator('$fuzzy', (query, listItem) => {
  const value = listItem.data[query.attribute];
  const pattern = query.value as string;
  // Simple fuzzy matching
  const regex = new RegExp(pattern.split('').join('.*'), 'i');
  return regex.test(value);
});

// Usage
dcupl.query.execute({
  modelKey: 'Product',
  queries: [
    { attribute: 'name', operator: '$fuzzy', value: 'lptp' }, // matches "laptop"
  ],
});
Distance operator (geospatial)
dcupl.query.registerCustomOperator('$nearBy', (query, listItem) => {
  const location = listItem.data[query.attribute];
  const { lat, lng, radius } = query.value as { lat: number; lng: number; radius: number };
  const distance = calculateDistance(location.lat, location.lng, lat, lng);
  return distance <= radius;
});

// Usage
dcupl.query.execute({
  modelKey: 'Store',
  queries: [
    {
      attribute: 'location',
      operator: '$nearBy',
      value: { lat: 40.7128, lng: -74.006, radius: 10 },
    },
  ],
});

Complete Examples

Faceted Search Implementation

faceted-search.ts
const list = dcupl.lists.create({
  modelKey: 'Product',
  listKey: 'search',
});

// Get available facets
function getFacets() {
  return {
    categories: list.catalog.fn.facets({ attribute: 'category' }),
    brands: list.catalog.fn.facets({ attribute: 'brand' }),
    priceRanges: list.catalog.fn.facets({ attribute: 'priceRange' }),
  };
}

// Apply facet filter
function applyFacet(attribute: string, value: any) {
  list.catalog.query.addCondition({
    operator: 'eq',
    attribute,
    value,
    queryKey: `facet-${attribute}`,
  });
}

// Remove facet filter
function removeFacet(attribute: string) {
  list.catalog.query.removeCondition({
    queryKey: `facet-${attribute}`,
  });
}

// Get results with metadata
function getResults() {
  return {
    items: list.catalog.query.items(),
    meta: list.catalog.fn.metadata(),
    facets: getFacets(),
  };
}

Dashboard Analytics

dashboard.ts
function getDashboardData() {
  // Product statistics
  const productStats = dcupl.fn.aggregate({
    modelKey: 'Product',
    options: {
      attribute: 'price',
      types: ['avg', 'min', 'max', 'count'],
    },
  });

  // Sales by category
  const salesByCategory = dcupl.fn.pivot({
    modelKey: 'OrderItem',
    options: {
      rows: [{ attribute: 'product.category' }],
      columns: [],
      values: [{ attribute: 'revenue', type: 'sum' }],
    },
  });

  // Top customers
  const topCustomers = dcupl.fn.groupBy({
    modelKey: 'Order',
    options: { attribute: 'customer.key' },
  });

  // Inventory alerts
  const lowStock = dcupl.fn.metadata({
    modelKey: 'Product',
    query: {
      modelKey: 'Product',
      queries: [{ operator: 'lt', attribute: 'stock', value: 10 }],
    },
  });

  return {
    productStats,
    salesByCategory,
    topCustomers: Object.entries(topCustomers)
      .map(([key, orders]) => ({
        customer: key,
        orderCount: orders.length,
        totalSpent: orders.reduce((sum, o) => sum + o.total, 0),
      }))
      .sort((a, b) => b.totalSpent - a.totalSpent)
      .slice(0, 10),
    lowStockCount: lowStock.count,
  };
}

Search Autocomplete

autocomplete.ts
const list = dcupl.lists.create({ modelKey: 'Product' });

async function getAutocomplete(term: string) {
  if (term.length < 2) return [];

  const suggestions = list.catalog.fn.suggest({
    attribute: 'name',
    value: term,
    max: 8,
  });

  // Also get category suggestions
  const categoryMatches = list.catalog.fn.suggest({
    attribute: 'category',
    value: term,
    max: 3,
  });

  return {
    products: suggestions,
    categories: categoryMatches,
  };
}

See Also