.md

Models API

The Models API (dcupl.models) provides methods for managing data model definitions in dcupl. Models define the structure, types, and relationships of your data.

Methods

set()

Adds a model definition. dcupl builds the model on the next init() or update().

If a model with the same key already exists, set() replaces its definition (SDK 2.0): a changed attribute takes effect and an attribute left out of the new definition is removed. To add to a definition instead, use update(). dcupl stores a copy, so later changes to the object you passed do not reach the instance. After init(), the change takes effect on the next dcupl.update(), which rebuilds all data like init() (a full update), and lists created earlier serve the new definition. Define each property only once within a definition: a property key given twice adds a second definition and reports a DuplicatePropertyKey quality error. Model files loaded by the app loader are the exception: files with the same key are still merged (first declaration wins).

set() does not throw on an invalid definition. A model with an empty key or keyProperty (or a malformed autoGenerateProperties, properties or references) is not registered, and a property with an unknown type is left out of the model; each case records an InvalidModelDefinition quality error, which stays until a valid set() for the same key replaces the definition.

Signature:

set(model: ModelDefinition): void

Parameters:

Parameter Type Required Description
model ModelDefinition Yes The model definition to set

Examples:

Basic model
dcupl.models.set({
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
  ],
});
Model with references
dcupl.models.set({
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
  ],
  references: [{ key: 'category', type: 'singleValued', model: 'Category' }],
});
Model with common options
dcupl.models.set({
  key: 'Product',
  keyProperty: 'id',
  autoGenerateKey: false,
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
    { key: 'tags', type: 'Array<string>', separator: ',' },
  ],
  references: [{ key: 'category', type: 'singleValued', model: 'Category' }],
  quality: {
    enabled: true,
    attributes: {
      required: true,
    },
  },
});

update()

Merges a definition into an existing model: properties and references are appended, scalar fields of the later definition win, and an attribute keeps its first declaration. The change is applied on the next dcupl.update(), which rebuilds all data like init(); get() then returns the merged definition. Use set() to replace a definition.

Signature:

update(model: ModelDefinition): void

Parameters:

Parameter Type Required Description
model ModelDefinition Yes The model definition to merge

Examples:

Update existing model
dcupl.models.update({
  key: 'Product',
  properties: [{ key: 'description', type: 'string' }],
});
Add reference to existing model
dcupl.models.update({
  key: 'Product',
  references: [{ key: 'manufacturer', type: 'singleValued', model: 'Manufacturer' }],
});

get()

Retrieves the definition of a built model by key. It returns undefined for a model that is not built yet: before init(), or for a key first set after init() until the next dcupl.update(). For a built model it returns the definition the model was built from; a set() or update() on it shows in get() only after the next dcupl.update(). A set() with an invalid definition unregisters the model at once, so get() returns undefined. For a model with autoGenerateProperties, the inferred properties are included. The result is a copy: changing it has no effect on the instance (edit a model with get → change → set() → dcupl.update()). Inline data given in the definition is not included.

Signature:

get(modelKey: string): ModelDefinition | undefined

Parameters:

Parameter Type Required Description
modelKey string Yes The key of the model to retrieve

Returns: ModelDefinition | undefined - The model definition or undefined if the model is not built

Examples:

Get a model
const model = dcupl.models.get('Product');
if (model) {
  console.log(model.properties);
  console.log(model.references);
}
Check if model exists
const exists = dcupl.models.get('Product') !== undefined;
Access model properties
const model = dcupl.models.get('Product');
if (model) {
  const properties = model.properties || [];
  const references = model.references || [];
}

remove()

Unregisters a model at once (SDK 2.0): its definition, its data (rows from data.* or inline data) and its lists. Queries on it throw, its errors leave quality.errors.summary(), and the next init() or update() rebuilds all data like init() without bringing it back; references from other models then resolve as to a missing model (MissingModel). A later set() registers the model again; pass its data again.

Signature:

remove(modelKey: string): void

Parameters:

Parameter Type Required Description
modelKey string Yes The key of the model to remove

Examples:

Remove a model
dcupl.models.remove('Product');
Remove multiple models
['Product', 'Category', 'Tag'].forEach((key) => {
  dcupl.models.remove(key);
});

