Pat 162007597
Pat 162007597
#
# Issue: LPAD-126663
# Module:
# Version: Content Server 16.2.0 64-bit for Windows
# Update: 24.2.0 (2024-03),24.1.0 (2023-12),23.4.0 (2023-09)
#
# Description:
# Poor performance of Name filter on SmartUI browse for large folders
#
m {'kernel','lliapi','webclassification'}
o {'pat162007597::LLNode',&&lliapi::LLNode}
N LLNode
f NodeContainerContextGet
s
/**
* Return a context string for this container object. This can be used in the
creation of SQL to provide
* context to the size of the container that allows the DB to generate optimal
explain plans for the
* differing sizes
*
* @param {Dynamic} node Either the DAPINODE or RecArray to the object
*
* @return {String} Either an empty string or the context based on the container
size ie "4=4"
*/
function String NodeContainerContextGet( Dynamic node )
Assoc checkVal
String anchorString
if ( checkVal.ok )
//
// the optimizers in SQL server, Oracle and probably others are using
optimistic algorithms
// testing found that since the SQL for a container of 10 items is the
same as the SQL for a container
// 1m items, explain or execution plans will be cached according to the
first execution and the context.
// What this sql fragment does is provide the optimizer a different SQL
statement based on the size of the
// of the container. This allows the optimizer to potentially produce
different plans based on that context.
// The rule here is take the number of children defined by the
childcount and divide by 100K and put that in the SQL
// as an always true term. For example:
// 0-100k --- 1=1
// 100k-200k --- 2=2
// 200k-300k --- 3=3
// 300k-400k --- 4=4
// 400k-500k --- 5=5
//
anchorString = Str.Format( " %1=%1",
Math.Round( ( checkVal.childCount / 10000 ) ) + 1 )
end
//
// if the checkVal.ok ! true, then not much to be done here.
// If there is a DB error etc then other places will get access to that
// and provide better context.
//
return anchorString
end
sEND
f _BrowseTagsReplace
s
/**
* @private
*
* this function replaces various tags generated in the SQL statements
*
* @param {Object} prgCtx Program session object
* @param {String} stmt The SQL statement to be changed
*
* @return {String}
*/
function String _BrowseTagsReplace( Object prgCtx, String stmt )
String nameColumn =
$LLIAPI.LanguagePkg.GetSQLSortString( prgCtx, 'wnm.Name' )
String lowerNameColumn =
$LLIAPI.LanguagePkg.GetSQLLowerString( prgCtx, 'wnm.Name' )
Boolean isMultilingual
= ._GetSelectWithPaging_IsMultilingual( prgCtx.DSession() )
end
return stmt
end
sEND
f _GetBrowseJoinCondition
s
/*
* This method provides the join condition for browse type queries. On MSSQL, the
LEFT JOIN
* performs better than the INNER JOIN whereas the other db's are good with INNER
*/
function String _GetBrowseJoinCondition( Object prgCtx )
return retString
end
sEND
f _GetNewGulpSize
s
/**
* @private
*
* Returns the ideal gulp size based on the last percentGood retrieved.
* This method will improve performance in the case the user has no permission to a
number of objects,
* allowing the application to retrieve an extra number of objects other than the
one defined in pagination.
*
* The maxBrowseGulpSize is set to 2500 unless ActiveView is being used, and we may
not allow the user to change this value,
* since setting higher numbers won't cause improvements in performance.
*
* @param {Integer} numberRequired Number of rows required
* @param {Real} percentGood Percentage good ( value between 0.0 and 1.0 )
* @param {Boolean} allResults If TRUE then ActiveView is being used, so
ignore MaxBrowseGulpSize and return all results
*
* @return {Integer} gulpSize New gulp size to use
*
*/
function Integer _GetNewGulpSize(
Integer numberRequired,
Real percentGood,
Boolean allResults )
Integer gulpSize
Integer paddingFactor
//
// Make sure the percent good never gets to zero.
//
paddingFactor = maxBrowseGulpSize
paddingFactor = 50
paddingFactor = 30
paddingFactor = 15
paddingFactor = 5
end
return gulpSize
end
sEND
f _GetSelectWithPaging_GetAnchor
s
/**
* @private
*
* Returns a SQL statement to fetch all potential items (unpermissioned) in the
container.
*
* @param {Object} dapiCtx
* @param {DAPINODE} node
* @param {Assoc} options
*
* @return {Assoc}
* @returnFeature {Boolean} ok FALSE if an error occurred, TRUE otherwise
* @returnFeature {String} errMsg Error message, if an error occurred
* @returnFeature {Dynamic} apiError Application error, if applicable
* @returnFeature {String} anchor The string that contains the anchor to the
parent depending on the browsemode
* @returnFeature {List} params Bind variables used for this statement
* @returnFeature {String} tableJoin The SQL fragment used to join the browse
sql to other tables
*
*/
function Assoc _GetSelectWithPaging_GetAnchor( Object dapiCtx, DAPINODE node, Assoc
options )
Assoc rtnVal
Dynamic apiError
List params
String anchor
String errMsg
String tableJoin
Boolean ok = TRUE
// LPAD-127345
if ( IsDefined( node ) )
//
// In most cases the parent params are always the node.pID
// the root object and the volume are checked.
//
// the anchor is the one required element needed for a browse operation
// in the case of the regular container browse it is the parent ID
//
params = { node.pID }
anchor = "ParentID=:A0"
else
end
end
rtnVal.ok = ok
rtnVal.errMsg = errMsg
rtnVal.apiError = apiError
rtnVal.anchor = anchor
rtnVal.params = params
rtnVal.tableJoin = tableJoin
return rtnVal
end
sEND
f _GetSelectWithPaging_IsMultilingual
s
/**
* @private
*
* Returns if the current system is a monolingual or multilingual to allow the
optimization
* of the the browse SQL
*
* @param {Object} dapiCtx
*
* @return {Boolean}
*/
function Boolean _GetSelectWithPaging_IsMultilingual( Object dapiCtx )
Assoc checkVal
if ( checkVal.ok == TRUE )
isMultilingual = checkVal.isMultilingual
end
return isMultilingual
end
sEND
f _GetSelectWithPaging_Paging
s
/**
* @private
*
* Returns a SQL statement that modifies the incoming sql to get wrapped
* with paging SQL. If the options states sql paging is not to be used then
* no change is made to the stmt. Called from _GetSelectWithPaging
*
* @param {Object} prgCtx User session
* @param {String} stmt sql string to wrap with the paging sql
* @param {Assoc} options options assoc indicating if sql pagin
should be used
*
* @return {Assoc}
* @returnFeature {Boolean} ok
* @returnFeature {String} errMsg
* @returnFeature {Dynamic} apiError
* @returnFeature {Assoc} stmt SQL statement
*
* @private
*/
function Assoc _GetSelectWithPaging_Paging( Object prgCtx, String stmt, Assoc
options )
Assoc rtnVal
Dynamic apiError
String errMsg
String newStmt
Boolean ok = TRUE
Boolean isOracle = prgCtx.fDBConnect.IsOracle()
Boolean isOracle12c = prgCtx.fDBConnect.IsOracle12c()
Boolean isMSSQL = prgCtx.fDBConnect.IsMSSQL()
Boolean sqlPaginationEnabled = CAPI.IniGet(
prgCtx.fDBConnect.fLogin,
'SystemAdministration',
'OptimizedBrowsePaginati
onEnabled',
TRUE )
Integer maxChildSizeForNonPaging = 1000
// the kini value can override any settings. This is a safety switch
options.sqlPaging = sqlPaginationEnabled ? options.sqlPaging : FALSE
if ( !sqlPaginationEnabled )
end
// LPAD-127345
echo( "_GetSelectWithPaging_Paging: turning off sql paginaiton" )
options.sqlPaging = FALSE
end
if ( options.sqlPaging )
.fIsPagingSQLPagination = TRUE
if ( isMSSQL || isOracle12c )
newStmt = stmt + " OFFSET :B1 ROWS FETCH NEXT :B2 ROWS ONLY "
elseif ( isOracle )
newStmt = Str.Format(
"SELECT /*+ BIND_AWARE NOPARALLEL */ OwnerID,
DataID, Catalog, GroupID, PermID FROM " +
"( SELECT a.*, rownum r__ FROM ( %1 ) a WHERE rownum <= :B2
) " +
"WHERE r__ >= :B1",
stmt )
else
end
else
.fIsPagingSQLPagination = FALSE
newStmt = stmt
end
rtnVal.ok = ok
rtnVal.errMsg = errMsg
rtnVal.apiError = apiError
rtnVal.stmt = newStmt
return rtnVal
end
sEND
f _GetSelectWithPaging_clauseTypeFilter
s
/**
* @private
*
* Returns SQL fragment used to filter based on the name
*
* @param {Object} dapiCtx DAPI session object
* @param {DAPINODE} node The browse page node
* @param {String} viewName The database view name
* @param {String} userPrefLanguage The user prefered language
* @param {String} sortField The view column name to sort on
* @param {Assoc} options An assoc containing SQL options. See
{@link _ValidateOptions}
*
* @return {Assoc}
* @returnFeature {Boolean} ok FALSE if an error occurred, TRUE otherwise
* @returnFeature {String} errMsg Error message, if an error occurred
* @returnFeature {Dynamic} apiError Application error, if applicable
* @returnFeature {Boolean} hasFilter Boolean indicating if the query has any
additional filters.
* @returnFeature {Assoc} clause assoc with info about a fragment of a SQL
clause
* @returnFeature {String} sql SQL fragment
* @returnFeature {List} params The first element contains the parent ID,
the second (optional) parameter contains filtering data.
*/
function Assoc _GetSelectWithPaging_clauseTypeFilter(
Object dapiCtx,
DAPINODE node,
String viewName,
String userPrefLanguage,
String sortField,
Assoc options )
Assoc checkVal
Assoc clause
Assoc rtnVal
Dynamic apiError
List params
String errMsg
String typeFilterClause
Boolean ok = TRUE
Object dbConnect = dapiCtx.fDBConnect
Boolean hasFilter = FALSE
Boolean IsPostgreSQL = dapiCtx.fPrgCtx.fDBConnect.IsPostgreSQL()
if ( IsPostgreSQL )
else
typeFilterClause = Str.Format(
" AND [lowernameclause] LIKE LOWER(
:A1 ) ESCAPE '%1'",
checkVal.escapeChar )
end
hasFilter = TRUE
end
clause.Sql = typeFilterClause
clause.params = params
rtnVal.ok = ok
rtnVal.errMsg = errMsg
rtnVal.apiError = apiError
rtnVal.hasFilter = hasFilter
rtnVal.clause = clause
return rtnVal
end
sEND
f _GetSelectWithPaging_clauseWhere
s
/**
* @private
*
* This method will return various SQL statements needed for browse.
*
* @param {Object} dapiCtx DAPI session object
* @param {DAPINODE} node The browse page node
* @param {String} viewName The database view name
* @param {String} userPrefLanguage The user prefered language
* @param {String} sortField The view column name to sort on
* @param {Assoc} options An assoc containing SQL options. See
{@link _ValidateOptions}
*
* @return {Assoc}
* @returnFeature {Boolean} ok TRUE if ok.
* @returnFeature {String} errMsg Application error message if not ok.
* @returnFeature {Error} apiError Error returned by API if not ok.
* @returnFeature {Boolean} hasFilter Boolean indicating if the query has any
additional filters.
* @returnFeature {Assoc} clause assoc with info about a fragment of a SQL
clause
* @returnFeature* {String} sql SQL fragment
* @returnFeature* {List} params The first element contains the parent ID,
the second (optional) parameter contains filtering data.
*/
function Assoc _GetSelectWithPaging_clauseWhere(
Object dapiCtx,
DAPINODE node,
String viewName,
String userPrefLanguage,
String sortField,
Assoc options )
Assoc clause
Assoc rtnVal
Dynamic apiError
List params
String errMsg
String whereClause
//
// If the caller has sent in where clause then we need to attach it to the
main script.
//
end
clause.Sql = whereClause
clause.params = params
rtnVal.ok = ok
rtnVal.errMsg = errMsg
rtnVal.apiError = apiError
rtnVal.hasFilter = hasFilter
rtnVal.clause = clause
return rtnVal
end
sEND
f _GetSelectWithPaging_stmtMimeCount
s
/**
* @private
*
* Returns a SQL statement needed for target browse in order to limit the displayed
items
* to a specific mime counts.
*
* @param {Object} dapiCtx DAPI session object
* @param {DAPINODE} node The browse page node
* @param {String} viewName The database view name
* @param {String} userPrefLanguage The user prefered language
* @param {String} sortField The view column name to sort on
* @param {Assoc} options An assoc containing SQL options. See
{@link _ValidateOptions}
* @param {Assoc} clause An assoc containing various SQL clause
snippets strings
* @param* {String} hidden A hidden clause string
* @param* {String} where A where clause string
* @param* {String} typeFilter A type filter clause string
* @param* {String} objectFilter A object filter clause string
* @param* {String} containerOnly A container only filter clause string
* @param* {String} mimeFilter A mime filter clause string
* @param* {String} descriptionFilter A description filter clause string
*
* @return {Assoc}
* @returnFeature {Boolean} ok FALSE if an error occurred, TRUE otherwise
* @returnFeature {String} errMsg Error message, if an error occurred
* @returnFeature {Dynamic} apiError Application error, if applicable
* @returnFeature {Assoc} stmt SQL statement
* @returnFeature {List} params bind variables used for this statement
*/
function Assoc _GetSelectWithPaging_stmtMimeCount(
Object dapiCtx,
DAPINODE node,
String viewName,
String userPrefLanguage,
String sortField,
Assoc options,
Assoc clause )
Assoc rtnVal
Dynamic apiError
List params
String errMsg
String mimeCountStmt
String whereString
Boolean ok = TRUE
if ( IsDefined( node ) )
end
if ( dapiCtx.fDBConnect.IsHana() )
if ( IsDefined( node ) )
end
else
end
end
rtnVal.ok = ok
rtnVal.errMsg = errMsg
rtnVal.apiError = apiError
rtnVal.stmt = mimeCountStmt
rtnVal.params = params
return rtnVal
end
sEND
f fMaxBrowseGulpSize
v 300
f fMaxBrowseGulpSize
v 300
o {'pat162007597::LanguagePkg',&&lliapi::LanguagePkg}
N LanguagePkg
f IsMultilingual
s
/**
* IsMultilingual() will determine whether the Content Server system is
multilingual
* (i.e. has multiple enabled languages).
*
* @param {Object} prgCtx Program session object
*
* @return {Assoc}
* @returnFeature {Boolean} ok FALSE if an error occurred; TRUE
otherwise
* @returnFeature {String} errMsg Error message if an error occurred
* @returnFeature {Dynamic} apiError Application error object if an error
occurred
* @returnFeature {Boolean} isMultilingual TRUE if this is a multilingual system
*/
function Assoc IsMultilingual( Object prgCtx )
Assoc checkVal
Dynamic apiError
Dynamic result
String errMsg
Boolean ok = TRUE
Boolean isMultilingual = FALSE
if ( IsUndefined( .fisMultilingual ) )
if ( IsNotError( result ) )
else
ok = FALSE
errMsg = [LLIAPI_ERRMSG.CouldNotGetEnabledLanguages]
apiError = result
end
.fisMultilingual = isMultilingual
end
return Assoc{
'ok': ok,
'errMsg': errMsg,
'apiError': apiError,
'isMultilingual': .fisMultilingual }
end
sEND
f fisMultilingual
v ?
f fisMultilingual
v ?
o {'pat162007597::Classification DocVolume',&&webclassification::#'Classification
DocVolume'#}
N Classification DocVolume
f _BrowseContents
s
Dynamic checkVal
RecArray contents
Assoc options
Assoc result
Integer i
Integer pageSize
Integer pstageID
RecArray stagingContents
Assoc retVal
String viewName
Assoc sectionData
String exclusionClause
String alias
if ( allContents )
alias = ""
elseif ( isMultilingual )
alias = "dt."
elseif ( !isMultilingual )
alias = "wnm."
end
exclusionClause = 'Catalog!=2'
exclusionClause += Str.Format(" AND %1SubType!=296 ",alias )
else
// this can be 'editconfig' action where we want to show hidden objects
end
for i in request.nodeScreen
if ( IsUndefined( screenClause ) )
else
if ( IsUndefined( screenClause ) )
screenClause = exclusionClause
end
end
if ( checkVal.ok == TRUE )
viewName = checkVal.viewName
else
ok = FALSE
response.error = checkVal.errMsg
end
if ( ok )
pageSize = request.pageSize
else
pageSize = KiniSettings.defaultPageSize
end
contents = result.contents
//
// save data for display if it exists
//
response.data.totalCount = result.totalCount
else
response.data.pages = result.pages
end
response.data.types = result.types
end
response.data.page = request.page
else
response.data.page = 1
end
if IsDefined( $PStage )
if ( request.viewType ==
Str.ValueToString( $ViewTypeStagedContent ) )
response.webScript
= .HTMLFile( 'BrowseBaseStaged', .fHTMLBrowseBaseStaged )
response.data.view
= .HTMLFile( 'BrowseStaged', .fHTMLBrowseStaged )
end
pstageID = node.pID
node = retVal.folder
stagingContents =
DAPI.ListContents( prgCtx.DapiSess(), \
node.pVolumeId, \
node.pId, \
"WebNodes", \
screenClause, \
$PSee )
else
stagingContents =
DAPI.ListContents( prgCtx.DapiSess(), \
node.pVolumeId, \
node.pId, \
"WebNodes" )
end
hasStagedContent = true
if ( request.viewType ==
Str.ValueToString( $ViewTypeStagedContent ) )
RecArray.AddRecord( contents,
RecArray.GetRecord( stagingContents, i ) )
end
end
end
end
end
end
response.data.contents = contents
if ( hasStagedContent )
response.data.hasStagedContent = true
response.data.stagedContent = stagingContents
response.data.pstageID = pstageID
end
response.data.objectsTitle =
[WebClassification_HTMLLabel.ClassifiedItems]
// Setup the data for the call to the ClassifiedItems virtual webnode
// The virtual webnode is used to allow the "Classifications" and
"Classified Items" sections to sort independently.
sectionData = Assoc.CreateAssoc()
response.data.SectionData = sectionData
end
end
sEND