Overview
Complex Attributes in Struct PIM allow you to create structured, nested data within your product information. Unlike simple attributes that store single values, Complex Attributes contain multiple Sub-Attributes that work together to form a cohesive data structure.
Key Benefits
- Organized Data: Group related attributes logically (e.g., technical specifications, dimensions)
- Reusable Structures: Define once, use across multiple product structures
- Rich Data Modeling: Support complex product information requirements
- UI Flexibility: Control how nested data is displayed and rendered
Understanding Complex Attributes
What Are Complex Attributes?
A Complex Attribute is a container that holds multiple Sub-Attributes. Think of it as a structured object in programming:
Complex Attribute Structure
Example showing the difference between simple and complex attributes
// Simple attributes store single values
ProductName: "iPhone 15 Pro"
Price: 999.99
// Complex attributes store structured objects
TechnicalSpecs: {
ModelNumber: "A2848",
PowerRating: 120,
Voltage: "220V",
IsEnergyEfficient: true
}
// Architecture
ComplexAttribute
├── SubAttribute 1 (TextAttribute)
├── SubAttribute 2 (NumberAttribute)
├── SubAttribute 3 (BooleanAttribute)
└── SubAttribute N (Any supported type)
API Schema vs Reality
The Documentation Gap
The official Swagger documentation shows a simplified schema, but our empirical testing revealed the actual working structure:
Documentation vs Reality
What the Swagger docs suggest vs what actually works
// ❌ What the Swagger Docs Suggest:
{
"alias": "TechSpecs",
"name": "Technical Specifications",
"type": "ComplexAttribute",
"configuration": {
"subAttributes": [...]
}
}
// ✅ What Actually Works:
{
"Uid": "uuid-here",
"Alias": "TechSpecs",
"BackofficeName": "Technical Specifications",
"AttributeType": "ComplexAttribute",
"Localized": false,
"ReadOnly": false,
"Mandatory": false,
"Columns": 12,
"Unchangeable": false,
"DisableRevisionLogging": false,
"DisableIndexing": false,
"SubAttributes": [
// Full attribute definitions here, not references
],
"RenderValuesForAttributeFieldUids": [],
"RenderValuesForBackofficeAttributeFieldUids": [],
"RenderedValueSeparator": null,
"RenderedValueInBackofficeSeparator": null
}
Creating Complex Attributes
Basic Structure
Every Complex Attribute must include these essential properties:
TypeScript Interface
Complete interface for Complex Attributes
interface ComplexAttributeBase {
Uid: string; // Required: Generate UUID
Alias: string; // Required: Unique identifier
BackofficeName: string; // Required: Display name
AttributeType: 'ComplexAttribute';
Localized: boolean; // Usually false
ReadOnly: boolean; // Usually false
Mandatory: boolean; // Usually false
Columns: number; // 1-12, commonly 12 for full width
Unchangeable: boolean; // Usually false
DisableRevisionLogging: boolean; // Usually false
DisableIndexing: boolean; // Usually false
SubAttributes: SubAttribute[]; // The nested attributes
}
Step-by-Step Creation
Follow these steps to create a Complex Attribute:
Creation Process
- Generate Unique IDs for the complex attribute and each sub-attribute
- Define Sub-Attributes with complete attribute definitions
- Compose the Complex Attribute with all required properties
- Create via API endpoint with proper error handling
Practical Examples
Example 1: Technical Specifications
Perfect for products with multiple technical details:
Technical Specifications Example
Complete example of a Complex Attribute for technical specifications
const technicalSpecsAttribute = {
Uid: generateGuid(),
Alias: 'TechnicalSpecs',
BackofficeName: 'Technical Specifications',
AttributeType: 'ComplexAttribute',
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 12,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
SubAttributes: [
{
Uid: generateGuid(),
Alias: 'ModelNumber',
BackofficeName: 'Model Number',
AttributeType: 'TextAttribute',
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 6,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
MaxLength: 100,
UseMultiRowInput: false,
UseRichText: false,
ShowCharacterCount: false
},
{
Uid: generateGuid(),
Alias: 'PowerRating',
BackofficeName: 'Power Rating (W)',
AttributeType: 'NumberAttribute',
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 6,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
NumberOfDecimals: 0,
Min: 0,
Max: 2000,
Unit: 'W'
},
{
Uid: generateGuid(),
Alias: 'IsEnergyEfficient',
BackofficeName: 'Energy Efficient',
AttributeType: 'BooleanAttribute',
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 6,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false
}
],
RenderValuesForAttributeFieldUids: [],
RenderValuesForBackofficeAttributeFieldUids: [],
RenderedValueSeparator: null,
RenderedValueInBackofficeSeparator: null
};
⚡ Complex Attributes in Global Lists (Important Discovery)
Breaking: Complex Attributes DO Work in Global Lists!
Contrary to what might be assumed from the documentation, Struct PIM DOES support Complex Attributes for Global Lists. This is a powerful, undocumented feature that enables rich, structured data models for global list values.
Instead of simple text values in your global lists, you can now have rich objects with multiple properties like brand name, code, description, logo URL, etc. This enables sophisticated use cases like:
Business Value
- Master Feature Dictionary (MFD): Complex feature definitions with metadata, ownership, documentation links
- Rich Reference Data: Global lists with multiple attributes per entry (code, name, description, sort order)
- Structured Lookups: Multi-field dropdown options that carry additional context
- Brand Management: Complete brand information including logos, codes, and descriptions in one global list
Critical Implementation Details
- Use ComplexAttribute (NOT CompositeAttribute) as the AttributeType
- Use SubAttributes array (NOT CompositeFields) to define nested fields
- Each SubAttribute must be a complete attribute definition with all required properties
- Attribute types must match exactly: NumberAttribute not NumericAttribute, TextAttribute not Text
Global List with Complex Attributes Example
Working example of a global list with complex structure for rich brand data
// ✅ WORKING: Global List with Complex Attributes
const brandGlobalListAttribute = {
Uid: generateGuid(),
Alias: 'BrandList',
BackofficeName: 'Brand Information',
AttributeType: 'ComplexAttribute', // MUST be ComplexAttribute
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 12,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
SubAttributes: [ // MUST be SubAttributes, not CompositeFields
{
Uid: generateGuid(),
Alias: 'BrandName',
BackofficeName: 'Brand Name',
AttributeType: 'TextAttribute', // MUST be TextAttribute
Localized: false,
ReadOnly: false,
Mandatory: true,
Columns: 6,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
MaxLength: 255,
UseMultiRowInput: false,
UseRichText: false,
ShowCharacterCount: false
},
{
Uid: generateGuid(),
Alias: 'BrandCode',
BackofficeName: 'Brand Code',
AttributeType: 'TextAttribute',
Localized: false,
ReadOnly: false,
Mandatory: true,
Columns: 3,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
MaxLength: 50
},
{
Uid: generateGuid(),
Alias: 'BrandLogoUrl',
BackofficeName: 'Logo URL',
AttributeType: 'TextAttribute',
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 12,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
MaxLength: 500
},
{
Uid: generateGuid(),
Alias: 'IsActive',
BackofficeName: 'Active',
AttributeType: 'BooleanAttribute',
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 3,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false
}
],
RenderValuesForAttributeFieldUids: [],
RenderValuesForBackofficeAttributeFieldUids: [],
RenderedValueSeparator: null,
RenderedValueInBackofficeSeparator: null
};
// Common errors and solutions:
// ❌ "No implementation was found for: CompositeAttribute" → Use ComplexAttribute
// ❌ "No implementation was found for: NumericAttribute" → Use NumberAttribute
// ❌ "No implementation was found for: Text" → Use TextAttribute
// ⚠️ Elasticsearch indexing errors may occur but don't indicate configuration problems
Example 2: Physical Dimensions
Ideal for products requiring size specifications with custom separators for display:
Physical Dimensions Example
Complex Attribute for product dimensions with custom rendering
const dimensionsAttribute = {
Uid: generateGuid(),
Alias: 'PhysicalDimensions',
BackofficeName: 'Physical Dimensions',
AttributeType: 'ComplexAttribute',
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 12,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
SubAttributes: [
{
Uid: generateGuid(),
Alias: 'Length',
BackofficeName: 'Length (cm)',
AttributeType: 'NumberAttribute',
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 4,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
NumberOfDecimals: 2,
Min: 0,
Max: 1000,
Unit: 'cm'
},
{
Uid: generateGuid(),
Alias: 'Width',
BackofficeName: 'Width (cm)',
AttributeType: 'NumberAttribute',
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 4,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
NumberOfDecimals: 2,
Min: 0,
Max: 1000,
Unit: 'cm'
},
{
Uid: generateGuid(),
Alias: 'Height',
BackofficeName: 'Height (cm)',
AttributeType: 'NumberAttribute',
Localized: false,
ReadOnly: false,
Mandatory: false,
Columns: 4,
Unchangeable: false,
DisableRevisionLogging: false,
DisableIndexing: false,
NumberOfDecimals: 2,
Min: 0,
Max: 1000,
Unit: 'cm'
}
],
// Custom separators for display: "100 × 50 × 25"
RenderValuesForAttributeFieldUids: [],
RenderValuesForBackofficeAttributeFieldUids: [],
RenderedValueSeparator: ' × ',
RenderedValueInBackofficeSeparator: ' × '
};
Best Practices
1. Naming Conventions
Follow consistent naming patterns:
Naming Guidelines
- Aliases: Use PascalCase without spaces (TechnicalSpecs, PhysicalDimensions)
- BackofficeNames: Use readable names with spaces (Technical Specifications, Physical Dimensions)
- SubAttribute Aliases: Use descriptive names (ModelNumber, PowerRating, IsEnergyEfficient)
UI Layout (Columns)
- Complex Attributes: Use full width (Columns: 12)
- SubAttributes: Half width for paired fields (Columns: 6)
- Dimensions: Third width for triplets (Columns: 4)
- Compact fields: Quarter width (Columns: 3)
Troubleshooting
Common Issues and Solutions
Frequent Problems
- "The attribute field is required" Error: Send attribute object directly, not wrapped in container
- SubAttribute Creation Failures: Ensure each SubAttribute has all required base properties
- Type Validation Errors: Use type casting for API calls (as any)
- Complex Attribute Not Displaying: Check Columns layout and rendering configuration
Debugging Tips
- Log Full Payloads: Always log the complete request before sending
- Check Response Data: API errors often contain detailed validation messages
- Validate UUIDs: Ensure all Uid fields contain valid UUIDs
- Test SubAttributes Individually: Create simple attributes first, then complex ones
- Export and Compare: Use export functionality to see how existing complex attributes are structured
Error Handling Example
Proper error handling when creating Complex Attributes
// Proper error handling
try {
await client.createAttribute(complexAttribute as any);
console.log(`✅ Complex attribute created: ${complexAttribute.Alias}`);
} catch (error) {
console.error(`❌ Failed to create ${complexAttribute.Alias}:`, error.response?.data);
// Log detailed error information for debugging
}
// API Response Patterns
// Success Response: "" (empty string)
// Error Response:
{
"error": "Validation failed",
"details": {
"SubAttributes[0].Alias": "The field is required",
"SubAttributes[1].NumberOfDecimals": "Must be between 0 and 10"
}
}
Conclusion
Complex Attributes are a powerful feature of Struct PIM that enable sophisticated product data modeling. By understanding the actual API requirements (not just the Swagger documentation) and following the patterns outlined in this guide, you can successfully implement complex nested data structures for your products.
The key insights from our empirical testing:
Key Takeaways
- Use the complete attribute schema - don't rely solely on Swagger docs
- SubAttributes must be complete attribute definitions - not just references
- All base properties are required - even if they seem optional
- TypeScript casting may be necessary - the API is more flexible than types suggest
- Test thoroughly and iterate - complex attributes have many moving parts