keys()

Returns the keys of all built models. dcupl's internal $Dcupl… models are left out.

Signature:

keys(): string[]

Returns: string[] - Array of model keys

Examples:

Get all model keys
const modelKeys = dcupl.models.keys();
console.log(modelKeys); // ['Product', 'Category', 'Tag']
Check if a model exists
const hasProduct = dcupl.models.keys().includes('Product');
Count models
const modelCount = dcupl.models.keys().length;

Type definitions

ModelDefinition

Defines the structure of a data model.

Type Definition:

type ModelDefinition<ModelNames extends string = string> = {
  key: ModelNames;
  properties?: Property[];
  references?: Reference[];
  data?: RawItem[];
  keyProperty?: string | string[];
  keyPropertySeparator?: string;
  autoGenerateKey?: boolean;
  autoGenerateProperties?: boolean | { deep?: boolean; sampleSize?: number };
  supportsAutoCreation?: boolean;
  valueMappings?: ValueMappingConfig[];
  meta?: ModelMetadata;
  quality?: ModelQualityConfig;
};

Properties:

Property Type Description
key string Unique model identifier
properties Property[] Array of property definitions
references Reference[] Array of reference definitions
data RawItem[] Optional inline data for the model
keyProperty string | string[] Property to use as item key (default: 'key'); an array builds a composite key
keyPropertySeparator string Separator joining the parts of a composite key (default: '::')
autoGenerateKey boolean Auto-generate unique keys for items without one (default: false)
autoGenerateProperties boolean | object Infer property definitions from the data (default: false), see below
supportsAutoCreation boolean Allow automatic item creation when referenced by other models (default: false)
valueMappings ValueMappingConfig[] Transform values during data loading
meta ModelMetadata Metadata attached to the model (name is required, other keys are free)
quality ModelQualityConfig Quality validation configuration

Examples:

Minimal model
const model: ModelDefinition = {
  key: 'Product',
  properties: [{ key: 'name', type: 'string' }],
};
Complete model
const model: ModelDefinition = {
  key: 'Product',
  keyProperty: 'productId',
  autoGenerateKey: false,
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
    { key: 'createdAt', type: 'date', inputFormat: 'yyyy-MM-dd' },
  ],
  references: [
    { key: 'category', type: 'singleValued', model: 'Category' },
    { key: 'tags', type: 'multiValued', model: 'Tag' },
  ],
  quality: {
    enabled: true,
    attributes: {
      required: true,
    },
  },
};

Model options

keyProperty

Specifies which property in your data should be used as the unique item identifier. By default, dcupl looks for a property named key.

Using a custom key property
const model: ModelDefinition = {
  key: 'Product',
  keyProperty: 'productId', // Use 'productId' instead of 'key'
  properties: [
    { key: 'productId', type: 'string' },
    { key: 'name', type: 'string' },
  ],
};

// Data can now use 'productId' as the identifier
dcupl.data.set(
  [
    { productId: 'SKU-001', name: 'Widget' },
    { productId: 'SKU-002', name: 'Gadget' },
  ],
  { model: 'Product' },
);

An array of property names builds a composite key: dcupl joins the values in array order with keyPropertySeparator (default '::') and writes the result to the item's key. Each value is converted with String(), so a missing part becomes 'undefined' (or 'null') in the key.

Composite key
dcupl.models.set({
  key: 'Translation',
  keyProperty: ['sku', 'lang'],
  properties: [
    { key: 'sku', type: 'string' },
    { key: 'lang', type: 'string' },
    { key: 'title', type: 'string' },
  ],
});

dcupl.data.set([{ sku: 'A1', lang: 'en', title: 'Widget' }], { model: 'Translation' });
await dcupl.init();

dcupl.query.one({ modelKey: 'Translation', itemKey: 'A1::en' }); // { key: 'A1::en', sku: 'A1', lang: 'en', title: 'Widget' }

