Skip to content

}bedrock.hier.clone – N→C Element Conversion, Extended Attribute Handling & Subset/MDX Filter Support#446

Open
PPN-OL wants to merge 2 commits into
cubewise-code:masterfrom
PPN-OL:hier.clone
Open

}bedrock.hier.clone – N→C Element Conversion, Extended Attribute Handling & Subset/MDX Filter Support#446
PPN-OL wants to merge 2 commits into
cubewise-code:masterfrom
PPN-OL:hier.clone

Conversation

@PPN-OL
Copy link
Copy Markdown

@PPN-OL PPN-OL commented May 18, 2026

Summary

This update extends }bedrock.hier.clone with three related enhancements:

  1. N→C element conversion (pIfNthanC): Numeric (N) elements can be automatically promoted to Consolidated (C) elements during cloning, preventing unintended leaf nodes and making the target hierarchy immediately ready for child assignments.
  2. Selective attribute copy (pAttr = 2): Attribute values are copied only for attributes that already exist on both source and target, leaving a curated target attribute schema untouched.
  3. Subset / MDX filter (pSrcFilter): Only the elements resolved by a named subset or an MDX expression are cloned, enabling partial hierarchy copies.

Motivation

When an existing dimension is used as the basis for a new, more detailed dimension, its leaf (N) elements must first be converted to consolidation (C) elements before children can be added. Previously this required a manual post-processing step. With pIfNthanC = 1 this conversion is now handled automatically inside the clone process itself.

In addition, real-world cloning scenarios often require only a subset of elements to be copied — for example the children of a single consolidation, or elements matching a business filter. The new pSrcFilter parameter enables this without requiring a separate pre-processing step.


Changes

New Parameter: pIfNthanC (Numeric, default: 0)

Value Behaviour
0 Default – element types are copied 1:1 from source (existing behaviour)
1 All non-String (N) elements are inserted as C-elements in the target hierarchy
  • Implemented in the Prolog element insertion loop:
    if(sElType @<> 'S' & pIfNthanC = 1);
        HierarchyElementInsert(pTgtDim, pTgtHier, '', sElName, 'C');
    else;
        HierarchyElementInsert(pTgtDim, pTgtHier, '', sElName, sElType);
    endif;
    
  • The parameter is correctly forwarded in the recursive Epilog call (used when a new target dimension is created and an alternate hierarchy must also be cloned).

Extended pAttr behaviour – value 2: copy only existing attributes

Value Behaviour
0 No attributes copied (existing behaviour)
1 Missing attributes are created on the target dimension; all attribute values are copied (existing behaviour)
2 (new) No new attributes are created; only values for attributes that already exist on both source and target are copied
  • Prolog: When pAttr > 1, nNumAttrs is set from sAttrTragetDim (target attr dim) instead of sAttrDim (source attr dim), so the target's own attribute set drives the loop.
  • Data: The attribute copy condition is now pAttr >= 1 (instead of = 1), and an additional guard skips copying when either the source or target attribute is not present and pAttr > 1.
  • Parameter prompt updated: "OPTIONAL: Include Attributes? (Boolean 1=True; 2 = only existing)"

New Parameter: pSrcFilter (String, default: '')

Value Behaviour
'' (empty) No filter — all elements of the source hierarchy are cloned (existing behaviour)
Subset:<name> Only elements of the named subset on pSrcDim:pSrcHier are cloned
MDX:<expression> A temporary static subset is created from the MDX expression, used for cloning, then destroyed

Prolog – variable initialisation:

nTempSubsetCreated = 0;
sTempSubset        = '';
sFilterPrefix      = '';
sFilterValue       = '';

Prolog – filter resolution block (inserted after the first error-check, before hierarchy creation):

  • Detects Subset: / MDX: prefix; logs an error for any unrecognised prefix.
  • Subset: mode: validates subset existence via SubsetExists; aborts if not found.
  • MDX: mode: calls }bedrock.hier.sub.create.bymdx with pConvertToStatic=1 and pTemp=1; stores the unique temp name from the existing cTempSub constant.
  • Falls back to sTempSubset = 'ALL' when pSrcFilter is empty.
  • A second error-check block guards against errors raised during filter resolution.

Prolog – element insertion loop — iteration source swapped from DimSiz/ElementName to SubsetGetSize/SubsetGetElementName; the existing pIfNthanC branch is preserved unchanged:

nSourceHierSize = SubsetGetSize( pSrcDim, sTempSubset );
nIndex = 1;
WHILE( nIndex <= nSourceHierSize );
  sElName = SubsetGetElementName( pSrcDim, sTempSubset, nIndex );
  sElType = ElementType( pSrcDim, pSrcHier, sElName );
  if( sElType @<> 'S' & pIfNthanC = 1 );
    HierarchyElementInsert( pTgtDim, pTgtHier, '', sElName, 'C' );
  else;
    HierarchyElementInsert( pTgtDim, pTgtHier, '', sElName, sElType );
  endif;
  nIndex = nIndex + 1;
