Here's the one I thought up. Does not use indirection.

/// Returns true if arrays <var>pFirst</var> and <var>pSecond</var> have all the same subscripts and all
/// the same values at those subscripts. <br />
/// If <var>pFirst</var> and <var>pSecond</var> both happen to be either undefined or unsubscripted variables
/// returns true if they're both undefined or have the same value or one is undefined and the other empty
/// <var>pMessage</var> has details of the first difference found, if any.
ClassMethod CompareArrays2(ByRef pFirst, ByRef pSecond, Output pMessage) As %Boolean
{
    Set pMessage = ""
    Return:(($Data(pFirst) '= 10) || ($Data(pSecond) '= 10)) $Get(pFirst) = $Get(pSecond)
    Merge First = pFirst
    Merge Second = pSecond
    Set Key = $Order(First(""))
    
    /// Iterate over first array
    While (Key '= "") {
        
        /// $Data on undefined var does not modify second argument
        Kill SecondVal
        
        /// If the second array does not have the same subscript
        /// or the values are different, quit
        If (($Data(Second(Key), SecondVal) = 0) || ($Get(SecondVal) '= First(Key))) {
            Set pMessage = "Different subscripts at " _ Key
            Return $$$NO
        } Else {
            /// Otherwise remove this element from the second array
            /// In here: Second(Key) = First(Key)
            Kill Second(Key)
        }
        Set Key = $Order(First(Key))
    }
    
    /// Second array should have no subscripts
    /// If there are any, that means they are not present
    /// in the first array, and so different
    If $Data(Second) = 10 {
        Set pMessage = "Different subscripts at " _ $Order(Second(""))
        Return $$$NO        
    }
    
    Return $$$YES
}

 Another question what is the sense to check "val" for IS NULL for Unique Index?

This check, if hit returns first Id with empty val.

So, "val" should exactly match the value of property, case sensitive?

That depends on property collation. For EXACT/ TRUNCATE/SQLSTRING collation, yes "val" should exactly match the value of the property (compared part of the value), case sensitive. For SQLUPPER - no.

Sure did. To clarify, it's the fastest way available by default. The fastest way is a direct global reference (provided of course that we do not have the object in a context already available), but we need to either hardcode the global reference or calculate it at compile time. Here's the test class (I won't copy it here - it's fairly long). The results are like this:

Iterations: 10000
Object access: .130111
GetStored access: .014388
SQL access: .020268
Global access: .007717
Object access takes 904.30% of GetStored time
Object access takes 641.95% of SQL time
Object access takes 1686.03% of Global time
GetStored access takes 70.99% of SQL time
GetStored access takes 186.45% of Global time
SQL access takes 262.64% of Global time

Where:

  • Object access is opening an object and retrieving the property
  • SQL access is embedded SQL
  • GetStored is a method discussed in this article
  • Global is a direct global reference to a stored property

One piece of code I'd like to share here is the macro to get global reference for a property by a class and property name:

Class Utils.GetStored Extends %Persistent
{

Property text As %String;

ClassMethod Global() As %Status
{
    #Define GetStoredProp(%cls,%prop,%idvar) ##Expression(##class(Utils.GetStored).GetPropGLVN(%cls,%prop, $name(%idvar)))
    Set Id = $Random(999)+1
    Set Val = $$$GetStoredProp("Utils.GetStored","text", Id)
}

/// Write ##class(Utils.GetStored).GetPropGLVN("Utils.GetStored", "text")
ClassMethod GetPropGLVN(Class, Property, IdVar = "id") As %String
{
    Set StoredCode = $Get($$$EXTPROPondisk($$$pEXT,Class,Property))
    Set StoredCode = $Replace(StoredCode, "(id)", "(" _ IdVar _ ")")
    Return StoredCode
}

On compilation the string with $$$GetStoredProp macro would be compiled into:

Set Val = $listget($g(^Utils.GetStoredD(Id)),2) 

"val" should be in Upper case I suppose?

No.  IndexOpen calls IndexExists to get object ID.  In IndexExists "val" is matched to corresponding ID with the following SQL expression (except for IDKEYExists. It calls %ExistsId):

SELECT %ID INTO :id 
FROM Package.Class 
WHERE 
  (val IS NOT NULL AND IndexProperty = val) OR 
  (val IS NULL     AND IndexProperty IS NULL)

The interesting question would be - why not traverse index global to get id instead of using SQL?

Cube has 2 methods:

  • %OnGetFilterSpec  - callback method which executes for every query and gives a Cube a chance to programmatically define a filter spec
  • %KillCache - deletes all cached values for the cube

If you combine them like this, you can get what you want:

ClassMethod %OnGetFilterSpec(pFilterSpec As %String) As %String
{
    Do  ..%KillCache()
    Quit pFilterSpec
}

Note, that %OnGetFilterSpec   has an access to %query object which is of %DeepSee.Query.query  class. I tried to set  some of it params but it didn't seem to help:

Set %query.useCache = $$$NO
Set %query.%mustCompute = $$$YES

You can try to modify %query some other way, or you can try set a query error global node

Set $$$DeepSeeResultsGLVN(%cubeIndex, %query.%QueryKey,"error") = "whatever"

It forces the query to recompute the results.

I think it's definitely doable, but you need to tinker with either %query or query  global cache. Well, it depends on the purpose: if it's low traffic/dev system killing all the cube cache is ok, but you may want to kill the cache only  for a current query.

Based on Alexanders comment, I think this should work.

<form method="post" action="">
<table>
<tr><td><textarea rows="40" cols="200" name="submitstring"></textarea></td></tr>
<tr><td><select name="decodeoption"><option>Decode</option><option>Encode</option></select><input type="submit"/></td></tr>
<tr><td>&nbsp;</td></tr>
<tr><td><h2>Result</h2></td></tr>
<tr><td>

<script language=Cache runat=server>
Set tString = $Get(%request.Data("submitstring",1))
Set tAction = $Get(%request.Data("decodeoption",1))
If tAction = "Decode" {
    Set tString = $SYSTEM.Encryption.Base64Decode(tString)
    Set tOutput = $ZCONVERT(tString,"I","UTF8")
} Else {
    Set tString = $ZCONVERT(tString,"O","UTF8")
    Set tOutput = $SYSTEM.Encryption.Base64Encode(tString)
}
Write tOutput
</script>
</td></tr>
</table>
</form>

I added  UTF8 conversion:

Set tOutput = $ZCONVERT(tString,"I","UTF8")

and

 Set tString = $ZCONVERT(tString,"O","UTF8")

It may be useful as one of the metrics related to the code quality/monitoring.

For example, my continuous integration system Cache GitHub CI tracks compile time for each commit, and if it suddenly grows, then the offending commit may be worth looking into.  But if we add one other metric - "lines of code added", then some of the offending commits may be removed based on a now determinable fact that a raise in compilation time is caused by a project size increase.

On the screenshot: compilation time (Length column) for some commits in a real-life project.

 

Other usage - find classes longer than, for example, 500 sloc and separate them into several classes.