A row with no value for the key property (undefined or null; for a composite key: every part) is dropped and reported as a MissingKey quality error. On a model with derived, grouped, resolved or query references, or derived or expression properties, such a row currently also makes init(), or an update() that rebuilds (for example after data.set(), or after upsert() on a model with query references), log "Initialization failed" or "Update failed" and resolve false. Processing stops at that row, so remove such rows before loading. After init(), a keyless row in an upsert() that dcupl.update() applies on the partial path is currently dropped without the error; the next full rebuild reports it, and on those models resolves false. A composite key with only some parts missing is kept, with 'undefined' or 'null' for those parts. An empty string counts as a value: a blank key cell in a CSV file gives the key '' and the row is kept; further rows with a blank key replace it and are reported as NonUniqueKey.

autoGenerateKey

When true, dcupl generates a unique 10-character key for items that don't have one. Useful when your data doesn't have natural identifiers.

Auto-generating keys
const model: ModelDefinition = {
  key: 'LogEntry',
  autoGenerateKey: true,
  properties: [
    { key: 'message', type: 'string' },
    { key: 'timestamp', type: 'date' },
  ],
};

// Data without keys - dcupl generates them automatically
dcupl.data.set(
  [
    { message: 'User logged in', timestamp: '2025-01-15' },
    { message: 'User logged out', timestamp: '2025-01-15' },
  ],
  { model: 'LogEntry' },
);

// Items will have auto-generated keys like 'a8f3k2m9x1'

autoGenerateProperties

When true, dcupl infers property definitions from the data structure. Useful for quick prototyping or when working with dynamic data.

Auto-generating properties
const model: ModelDefinition = {
  key: 'DynamicData',
  autoGenerateProperties: true,
  // No properties defined - they will be inferred from data
};

dcupl.data.set([{ key: 'item1', name: 'Test', count: 42, active: true }], { model: 'DynamicData' });

// Properties 'name', 'count', 'active' are automatically created

autoGenerateProperties accepts true, { deep: true } (scan every row), { sampleSize: N } (scan up to N rows) or { deep: false } (the first row and nothing else). With true the first row names the properties and their types, and a column it types as a number is checked against every row: a later decimal makes it float, a later zero-padded value makes it string. In SDK 2.0, string values are inferred too: '19.99' becomes float, '3' becomes int, 'true' / 'false' (in any case) become boolean, '1' / '0' stay int, and a strict ISO-8601 date becomes date, so CSV columns are not all string. The key column stays string. A digit string is a number only when the number keeps every character: zero-padded values ('007', a zip code '01234') and integers beyond Number.MAX_SAFE_INTEGER are inferred as string, and so is a column that mixes them with numbers. Properties you declare in properties keep their declared type, so declare one where you need a specific type (for example int for months '01' to '12', which reads '07' as 7).

Inferring types from string values
dcupl.models.set({ key: 'Row', autoGenerateProperties: { deep: true } });

dcupl.data.set(
  [
    { key: 'r1', price: '5' },
    { key: 'r2', price: '19.99' },
  ],
  { model: 'Row' },
);
await dcupl.init();

// price is inferred as 'float', with true as well (number columns are checked against every row).
// With { deep: false } only the first row counts: price becomes 'int' and '19.99' is stored as 19
Note

A boolean property, declared or inferred, converts the strings 'true', 'false', '1' and '0' (case-insensitive, no trimming) and the numbers 1 and 0 to booleans. Any other string, number, object or array is reported as WrongDataType and the value stays undefined, so 'yes', 'no', '' or 2 do not load as a boolean. Inline data, JSON and CSV follow the same rule; an empty CSV cell counts as no value, not as ''. Map other spellings with valueMappings, or declare the column as string:

dcupl.models.set({
  key: 'Row',
  properties: [{ key: 'ok', type: 'boolean' }],
  valueMappings: [
    {
      attributes: ['ok'],
      values: [
        { from: ['yes', 'Y'], to: true },
        { from: ['no', 'N'], to: false },
      ],
    },
  ],
});

supportsAutoCreation

When true, items are automatically created in this model when referenced by other models. This is useful for lookup tables or reference data that might be incomplete.

Auto-creation of referenced items
const productModel: ModelDefinition = {
  key: 'Product',
  references: [{ key: 'category', type: 'singleValued', model: 'Category' }],
  data: [
    { key: 'p1', category: 'electronics' },
    { key: 'p2', category: 'books' },
  ],
};