END;

Prolog – datasource assignment updated from 'ALL' to sTempSubset, so the Metadata and Data tabs iterate only the filtered elements.

Metadata tabSubsetGetIndex guard added before HierarchyElementComponentAdd to prevent linking children that are outside the working subset (strict partial hierarchy):

If( SubsetGetIndex( pSrcDim, sTempSubset, sChildElement ) > 0 );
    HierarchyElementComponentAdd( pTgtDim, pTgtHier, vEle, sChildElement, sChildWeight );
EndIf;

SubsetGetIndex is preferred over SubsetIsElementInSubset for broader TM1 server compatibility.

Data tab — no changes; iterates vEle from the datasource subset automatically.

Epilog – cleanup (placed before the recursive self-call):

If( nTempSubsetCreated = 1 );
    SubsetDestroy( pSrcDim, sTempSubset );
    nTempSubsetCreated = 0;
EndIf;

Epilog – recursive self-call extended with 'pSrcFilter', pSrcFilter.

cLogInfo updated to include pIfNthanC and pSrcFilter in the log output line.


Files Changed

Section Change
Prolog Variable init block: 4 new filter variables added (nTempSubsetCreated, sTempSubset, sFilterPrefix, sFilterValue)
Prolog cLogInfo constant extended to log pIfNthanC and pSrcFilter
Prolog Filter resolution block inserted after first error-check
Prolog Second error-check block added after filter resolution
Prolog Element insertion loop: DimSiz/ElementName replaced by SubsetGetSize/SubsetGetElementName; pIfNthanC branch preserved
Prolog Datasource assignment: 'ALL' replaced by sTempSubset
Prolog Attribute init block: ElseIf(pAttr > 1 ...) branch added to set nNumAttrs from target attr dim
Metadata SubsetGetIndex guard added before HierarchyElementComponentAdd
Data Attribute copy condition changed from pAttr = 1 to pAttr >= 1; skip-guard added for pAttr > 1
Epilog Temp-subset cleanup block added before recursive call
Epilog pIfNthanC and pSrcFilter added to recursive ExecuteProcess call
Parameters New parameters pIfNthanC and pSrcFilter added; pAttr prompt updated

Testing

Scenario Expected Result
pIfNthanC = 0 (default) Behaviour identical to previous version
pIfNthanC = 1, source has N-elements All N-elements appear as C-elements in target; no leaves created
pIfNthanC = 1, source has S-elements S-elements remain S-elements in target
pAttr = 1 All source attributes created and copied to target (existing behaviour unchanged)
pAttr = 2 Only attributes existing on both source and target are copied; no new attributes created on target
pAttr = 2, target has no attributes Nothing is copied; no error
New target dimension + alternate hierarchy Recursive epilog call passes pIfNthanC and pSrcFilter correctly
pSrcFilter = '' (default) Behaviour identical to previous version; sTempSubset = 'ALL'
pSrcFilter = 'Subset:MySub', subset exists Only elements in MySub are cloned; child links outside subset are omitted
pSrcFilter = 'Subset:Missing', subset not found Process aborts with error; target hierarchy is not modified
pSrcFilter = 'MDX:{...}', valid MDX Temp subset created, used, then destroyed in Epilog; no artefacts left
pSrcFilter = 'MDX:{...}', invalid MDX }bedrock.hier.sub.create.bymdx returns error; process aborts
pSrcFilter = 'INVALID' (no prefix) Process aborts with error message referencing valid prefixes
pSrcFilter + pIfNthanC = 1 Filtered elements inserted as C-elements; no interaction issues

Notes

  • The pIfNthanC condition intentionally excludes String (S) elements, as S-elements cannot be consolidated and must remain S.
  • pAttr = 2 is intended for scenarios where the target dimension has a curated, pre-defined attribute schema that must not be altered by the clone operation.
  • pSrcFilter with MDX: prefix requires }bedrock.hier.sub.create.bymdx to be available on the server. The temporary subset is always created as a public, static subset with a unique name derived from cTempSub (process name + timestamp + random int) to prevent name collisions under concurrent execution.
  • When pSrcFilter is active and the subset resolves to zero elements, the target hierarchy is created or cleared but left empty — no error is raised.
  • pSrcFilter is forwarded unchanged in the recursive Epilog call (new-dimension scenario). Each recursive invocation resolves the filter independently and manages its own temporary subset lifecycle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant