From 1808dfaa937b9b101230266f957101b383fe4ed9 Mon Sep 17 00:00:00 2001 From: Wim Gielis Date: Sat, 10 Jan 2026 23:44:28 +0100 Subject: [PATCH 1/3] Bedrock element filter string from a public subset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new TI for Bedrock. Generate a Bedrock element filter string like Company ¦ comp A + comp B + comp Z From a subset 'My companies' in the dimension 'Company'. It avoids several loops in the calling TI. Now these loops are separated in a Bedrock TI process. The process tackles a limitation where element filter strings can only contain hardcoded element names, not public subset names. --- main/}bedrock.dim.filter.fromsubset.pro | 355 ++++++++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 main/}bedrock.dim.filter.fromsubset.pro diff --git a/main/}bedrock.dim.filter.fromsubset.pro b/main/}bedrock.dim.filter.fromsubset.pro new file mode 100644 index 0000000..4d28034 --- /dev/null +++ b/main/}bedrock.dim.filter.fromsubset.pro @@ -0,0 +1,355 @@ +601,100 +602,"}bedrock.dim.filter.fromsubset" +562,"NULL" +586, +585, +564, +565,"TM1rules" +559,1 +928,0 +593, +594, +595, +597, +598, +596, +800, +801, +566,0 +567,"," +588,"," +589,"." +568,"""" +570, +571, +569,0 +592,0 +599,1000 +560,11 +pLogOutput +pStrictErrorHandling +pSelection_1 +pSelection_2 +pSelection_3 +pSelection_4 +pSelection_5 +pSelectionDelim +pDimDelim +pEleStartDelim +pEleDelim +561,11 +1 +1 +2 +2 +2 +2 +2 +2 +2 +2 +2 +590,11 +pLogOutput,0 +pStrictErrorHandling,0 +pSelection_1,"Account&level 0" +pSelection_2,"" +pSelection_3,"" +pSelection_4,"" +pSelection_5,"" +pSelectionDelim,"&" +pDimDelim,"&" +pEleStartDelim,"¦" +pEleDelim,"+" +637,11 +pLogOutput,"OPTIONAL: Write parameters and action summary to server message log (Boolean True = 1)" +pStrictErrorHandling,"OPTIONAL: On encountering any error, exit with major error status by ProcessQuit after writing to the server message log (Boolean True = 1)" +pSelection_1,"OPTIONAL: a delimited string containing dimension name pDelim subset name" +pSelection_2,"OPTIONAL: a delimited string containing dimension name pDelim subset name" +pSelection_3,"OPTIONAL: a delimited string containing dimension name pDelim subset name" +pSelection_4,"OPTIONAL: a delimited string containing dimension name pDelim subset name" +pSelection_5,"OPTIONAL: a delimited string containing dimension name pDelim subset name" +pSelectionDelim,"OPTIONAL: delimiter character for element list. (default value if blank = '&')" +pDimDelim,"OPTIONAL: Delimiter between dimensions (default value if blank = '&')" +pEleStartDelim,"OPTIONAL: Delimiter for start of element list (default value if blank = '¦')" +pEleDelim,"OPTIONAL: Delimiter between elements (default value if blank = '+')" +577,0 +578,0 +579,0 +580,0 +581,0 +582,0 +603,0 +572,192 +#Region CallThisProcess +# A snippet of code provided as an example how to call this process should the developer be working on a system without access to an editor with auto-complete. +If( 1 = 0 ); + StringGlobalVariable('sFilter_String'); + vFilter = 'Year¦2025 + 2026 & '; + # or: + vFilter = ''; + # or even without a vFilter and just with sFilter_String + ExecuteProcess( '}bedrock.dim.filter.fromsubset', + 'pLogOutput', pLogOutput, 'pStrictErrorHandling', pStrictErrorHandling, + 'pSelectionDelim', '&', 'pDimDelim', '&', 'pEleStartDelim', '¦', 'pEleDelim', '+', + 'pSelection_1', 'Year & My years subset', 'pSelection_2', 'Company & Level 0 company', 'pSelection_3', '', 'pSelection_4', '', 'pSelection_5', '' + ); + vFilter = vFilter | sFilter_String; +EndIf; +#EndRegion CallThisProcess + +#****Begin: Generated Statements*** +#****End: Generated Statements**** + +################################################################################################# +##~~Join the bedrock TM1 community on GitHub https://github.com/cubewise-code/bedrock Ver 4.0 ~~## +################################################################################################# + +#Region @DOC +# Description: +# This process will create a filter string for Bedrock processes. +# The elements in the public subset will be concatenated and form a correct filter string for a dimension. +# PA alternate hierarchies are not allowed. Only the main hierarchy in a dimension or the leaves hierarchy. +# Make sure that string count limits are not exceeded. +# The subset should be a public subset. It can be static or dynamic. It can be permanent or temporary. + +# The global string variable 'sFilter_String' is populated. The process can also append to it. +# Up to 5 filters can be created with 1 call of this process. + +# Use case: Intended for Development but could be used in production too. +# To circumvent the limitation with Bedrock filter strings that they can only contain hardcoded lists of elements. + +#EndRegion @DOC + +### Global Variables +StringGlobalVariable('sFilter_String'); +StringGlobalVariable('sProcessReturnCode'); +NumericGlobalVariable('nProcessReturnCode'); +nProcessReturnCode= 0; + +### Constants ### +cThisProcName = GetProcessName(); +cUserName = TM1User(); +cTimeStamp = TimSt( Now, '\Y\m\d\h\i\s' ); +cRandomInt = NumberToString( INT( RAND( ) * 1000 )); +cTempSub = cThisProcName |'_'| cTimeStamp |'_'| cRandomInt; +cMsgErrorLevel = 'ERROR'; +cMsgErrorContent = 'User:%cUserName% Process:%cThisProcName% ErrorMsg:%sMessage%'; +cMsgInfoContent = 'User:%cUserName% Process:%cThisProcName% Message:%sMessage%'; +cLogInfo = 'Process:%cThisProcName% run with parameters pSelectionDelim:%pSelectionDelim%, pDimDelim:%pDimDelim%, pEleStartDelim:%pEleStartDelim%, pEleDelim:%pEleDelim%, +pSelection_1:%pSelection_1%, pSelection_2:%pSelection_2%, pSelection_3:%pSelection_3%, pSelection_4:%pSelection_4%, pSelection_5:%pSelection_5%.'; + +## LogOutput parameters +IF( pLogoutput = 1 ); + LogOutput('INFO', Expand( cLogInfo ) ); +ENDIF; + +### Validate Parameters ### +nErrors = 0; + + +## Default delimiters +If( pSelectionDelim @= '' ); + pSelectionDelim = '&'; +EndIf; +If( pDimDelim @= '' ); + pDimDelim = '&'; +EndIf; +If( pEleStartDelim@= '' ); + pEleStartDelim= '¦'; +EndIf; +If( pEleDelim @= '' ); + pEleDelim = '+'; +EndIf; + + +# Initialization +sFilter = ''; +sTreated_Dimensions = '#'; + +# Loop through the selections +nSelectionIndex = 1; +While( nSelectionIndex <= 5 ); + sDimension = ''; sSubset = ''; + sSelection = Expand( '%pSelection_' | NumberToString( nSelectionIndex ) | '%' ); + nDimDelimiterIndex = 1; + If( sSelection @<> '' ); + + # Get the parts in the string + While( nDimDelimiterIndex <> 0 ); + + nDimDelimiterIndex = Scan( pSelectionDelim, sSelection ); + If( nDimDelimiterIndex = 1 ); + nErrors = 1; + sPart = sSelection; + If( pLogOutput = 1 ); + sMessage = Expand( 'Empty dimension name or subset name extracted from a selection parameter: %sSelection% (iteration ' | NumberToString( nSelectionIndex ) | ')' ); + LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + EndIf; + ElseIf( nDimDelimiterIndex = 0 ); + sPart = sSelection; + Else; + sPart = Trim( Subst( sSelection, 1, nDimDelimiterIndex - 1 )); + sSelection = Trim( Delet( sSelection, 1, nDimDelimiterIndex )); + EndIf; + + # No wildcards allowed, no colons either + If( Scan( '*', sPart ) > 0 % Scan( '?', sPart ) > 0 & Scan( ':', sPart ) > 0 ); + nErrors = 1; + If( pLogOutput = 1 ); + sMessage = Expand( 'Invalid characters observed in a selection parameter: %sPart% (iteration ' | NumberToString( nSelectionIndex ) | ')' ); + LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + EndIf; + EndIf; + + ### Check for errors before continuing + If( nErrors <> 0 ); + If( pStrictErrorHandling = 1 ); + ProcessQuit; + Else; + ProcessBreak; + EndIf; + EndIf; + + # No errors observed + If( sDimension @= '' ); + sDimension = sPart; + Else; + sSubset = sPart; + nDimDelimiterIndex = 0; + EndIf; + + End; + + If( DimensionExists( sDimension ) = 0 ); + nErrors = 1; + If( pLogOutput = 1 ); + sMessage = Expand( 'Invalid dimension name: %sDimension% (iteration ' | NumberToString( nSelectionIndex ) | ')' ); + LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + EndIf; + EndIf; + If( SubsetExists( sDimension, sSubset ) = 0 ); + nErrors = 1; + If( pLogOutput = 1 ); + sMessage = Expand( 'Invalid subset name: %sSubset% in dimension %sDimension% (iteration ' | NumberToString( nSelectionIndex ) | ')' ); + LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + EndIf; + EndIf; + + If( Scan( '#' | NumberToString( Dimix( '}Dimensions', sDimension )) | '#', sTreated_Dimensions ) > 0 ); + nErrors = 1; + If( pLogOutput = 1 ); + sMessage = Expand( 'Dimension used twice: %sDimension% (iteration ' | NumberToString( nSelectionIndex ) | ')' ); + LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + EndIf; + EndIf; + + ### Check for errors before continuing + If( nErrors <> 0 ); + If( pStrictErrorHandling = 1 ); + ProcessQuit; + Else; + ProcessBreak; + EndIf; + EndIf; + + # All good now, let's continue with the heart of the process + sFilter = sFilter | If( sFilter @= '', '', ' ' | pDimDelim | ' ' ) | sDimension | pEleStartDelim; + m = 1; + While( m <= SubsetGetSize( sDimension, sSubset )); + vElement = SubsetGetElementName( sDimension, sSubset, m ); + sFilter = sFilter | If( m = 1, '', ' ' | pEleDelim | ' ' ) | vElement; + m = m + 1; + End; + + # Note that this dimension is done and should not be used more than once + sTreated_Dimensions = sTreated_Dimensions | NumberToString( Dimix( '}Dimensions', sDimension )) | '#'; + + EndIf; + nSelectionIndex = nSelectionIndex + 1; +End; + + +sFilter_String = sFilter_String | If( sFilter_String @= '', '', ' ' | pDimDelim | ' ' ) | sFilter; + +### End Prolog ### +573,4 + +#****Begin: Generated Statements*** +#****End: Generated Statements**** + +574,4 + +#****Begin: Generated Statements*** +#****End: Generated Statements**** + +575,29 + +#****Begin: Generated Statements*** +#****End: Generated Statements**** + +################################################################################################# +##~~Join the bedrock TM1 community on GitHub https://github.com/cubewise-code/bedrock Ver 4.0~~## +################################################################################################# + +### Return code & final error message handling +If( nErrors > 0 ); + sMessage = 'the process incurred at least 1 error. Please see above lines in this file for more details.'; + nProcessReturnCode = 0; + LogOutput( cMsgErrorLevel, Expand( cMsgErrorContent ) ); + sProcessReturnCode = Expand( '%sProcessReturnCode% Process:%cThisProcName% completed with errors. Check tm1server.log for details.' ); + If( pStrictErrorHandling = 1 ); + ProcessQuit; + EndIf; +Else; + sProcessAction = Expand( 'Process:%cThisProcName% successfully returned/updated the filter string.' ); + sProcessReturnCode = Expand( '%sProcessReturnCode% %sProcessAction%' ); + nProcessReturnCode = 1; + If( pLogoutput = 1 ); + LogOutput('INFO', Expand( sProcessAction ) ); + EndIf; +EndIf; + + + +### End Epilog ### +576, +930,0 +638,1 +804,0 +1217,1 +900, +901, +902, +938,0 +937, +936, +935, +934, +932,0 +933,0 +903, +906, +929, +907, +908, +904,0 +905,0 +909,0 +911, +912, +913, +914, +915, +916, +917,0 +918,1 +919,0 +920,50000 +921,"" +922,"" +923,0 +924,"" +925,"" +926,"" +927,"" From 601608bb9f0a2f17caa28763ccd90d9b9224722d Mon Sep 17 00:00:00 2001 From: Wim Gielis Date: Sun, 11 Jan 2026 01:32:29 +0100 Subject: [PATCH 2/3] Enhance filter process with MDX and subset support Updated the filter process to support MDX and subset parameters, enhancing flexibility in dimension filtering. --- main/}bedrock.dim.filter.fromsubset.pro | 148 ++++++++++++++---------- 1 file changed, 87 insertions(+), 61 deletions(-) diff --git a/main/}bedrock.dim.filter.fromsubset.pro b/main/}bedrock.dim.filter.fromsubset.pro index 4d28034..ae5be2b 100644 --- a/main/}bedrock.dim.filter.fromsubset.pro +++ b/main/}bedrock.dim.filter.fromsubset.pro @@ -1,4 +1,4 @@ -601,100 +601,100 602,"}bedrock.dim.filter.fromsubset" 562,"NULL" 586, @@ -52,23 +52,23 @@ pEleDelim 590,11 pLogOutput,0 pStrictErrorHandling,0 -pSelection_1,"Account&level 0" -pSelection_2,"" -pSelection_3,"" +pSelection_1,"Account \ subset:assets accounts" +pSelection_2,"Company \ mdx:Filter( TM1FilterByLevel( TM1SubsetAll( [Company] ), 0 ), [Company].CurrentMember.Properties(""Region"") = ""' | pRegion | '"" )" +pSelection_3,"Account\mdx: Head ( [Account].[Total Revenue].Children, 3 )" pSelection_4,"" pSelection_5,"" -pSelectionDelim,"&" +pSelectionDelim,"\" pDimDelim,"&" pEleStartDelim,"¦" pEleDelim,"+" 637,11 pLogOutput,"OPTIONAL: Write parameters and action summary to server message log (Boolean True = 1)" pStrictErrorHandling,"OPTIONAL: On encountering any error, exit with major error status by ProcessQuit after writing to the server message log (Boolean True = 1)" -pSelection_1,"OPTIONAL: a delimited string containing dimension name pDelim subset name" -pSelection_2,"OPTIONAL: a delimited string containing dimension name pDelim subset name" -pSelection_3,"OPTIONAL: a delimited string containing dimension name pDelim subset name" -pSelection_4,"OPTIONAL: a delimited string containing dimension name pDelim subset name" -pSelection_5,"OPTIONAL: a delimited string containing dimension name pDelim subset name" +pSelection_1,"OPTIONAL: a delimited string containing dimension name and subset name or MDX" +pSelection_2,"OPTIONAL: a delimited string containing dimension name and subset name or MDX" +pSelection_3,"OPTIONAL: a delimited string containing dimension name and subset name or MDX" +pSelection_4,"OPTIONAL: a delimited string containing dimension name and subset name or MDX" +pSelection_5,"OPTIONAL: a delimited string containing dimension name and subset name or MDX" pSelectionDelim,"OPTIONAL: delimiter character for element list. (default value if blank = '&')" pDimDelim,"OPTIONAL: Delimiter between dimensions (default value if blank = '&')" pEleStartDelim,"OPTIONAL: Delimiter for start of element list (default value if blank = '¦')" @@ -80,7 +80,7 @@ pEleDelim,"OPTIONAL: Delimiter between elements (default value if blank = '+')" 581,0 582,0 603,0 -572,192 +572,218 #Region CallThisProcess # A snippet of code provided as an example how to call this process should the developer be working on a system without access to an editor with auto-complete. If( 1 = 0 ); @@ -91,8 +91,8 @@ If( 1 = 0 ); # or even without a vFilter and just with sFilter_String ExecuteProcess( '}bedrock.dim.filter.fromsubset', 'pLogOutput', pLogOutput, 'pStrictErrorHandling', pStrictErrorHandling, - 'pSelectionDelim', '&', 'pDimDelim', '&', 'pEleStartDelim', '¦', 'pEleDelim', '+', - 'pSelection_1', 'Year & My years subset', 'pSelection_2', 'Company & Level 0 company', 'pSelection_3', '', 'pSelection_4', '', 'pSelection_5', '' + 'pSelectionDelim', '\', 'pDimDelim', '&', 'pEleStartDelim', '¦', 'pEleDelim', '+', + 'pSelection_1', 'Year \ subset: My years subset', 'pSelection_2', 'Company \ subset: Level 0 company', 'pSelection_3', '', 'pSelection_4', '', 'pSelection_5', '' ); vFilter = vFilter | sFilter_String; EndIf; @@ -107,14 +107,13 @@ EndIf; #Region @DOC # Description: -# This process will create a filter string for Bedrock processes. -# The elements in the public subset will be concatenated and form a correct filter string for a dimension. -# PA alternate hierarchies are not allowed. Only the main hierarchy in a dimension or the leaves hierarchy. -# Make sure that string count limits are not exceeded. -# The subset should be a public subset. It can be static or dynamic. It can be permanent or temporary. - +# This process will create a filter string for Bedrock processes. The elements are either contained in a public subset, or can be retrieved from an MDX. +# The elements that are found will be concatenated and form a correct filter string for a dimension. +# If the source is a subset then it should be a public subset. It can be static or dynamic. It can be permanent or temporary. +# PA alternate hierarchies are not allowed. Make sure that string count limits are not exceeded. # The global string variable 'sFilter_String' is populated. The process can also append to it. # Up to 5 filters can be created with 1 call of this process. +# Do not forget the keywords 'subset' and 'mdx' in the parameter values. # Use case: Intended for Development but could be used in production too. # To circumvent the limitation with Bedrock filter strings that they can only contain hardcoded lists of elements. @@ -150,7 +149,7 @@ nErrors = 0; ## Default delimiters If( pSelectionDelim @= '' ); - pSelectionDelim = '&'; + pSelectionDelim = '\'; EndIf; If( pDimDelim @= '' ); pDimDelim = '&'; @@ -170,76 +169,102 @@ sTreated_Dimensions = '#'; # Loop through the selections nSelectionIndex = 1; While( nSelectionIndex <= 5 ); - sDimension = ''; sSubset = ''; - sSelection = Expand( '%pSelection_' | NumberToString( nSelectionIndex ) | '%' ); - nDimDelimiterIndex = 1; + sParameter_Name = 'pSelection_' | NumberToString( nSelectionIndex ); + sSelection = Expand( '%' | sParameter_Name | '%' ); If( sSelection @<> '' ); - # Get the parts in the string + sDimension = ''; + sSubset = ''; + sMDX = ''; + nNrOfDelimiters = 0; + + # Get the different parts in the string + nDimDelimiterIndex = 1; While( nDimDelimiterIndex <> 0 ); - + nDimDelimiterIndex = Scan( pSelectionDelim, sSelection ); - If( nDimDelimiterIndex = 1 ); - nErrors = 1; - sPart = sSelection; - If( pLogOutput = 1 ); - sMessage = Expand( 'Empty dimension name or subset name extracted from a selection parameter: %sSelection% (iteration ' | NumberToString( nSelectionIndex ) | ')' ); - LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); - EndIf; - ElseIf( nDimDelimiterIndex = 0 ); + If( nDimDelimiterIndex = 0 ); sPart = sSelection; Else; sPart = Trim( Subst( sSelection, 1, nDimDelimiterIndex - 1 )); sSelection = Trim( Delet( sSelection, 1, nDimDelimiterIndex )); EndIf; - # No wildcards allowed, no colons either - If( Scan( '*', sPart ) > 0 % Scan( '?', sPart ) > 0 & Scan( ':', sPart ) > 0 ); - nErrors = 1; - If( pLogOutput = 1 ); - sMessage = Expand( 'Invalid characters observed in a selection parameter: %sPart% (iteration ' | NumberToString( nSelectionIndex ) | ')' ); - LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + nNrOfDelimiters = nNrOfDelimiters + 1; + If( nNrOfDelimiters = 1 ); + sDimension = sPart; + ElseIf( nNrOfDelimiters = 2 ); + If( Scan( 'subset:', Lower( sPart )) = 1 ); + sSubset = Trim( Delet( sPart, 1, 7 )); + ElseIf( Scan( 'mdx:', Lower( sPart )) = 1 ); + sMDX = Trim( Delet( sPart, 1, 4 )); EndIf; EndIf; - ### Check for errors before continuing - If( nErrors <> 0 ); - If( pStrictErrorHandling = 1 ); - ProcessQuit; - Else; - ProcessBreak; - EndIf; - EndIf; + End; - # No errors observed - If( sDimension @= '' ); - sDimension = sPart; - Else; - sSubset = sPart; - nDimDelimiterIndex = 0; + # Validation checks + If( sDimension @= '' ); + nErrors = 1; + If( pLogOutput = 1 ); + sMessage = Expand( 'Empty dimension name: %sDimension% [%sParameter_Name%]' ); + LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); EndIf; + EndIf; - End; + If( Scan( ':', sDimension ) > 0 ); + nErrors = 1; + If( pLogOutput = 1 ); + sMessage = Expand( 'Hierarchies are not accepted: %sDimension% [%sParameter_Name%]' ); + LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + EndIf; + EndIf; If( DimensionExists( sDimension ) = 0 ); nErrors = 1; If( pLogOutput = 1 ); - sMessage = Expand( 'Invalid dimension name: %sDimension% (iteration ' | NumberToString( nSelectionIndex ) | ')' ); + sMessage = Expand( 'Invalid dimension name: %sDimension% [%sParameter_Name%]' ); LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); EndIf; EndIf; - If( SubsetExists( sDimension, sSubset ) = 0 ); + + If( sSubset @= '' & sMDX @= '' ); nErrors = 1; If( pLogOutput = 1 ); - sMessage = Expand( 'Invalid subset name: %sSubset% in dimension %sDimension% (iteration ' | NumberToString( nSelectionIndex ) | ')' ); + sMessage = Expand( 'Empty subset name and empty MDX specification, or missing keywords ''subset:'' and ''mdx:'': %sDimension% [%sParameter_Name%]' ); LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); EndIf; EndIf; + If( sSubset @<> '' ); + If( SubsetExists( sDimension, sSubset ) = 0 ); + nErrors = 1; + If( pLogOutput = 1 ); + sMessage = Expand( 'Invalid subset name: %sSubset% in dimension %sDimension% [%sParameter_Name%]' ); + LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + EndIf; + EndIf; + EndIf; + If( Scan( '#' | NumberToString( Dimix( '}Dimensions', sDimension )) | '#', sTreated_Dimensions ) > 0 ); nErrors = 1; If( pLogOutput = 1 ); - sMessage = Expand( 'Dimension used twice: %sDimension% (iteration ' | NumberToString( nSelectionIndex ) | ')' ); + sMessage = Expand( 'Dimension used twice: %sDimension% [%sParameter_Name%]' ); + LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + EndIf; + EndIf; + + # All good for now, let's continue with the heart of the process + # We create a temporary subset and loop over its contents + If( sSubset @<> '' ); + sMDX = 'Distinct( TM1SubsetToSet( [' | sDimension | '], "' | sSubset | '", "public" ))'; + EndIf; + SubsetCreateByMDX( cTempSub, sMDX, sDimension, 1 ); + n = SubsetGetSize( sDimension, cTempSub ); + If( n = 0 ); + nErrors = 1; + If( pLogOutput = 1 ); + sMessage = Expand( 'The ' | If( sSubset @<> '', 'subset', 'MDX' ) | ' selection leads to 0 elements or an invalid MDX was passed: %sDimension% in %sDimension% [%sParameter_Name%]' ); LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); EndIf; EndIf; @@ -253,11 +278,12 @@ While( nSelectionIndex <= 5 ); EndIf; EndIf; - # All good now, let's continue with the heart of the process + + # Finally there, let's loop and concatenate element names sFilter = sFilter | If( sFilter @= '', '', ' ' | pDimDelim | ' ' ) | sDimension | pEleStartDelim; m = 1; - While( m <= SubsetGetSize( sDimension, sSubset )); - vElement = SubsetGetElementName( sDimension, sSubset, m ); + While( m <= n ); + vElement = SubsetGetElementName( sDimension, cTempSub, m ); sFilter = sFilter | If( m = 1, '', ' ' | pEleDelim | ' ' ) | vElement; m = m + 1; End; From 4da5f53e4f562aa4807dd3d7c51ef26cf9b71ed3 Mon Sep 17 00:00:00 2001 From: Wim Gielis Date: Sun, 11 Jan 2026 11:40:15 +0100 Subject: [PATCH 3/3] Updated version --- main/}bedrock.dim.filter.fromsubset.pro | 125 +++++++++++++++--------- 1 file changed, 80 insertions(+), 45 deletions(-) diff --git a/main/}bedrock.dim.filter.fromsubset.pro b/main/}bedrock.dim.filter.fromsubset.pro index ae5be2b..63ee7b1 100644 --- a/main/}bedrock.dim.filter.fromsubset.pro +++ b/main/}bedrock.dim.filter.fromsubset.pro @@ -80,21 +80,27 @@ pEleDelim,"OPTIONAL: Delimiter between elements (default value if blank = '+')" 581,0 582,0 603,0 -572,218 +572,255 #Region CallThisProcess # A snippet of code provided as an example how to call this process should the developer be working on a system without access to an editor with auto-complete. If( 1 = 0 ); - StringGlobalVariable('sFilter_String'); - vFilter = 'Year¦2025 + 2026 & '; + StringGlobalVariable('vElementFilter'); + NumericGlobalVariable('nProcessReturnCode'); + vElementFilter = 'Year¦2025 + 2026 & '; # or: - vFilter = ''; - # or even without a vFilter and just with sFilter_String + vElementFilter = ''; + # or even without a Filter ExecuteProcess( '}bedrock.dim.filter.fromsubset', 'pLogOutput', pLogOutput, 'pStrictErrorHandling', pStrictErrorHandling, 'pSelectionDelim', '\', 'pDimDelim', '&', 'pEleStartDelim', '¦', 'pEleDelim', '+', 'pSelection_1', 'Year \ subset: My years subset', 'pSelection_2', 'Company \ subset: Level 0 company', 'pSelection_3', '', 'pSelection_4', '', 'pSelection_5', '' ); - vFilter = vFilter | sFilter_String; + If( nProcessReturnCode = 0 ); + LogOutput('ERROR', 'The process to create a Bedrock filter string did not give the desired result. Please investigate.' ); + ProcessQuit; + EndIf; + + LogOutput('INFO', vElementFilter ); EndIf; #EndRegion CallThisProcess @@ -111,9 +117,9 @@ EndIf; # The elements that are found will be concatenated and form a correct filter string for a dimension. # If the source is a subset then it should be a public subset. It can be static or dynamic. It can be permanent or temporary. # PA alternate hierarchies are not allowed. Make sure that string count limits are not exceeded. -# The global string variable 'sFilter_String' is populated. The process can also append to it. +# The global string variable 'vElementFilter' is populated. The process can also append to it. # Up to 5 filters can be created with 1 call of this process. -# Do not forget the keywords 'subset' and 'mdx' in the parameter values. +# Do not forget the keyword 'subset' or 'mdx' for the selection filter parameter values. # Use case: Intended for Development but could be used in production too. # To circumvent the limitation with Bedrock filter strings that they can only contain hardcoded lists of elements. @@ -165,6 +171,8 @@ EndIf; # Initialization sFilter = ''; sTreated_Dimensions = '#'; +nApplied_Filters = 0; +sApplied_Filters = ''; # Loop through the selections nSelectionIndex = 1; @@ -208,71 +216,87 @@ While( nSelectionIndex <= 5 ); nErrors = 1; If( pLogOutput = 1 ); sMessage = Expand( 'Empty dimension name: %sDimension% [%sParameter_Name%]' ); - LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + LogOutput( cMsgErrorLevel, Expand( cMsgInfoContent ) ); EndIf; - EndIf; - If( Scan( ':', sDimension ) > 0 ); + ElseIf( Scan( ':', sDimension ) > 0 ); nErrors = 1; If( pLogOutput = 1 ); sMessage = Expand( 'Hierarchies are not accepted: %sDimension% [%sParameter_Name%]' ); - LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + LogOutput( cMsgErrorLevel, Expand( cMsgInfoContent ) ); EndIf; - EndIf; - If( DimensionExists( sDimension ) = 0 ); + ElseIf( DimensionExists( sDimension ) = 0 ); nErrors = 1; If( pLogOutput = 1 ); sMessage = Expand( 'Invalid dimension name: %sDimension% [%sParameter_Name%]' ); - LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + LogOutput( cMsgErrorLevel, Expand( cMsgInfoContent ) ); EndIf; - EndIf; - If( sSubset @= '' & sMDX @= '' ); + ElseIf( Scan( '#' | NumberToString( Dimix( '}Dimensions', sDimension )) | '#', sTreated_Dimensions ) > 0 ); + nErrors = 1; + If( pLogOutput = 1 ); + sMessage = Expand( 'Dimension used twice: %sDimension% [%sParameter_Name%]' ); + LogOutput( cMsgErrorLevel, Expand( cMsgInfoContent ) ); + EndIf; + + ElseIf( sSubset @= '' & sMDX @= '' ); nErrors = 1; If( pLogOutput = 1 ); sMessage = Expand( 'Empty subset name and empty MDX specification, or missing keywords ''subset:'' and ''mdx:'': %sDimension% [%sParameter_Name%]' ); - LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + LogOutput( cMsgErrorLevel, Expand( cMsgInfoContent ) ); EndIf; - EndIf; - If( sSubset @<> '' ); + ElseIf( sSubset @<> '' ); If( SubsetExists( sDimension, sSubset ) = 0 ); nErrors = 1; If( pLogOutput = 1 ); sMessage = Expand( 'Invalid subset name: %sSubset% in dimension %sDimension% [%sParameter_Name%]' ); - LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + LogOutput( cMsgErrorLevel, Expand( cMsgInfoContent ) ); + EndIf; + ElseIf( SubsetGetSize( sDimension, sSubset ) = 0 ); + nErrors = 1; + If( pLogOutput = 1 ); + sMessage = Expand( 'Empty subset: %sSubset% in dimension %sDimension% [%sParameter_Name%]' ); + LogOutput( cMsgErrorLevel, Expand( cMsgInfoContent ) ); EndIf; EndIf; EndIf; - If( Scan( '#' | NumberToString( Dimix( '}Dimensions', sDimension )) | '#', sTreated_Dimensions ) > 0 ); - nErrors = 1; - If( pLogOutput = 1 ); - sMessage = Expand( 'Dimension used twice: %sDimension% [%sParameter_Name%]' ); - LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + ### Check for errors before continuing + If( nErrors <> 0 ); + If( pStrictErrorHandling = 1 ); + ProcessQuit; + Else; + ProcessBreak; EndIf; EndIf; - # All good for now, let's continue with the heart of the process - # We create a temporary subset and loop over its contents + # When we use an MDX statement, we create a temporary subset first If( sSubset @<> '' ); - sMDX = 'Distinct( TM1SubsetToSet( [' | sDimension | '], "' | sSubset | '", "public" ))'; + vSubset_For_Loop = sSubset; + Else; + SubsetCreateByMDX( cTempSub, sMDX, sDimension, 1 ); + vSubset_For_Loop = cTempSub; EndIf; - SubsetCreateByMDX( cTempSub, sMDX, sDimension, 1 ); - n = SubsetGetSize( sDimension, cTempSub ); + + n = SubsetGetSize( sDimension, vSubset_For_Loop ); If( n = 0 ); nErrors = 1; If( pLogOutput = 1 ); - sMessage = Expand( 'The ' | If( sSubset @<> '', 'subset', 'MDX' ) | ' selection leads to 0 elements or an invalid MDX was passed: %sDimension% in %sDimension% [%sParameter_Name%]' ); - LogOutput( 'ERROR', Expand( cMsgInfoContent ) ); + If( sSubset @<> '' ); + sMessage = Expand( 'The subset selection leads to 0 elements: %sDimension% in %sDimension% [%sParameter_Name%]' ); + Else; + sMessage = Expand( 'The MDX selection leads to 0 elements or an invalid MDX was passed: %sDimension% in %sDimension% [%sParameter_Name%]' ); + EndIf; + LogOutput( cMsgErrorLevel, Expand( cMsgInfoContent ) ); EndIf; EndIf; ### Check for errors before continuing If( nErrors <> 0 ); - If( pStrictErrorHandling = 1 ); - ProcessQuit; + If( pStrictErrorHandling = 1 ); + ProcessQuit; Else; ProcessBreak; EndIf; @@ -283,20 +307,33 @@ While( nSelectionIndex <= 5 ); sFilter = sFilter | If( sFilter @= '', '', ' ' | pDimDelim | ' ' ) | sDimension | pEleStartDelim; m = 1; While( m <= n ); - vElement = SubsetGetElementName( sDimension, cTempSub, m ); + vElement = SubsetGetElementName( sDimension, vSubset_For_Loop, m ); sFilter = sFilter | If( m = 1, '', ' ' | pEleDelim | ' ' ) | vElement; m = m + 1; End; # Note that this dimension is done and should not be used more than once + # Work towards an overall summary of the activity in the filtering sTreated_Dimensions = sTreated_Dimensions | NumberToString( Dimix( '}Dimensions', sDimension )) | '#'; + nApplied_Filters = nApplied_Filters + 1; + sApplied_Filters = sApplied_Filters | If( nApplied_Filters = 1, '', ' ' | pDimDelim | ' ' ) | 'dimension ''' | sDimension | ''' ==> ' | NumberToString(n) | ' element' | If( n = 1, '', 's'); EndIf; nSelectionIndex = nSelectionIndex + 1; End; - -sFilter_String = sFilter_String | If( sFilter_String @= '', '', ' ' | pDimDelim | ' ' ) | sFilter; +### Do we have 1 or more filters that were applied? +If( nApplied_Filters > 0 ); + If( pLogOutput = 1 ); + sMessage = Expand( 'The selections lead to a filter string based on: ' | Trim( sApplied_Filters )); + LogOutput( 'INFO', Expand( cMsgInfoContent ) ); + EndIf; + vElementFilter = vElementFilter | If( Subst( Trim(vElementFilter), Long( Trim(vElementFilter) ) + 1 - Long(pDimDelim), Long(pDimDelim)) @= pDimDelim, '', ' ' | pDimDelim | ' ' ) | ' ' | sFilter; +Else; + nErrors = 1; + sMessage = Expand( 'All selections are empty.' ); + LogOutput( cMsgErrorLevel, Expand( cMsgInfoContent ) ); +EndIf; ### End Prolog ### 573,4 @@ -309,7 +346,7 @@ sFilter_String = sFilter_String | If( sFilter_String @= '', '', ' ' | pDimDelim #****Begin: Generated Statements*** #****End: Generated Statements**** -575,29 +575,27 #****Begin: Generated Statements*** #****End: Generated Statements**** @@ -320,24 +357,22 @@ sFilter_String = sFilter_String | If( sFilter_String @= '', '', ' ' | pDimDelim ### Return code & final error message handling If( nErrors > 0 ); - sMessage = 'the process incurred at least 1 error. Please see above lines in this file for more details.'; + sMessage = 'the process incurred at least 1 error. Please see above lines in this file for more details. Be careful not to clear/copy/export/... too much data in the calling process!'; nProcessReturnCode = 0; LogOutput( cMsgErrorLevel, Expand( cMsgErrorContent ) ); sProcessReturnCode = Expand( '%sProcessReturnCode% Process:%cThisProcName% completed with errors. Check tm1server.log for details.' ); - If( pStrictErrorHandling = 1 ); - ProcessQuit; + If( pStrictErrorHandling = 1 ); + ProcessQuit; EndIf; Else; sProcessAction = Expand( 'Process:%cThisProcName% successfully returned/updated the filter string.' ); sProcessReturnCode = Expand( '%sProcessReturnCode% %sProcessAction%' ); nProcessReturnCode = 1; If( pLogoutput = 1 ); - LogOutput('INFO', Expand( sProcessAction ) ); + LogOutput('INFO', Expand( sProcessAction ) ); EndIf; EndIf; - - ### End Epilog ### 576, 930,0