const categoryModel: ModelDefinition = {
  key: 'Category',
  supportsAutoCreation: true, // Categories created automatically
  properties: [{ key: 'name', type: 'string' }],
};

// After init, 'electronics' and 'books' exist in Category model
// even though no Category data was explicitly provided

valueMappings

Transform values during data loading. Useful for normalizing data, handling special values, or converting between formats.

Type Definition:

type ValueMappingConfig = {
  attributes: string[]; // Properties/references to apply mapping to
  values: {
    from: (string | '$dcupl_falsy')[]; // Values to match
    to: any; // Replacement value
  }[];
};

Mappings run on the raw value, before type conversion. CSV files loaded by DcuplAppLoader are the exception: the CSV parser converts each cell to the declared type first, and a boolean column accepts only true / false / 1 / 0 (in any case), so Yes / No are dropped before a mapping sees them (convert them in a loader transformer instead). A from value matches only with strict equality ('0' does not match the number 0). A from list that contains '$dcupl_falsy' matches only undefined, null and ''; other entries in that list are ignored, so put them in a separate values entry.

Mapping falsy values to a default
const model: ModelDefinition = {
  key: 'Product',
  valueMappings: [
    {
      attributes: ['category', 'brand'],
      values: [
        {
          from: ['$dcupl_falsy'], // Matches undefined, null, empty string
          to: 'uncategorized',
        },
      ],
    },
  ],
  references: [
    { key: 'category', type: 'singleValued', model: 'Category' },
    { key: 'brand', type: 'singleValued', model: 'Brand' },
  ],
};
Converting string values to boolean
const model: ModelDefinition = {
  key: 'Product',
  valueMappings: [
    {
      attributes: ['inStock'],
      values: [
        { from: ['Yes', 'yes', 'Y', '1'], to: true },
        { from: ['No', 'no', 'N', '0'], to: false },
      ],
    },
  ],
  properties: [{ key: 'inStock', type: 'boolean' }],
};

meta

Attach arbitrary metadata to the model for documentation or application-specific purposes.

Model with metadata
const model: ModelDefinition = {
  key: 'Product',
  meta: {
    name: 'Product Catalog',
    description: 'All products available for sale',
    version: '2.0',
    maintainer: 'catalog-team@example.com',
  },
  properties: [{ key: 'name', type: 'string' }],
};

// Access metadata
const productModel = dcupl.models.get('Product');
console.log(productModel?.meta?.description);

Property

Defines a property (field) in a model.

Type Definition:

type Property =
  | PropertyString
  | PropertyStringArray
  | PropertyInt
  | PropertyIntArray
  | PropertyFloat
  | PropertyFloatArray
  | PropertyDate
  | PropertyDateArray
  | PropertyJson
  | PropertyBoolean
  | PropertyAny;

Common Properties:

Property Type Description
key string Unique property identifier
type PropertyType Data type of the property
index boolean Build the property's lookup index at load time instead of on the first query that needs it (ignored on expression properties)
expression string Computed property expression
derive DeriveConfig Derive value from reference
quality AttributeQualityConfig Validation rules
filter boolean | object List the property in catalog.filters (filter UI items)
origin string Read the value from a differently named source field, e.g. origin: 'Product Title' for a CSV column header
meta ModelMetadata Metadata attached to the property
aggregate boolean | object Accepted and returned by models.get(); no effect on queries or fn.aggregate()

Type-specific options:

Option Types Description
separator Array<…> Splits a string value into array elements (default: ',')
fractionDigits float, Array<float> Rounds the value to this many decimals
inputFormat date, Array<date> Format of the raw value, e.g. 'yyyy-MM-dd' or 'dd.MM.yyyy'; 't' = Unix seconds, 'T' = Unix milliseconds
UTC date, Array<date> Treat the parsed date and time as UTC instead of local time

Property Types:

Type Description
'string' Text values
'Array<string>' Array of text values
'int' Integer numbers
'Array<int>' Array of integers
'float' Floating point numbers
'Array<float>' Array of floats
'date' Date/time values
'Array<date>' Array of dates
'boolean' Boolean values: the strings true/false/1/0 (any case) and the numbers 1/0 convert, anything else is a WrongDataType
'json' JSON objects
'any' Any type

Examples:

String property
{
  key: 'name',
  type: 'string'
}
Number with fractionDigits
{
  key: 'price',
  type: 'float',
  fractionDigits: 2
}
Array property
{
  key: 'tags',
  type: 'Array<string>',
  separator: ','
}
Date property
{
  key: 'createdAt',
  type: 'date',
  inputFormat: 'yyyy-MM-dd',
  UTC: true
}
Derived property
{
  key: 'categoryName',
  type: 'string',
  derive: {
    localReference: 'category',
    remoteProperty: 'name'
  }
}
Expression property
{
  key: 'displayName',
  type: 'string',
  expression: '${brand} ${name}'
}
Property with quality checks
{
  key: 'email',
  type: 'string',
  quality: {
    required: true,
    validators: {
      email: { value: true }
    }
  }
}

Reference

Defines a relationship to another model.

Type Definition:

// GroupedRefeference is spelled this way in @dcupl/common
type Reference = ReferenceBase | ResolvedReference | DerivedReference | GroupedRefeference | QueryReference;

Common Properties:

Property Type Description
key string Unique reference identifier
type 'singleValued' | 'multiValued' Single or multiple items
model string Target model key
separator string Separator for multi-valued references
validity 'loose' | 'strict' 'loose' (default) keeps a reference to a key missing in the target model; 'strict' drops it
filter boolean | object List the reference in catalog.filters (filter UI items)
origin string Read the key(s) from a differently named source field
quality AttributeQualityConfig Only required and nullable apply (AttributeQualityConfig): a null is kept and reported as NullValue unless nullable; a missing value is an UndefinedValue unless required: false
meta ModelMetadata Metadata attached to the reference (returned by models.get())
index boolean Accepted, no effect: a reference's lookup index is always built at load time
aggregate boolean | object Accepted and returned by models.get(); no effect on queries or fn.aggregate()

Reference Types:

1. Basic Reference

{
  key: 'category',
  type: 'singleValued',
  model: 'Category'
}

{
  key: 'tags',
  type: 'multiValued',
  model: 'Tag',
  separator: ','
}

2. Resolved Reference

The reverse of a reference: on Category, collect every Product whose category reference points to this category. resolve.model is the model that holds the reference, resolve.reference the reference on it.

{
  key: 'products',
  type: 'multiValued',
  model: 'Product',
  resolve: {
    reference: 'category',
    model: 'Product'
  }
}

3. Derived Reference

Follows a local reference and reads a reference of the target. Here: the products of this product's category (uses the products reference on Category from above; the product itself is included).

{
  key: 'similarProducts',
  type: 'multiValued',
  model: 'Product',
  derive: {
    localReference: 'category',
    remoteReference: 'products'
  }
}

4. Grouped Reference

Collects the other items of the same model that share a value of groupBy.reference. Here: the other products in this product's category. includeSelfInGroup: true adds the item itself (default false).

{
  key: 'categoryGroup',
  type: 'multiValued',
  model: 'Product',
  groupBy: {
    reference: 'category',
    includeSelfInGroup: false
  }
}

5. Query Reference

Runs a query for every item. Values in the query can contain ${attribute} placeholders that are filled from the item's raw data, for example value: '${category}'. The query always runs against the reference's model; a modelKey inside query is ignored. query also accepts a single condition or an array of conditions, and a singleValued query reference takes the first match.

Note

When the rows of the model that defines a query reference change, or its target model (or a model the target reaches through its references), dcupl.update() falls back to a full rebuild so the reference is recomputed; other changes stay on the partial path. See Partial Updates.

{
  key: 'activeProducts',
  type: 'multiValued',
  model: 'Product',
  query: {
    modelKey: 'Product',
    queries: [
      { operator: 'eq', attribute: 'status', value: 'active' }
    ]
  }
}

Property options

derive

Derives a property value from a referenced model.

Type: { localReference: string; remoteProperty: string; separator?: string }

Examples:

Derive single value
{
  key: 'categoryName',
  type: 'string',
  derive: {
    localReference: 'category',
    remoteProperty: 'name'
  }
}
Derive multiple values
{
  key: 'tagNames',
  type: 'string',
  derive: {
    localReference: 'tags',
    remoteProperty: 'name',
    separator: ', '
  }
}

