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.

Replace full reference to a  global with one zn.

 Consider the following code:

Class Utils.Global
{

/// Do ##class(Utils.Global).Generate()
ClassMethod Generate(NS = "SAMPLES", Count = 100000000)
{
    New $namespace
    Zn NS
    Kill ^LAB
    For i=1:1:Count {
        Set ^LAB(i) = i
    }
}

/// Do ##class(Utils.Global).Test()
ClassMethod Test(NS = "SAMPLES")
{
    Set time = $p($h,",",2)
    Do ..Ref1(NS)
    Set time1 = $p($h,",",2)
    Do ..Ref2(NS)
    Set time2 = $p($h,",",2)
    Write "Full ref time ",time1-time,!,"ZN time ",time2-time1
}

ClassMethod Ref1(NS)
{
    Set PIDX=""    
    For {
        Set PIDX=$ORDER(^[NS]LAB(PIDX))
        Quit:PIDX=""  
    }
}

ClassMethod Ref2(NS)
{
    New $namespace
    Zn NS
    Set PIDX=""    
    For {
        Set PIDX=$ORDER(^LAB(PIDX))
        Quit:PIDX=""  
    }
}

}

When I run Do ##class(Utils.Global).Test() in a terminal I get the following results:

Full ref time 38
ZN time 35

even better difference on smaller loops (10000000):

Full ref time 3
ZN time 2

GitHub.