Over a multi-valued reference, an Array<…> property gets the list of values (separator is ignored); a string, int, float or date property gets the values joined with separator (default ', '), e.g. 'sale, new'.

expression

Defines a computed property from a template: every ${attribute} placeholder is replaced with the item's value. Expressions are text templates, not formulas: there is no arithmetic, comparison or condition, and the result is always a string (the declared type is not applied). An unknown placeholder becomes an empty string and records an UnknownExpressionVariable quality error. Compute numbers before loading the data, for example in a loader transformer.

Type: string

Examples:

Combine values
{
  key: 'displayName',
  type: 'string',
  expression: '${brand} ${name}'
}
Text around a value
{
  key: 'priceLabel',
  type: 'string',
  expression: '${price} EUR'
}
Use a derived property
{
  key: 'label',
  type: 'string',
  expression: '${name} (${categoryName})' // categoryName derived from the category reference
}

A placeholder can also follow a single-valued reference ('${category.name}'). It resolves, but SDK 2.0 reports it as an UnknownExpressionVariable quality error; a derived property as above avoids that. More examples: Expression properties.

quality

Defines validation rules for a property.

Type: AttributeQualityConfig

Examples:

Required field
{
  key: 'name',
  type: 'string',
  quality: {
    required: true
  }
}
Email validation
{
  key: 'email',
  type: 'string',
  quality: {
    required: true,
    validators: {
      email: { value: true }
    }
  }
}
Number range
{
  key: 'age',
  type: 'int',
  quality: {
    required: true,
    validators: {
      min: { value: 0 },
      max: { value: 120 }
    }
  }
}
String pattern
{
  key: 'zipCode',
  type: 'string',
  quality: {
    validators: {
      pattern: { value: '^[0-9]{5}$' }
    }
  }
}
Unique constraint
{
  key: 'username',
  type: 'string',
  quality: {
    required: true,
    validators: {
      unique: { value: true }
    }
  }
}

ModelQualityConfig

Quality validation configuration for a model.

Type Definition:

type ModelQualityConfig = {
  enabled?: boolean; // default: true
  attributes?: Omit<AttributeQualityConfig, 'validators'>; // model-wide defaults for every attribute
};

attributes sets defaults for the quality flags of all properties and references; each attribute's own quality overrides them. Validators can only be set per attribute.

Example:

{
  key: 'Product',
  properties: [...],
  quality: {
    enabled: true,
    attributes: {
      required: true,
      nullable: false
    }
  }
}

AttributeQualityConfig

Quality validation configuration for properties and references (on a reference only required and nullable apply).

Type Definition:

type AttributeQualityConfig = {
  required?: boolean;
  nullable?: boolean;
  forceStrictDataType?: boolean;
  validators?: AttributeValidatorConfig;
  validatorHandling?: 'loose' | 'strict';
};

Properties:

Property Type Description
required boolean A missing value is reported as UndefinedValue (default: true)
nullable boolean null is allowed; with false it is reported as NullValue (default: false)
forceStrictDataType boolean Turns off type coercion: wrong-typed values are reported as WrongDataType and dropped (default: false)
validators AttributeValidatorConfig Validation rules
validatorHandling 'loose' | 'strict' 'loose' (default) reports a failed validator and keeps the value; 'strict' also drops the value

AttributeValidatorConfig

Validator configuration for properties.

Type Definition:

type AttributeValidatorConfig = {
  email?: ValidatorConfig;
  endsWith?: ValidatorConfig;
  enum?: ValidatorConfig;
  includes?: ValidatorConfig;
  max?: ValidatorConfig;
  maxLength?: ValidatorConfig;
  min?: ValidatorConfig;
  minLength?: ValidatorConfig;
  pattern?: ValidatorConfig;
  unique?: ValidatorConfig;
  startsWith?: ValidatorConfig;
};

Validators check string, int and float values (for Array<…> types, each element). Other property types and references are not validated, except by unique, which only detects repeated primitive values (not dates, arrays or objects). A validator runs whenever its key is present, so email: { value: false } still checks the format. A failed validator is reported as an InvalidValidator quality error.

Available Validators:

Validator Description Value Type
email Email format validation boolean
endsWith String ends with value string
enum Value must be in list any[]
includes String contains value string
max Maximum value number
maxLength Maximum string length number
min Minimum value number
minLength Minimum string length number
pattern Regex pattern match string
unique Unique across all items boolean
startsWith String starts with value string

Examples:

validators: {
  email: { value: true },
  minLength: { value: 5 },
  maxLength: { value: 100 },
  pattern: { value: '^[a-zA-Z0-9]+$' }
}

validators: {
  min: { value: 0 },
  max: { value: 1000 },
  unique: { value: true }
}

validators: {
  enum: { value: ['active', 'inactive', 'pending'] },
  startsWith: { value: 'PRD-' }
}

Complete examples

Product model

dcupl.models.set({
  key: 'Product',
  keyProperty: 'productId',
  properties: [
    {
      key: 'name',
      type: 'string',
      quality: {
        required: true,
        validators: {
          minLength: { value: 3 },
          maxLength: { value: 100 },
        },
      },
    },
    {
      key: 'price',
      type: 'float',
      fractionDigits: 2,
      quality: {
        required: true,
        validators: {
          min: { value: 0 },
        },
      },
    },
    {
      key: 'description',
      type: 'string',
    },
    {
      key: 'tags',
      type: 'Array<string>',
      separator: ',',
    },
    {
      key: 'createdAt',
      type: 'date',
      inputFormat: 'yyyy-MM-dd',
      UTC: true,
    },
  ],
  references: [
    {
      key: 'category',
      type: 'singleValued',
      model: 'Category',
    },
    {
      key: 'manufacturer',
      type: 'singleValued',
      model: 'Manufacturer',
    },
  ],
  quality: {
    enabled: true,
  },
});

Model with derived properties

dcupl.models.set({
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
    {
      key: 'categoryName',
      type: 'string',
      derive: {
        localReference: 'category',
        remoteProperty: 'name',
      },
    },
    {
      key: 'label',
      type: 'string',
      expression: '${name} (${categoryName})',
    },
  ],
  references: [{ key: 'category', type: 'singleValued', model: 'Category' }],
});

Model with quality checks

dcupl.models.set({
  key: 'User',
  properties: [
    {
      key: 'username',
      type: 'string',
      quality: {
        required: true,
        validators: {
          minLength: { value: 3 },
          maxLength: { value: 20 },
          pattern: { value: '^[a-zA-Z0-9_]+$' },
          unique: { value: true },
        },
      },
    },
    {
      key: 'email',
      type: 'string',
      quality: {
        required: true,
        validators: {
          email: { value: true },
          unique: { value: true },
        },
      },
    },
    {
      key: 'age',
      type: 'int',
      quality: {
        validators: {
          min: { value: 18 },
          max: { value: 120 },
        },
      },
    },
    {
      key: 'status',
      type: 'string',
      quality: {
        required: true,
        validators: {
          enum: { value: ['active', 'inactive', 'pending'] },
        },
      },
    },
  ],
  quality: {
    enabled: true,
    attributes: {
      required: true,
      nullable: false,
    },
  },
});

Complex references

// Product model with various reference types
dcupl.models.set({
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
  ],
  references: [
    // Basic reference
    {
      key: 'category',
      type: 'singleValued',
      model: 'Category',
    },
    // Multi-valued reference
    {
      key: 'tags',
      type: 'multiValued',
      model: 'Tag',
      separator: ',',
    },
    // Query reference: products in the same category (${category} is this item's category key)
    {
      key: 'similarProducts',
      type: 'multiValued',
      model: 'Product',
      query: {
        modelKey: 'Product',
        queries: [{ operator: 'eq', attribute: 'category', value: '${category}' }],
      },
    },
  ],
});

// Category model with a resolved reference back to its products
dcupl.models.set({
  key: 'Category',
  properties: [{ key: 'name', type: 'string' }],
  references: [
    {
      key: 'products',
      type: 'multiValued',
      model: 'Product',
      resolve: {
        reference: 'category',
        model: 'Product',
      },
    },
  ],
});

See also