Eduard Lebedyuk · Jul 17, 2016 go to post

tParams should be an array:

set tParms("tExperssion") = tExperssion
set tParms("tIndex") = tIndex

and this line:

<xsl:copy-of select="$tExperssion"/>

should maybe be:

<xsl:value-of select="$tExperssion"/>
Eduard Lebedyuk · Jul 15, 2016 go to post

You can change wildcard programmatically, see this topic. Though I think the better solution would be subclassing and calculating wildcard there. 

Eduard Lebedyuk · Jul 15, 2016 go to post

Time Stamp Specifications for Filenames - while configuring business operations and business services that transmit data to and from files, you can often specify input and output filenames in a string that includes date and time format codes, such as %Y%M%d%h%m%s_%f.txt. At runtime, the format codes within this string resolve dynamically based on the current date and time.

Eduard Lebedyuk · Jul 15, 2016 go to post

That's generally a questionable idea, here's why:

  • It's the slowest possible method - if we're iterating over result set the application logic may decide to end processing not after the last row but earlier. If a whole result set is returned, then we still spent CPU/RAM to calculate something we may not even need
  • Result set may be quite large which would cause application logic errors
  • Result set may be larger than amount of RAM available
  • Time to first result is usually smaller than getting all results, so the user can start working with the first row, while an application fetches more rows
Eduard Lebedyuk · Jul 15, 2016 go to post

I need "u" flag in studio. During compilation the flags from user (studio) are combined with namespace or system flags. So even if I set namespace default flags without "u", the compiler would still use this flag as it is present in studio.

Eduard Lebedyuk · Jul 12, 2016 go to post

Well, that depends.

  1. If an index definition includes an explicitly specified collation for a property, the index uses that collation.
  2. If an index definition does not include an explicitly specified collation for a property, the index uses the collation explicitly specified in the property definition.
  3. If the property definition does not include an explicitly specified collation, then the index uses the collation that is the default for the property data type.
  4. If the property data type does not include an explicitly specified collation, then the index uses namespace default collation
  5. If the namespace default collation is not specified, then SQLUPPER is used.
Eduard Lebedyuk · Jul 12, 2016 go to post

Here's the list of all intrinsic member properties:

  • ID
  • KEY
  • NAME
  • MEMBER_NAME
  • CAPTION
  • CUBE_NAME
  • LEVEL_NUMBER
  • LEVEL
  • HIERARCHY
  • DIMENSION
  • LEVEL_CAPTION
  • DIMENSION_CAPTION
  • HIERARCHY_CAPTION
Eduard Lebedyuk · Jul 12, 2016 go to post

I use the following code:

Class Form.File Extends %Persistent
{

/// Full filepath (path+name)
Property name As %String(MAXLEN = 1000, MINLEN = 1) [ Required ];

/// Description
Property description As %String(MAXLEN = 500);

/// Attachmentt SHA Hash in Base64
Property attachmentHASH As %String;

/// The file itself (data is stored on disk, it's just a link)
Property stream As %FileBinaryStream;

/// Save file
Method %OnNew(name As %String = "", description As %String = "", stream As %Stream.Object = {##class(%FileBinaryStream).%New()}) As %Status [ Private, ServerOnly = 1 ]
{
    #dim sc As %Status = $$$OK
    set ..name = ##class(%File).GetFilename(name)
    set ..description = description
    set ..stream.Filename = name
    set ..attachmentHASH = $system.Encryption.Base64Encode($zcvt($system.Encryption.SHAHashStream(512, ..stream, .sc),"O","UTF8"), 1)  
    return:$$$ISERR(sc) sc
    set sc = ..stream.CopyFromAndSave(stream)
    quit sc
}

/// Serve file in web context
Method serve() As %Status
{
    #dim sc As %Status = $$$OK
    #dim %response As %CSP.Response
    kill %request.Data
    set %request.Data("STREAMOID",1)= ##class(%CSP.StreamServer).Encrypt(..stream.%Oid())
    if ##class(%CSP.StreamServer).OnPreHTTP() {
        set %response.Headers("Content-Disposition")="attachment; filename*=UTF-8''"_##class(%CSP.Page).EscapeURL(..name,"UTF8")
        set st = ##class(%CSP.StreamServer).OnPage()
    }

    quit sc
}

During file upload new Form.File class object gets created and saved, and for download the requested object and calls the serve method.

Note, that this code is not specific to ZEN Mojo, but rather a generic CSP version.

Eduard Lebedyuk · Jul 12, 2016 go to post

Please clarify your question.

DeepSee supports a hierarchical structure of grouping. Where do you want it? In the Architect or Analyzer pivot?

DeepSee cube definitions and pivots in Samples offer many examples.

Eduard Lebedyuk · Jul 12, 2016 go to post

%Label is the way to go.

Its fifth argument is a CSS applied to a cell. So the following MDX:

SELECT NON EMPTY %LABEL([Measures].[%COUNT],,,,"width:100%") ON 1 FROM [HOLEFOODS]

Would be displayed like this in analyzer:

And its sixth argument is a CSS applied to the cell header:

SELECT NON EMPTY %LABEL([Measures].[%COUNT],,,,,"width:80%") ON 1 FROM [HOLEFOODS]

Alternatively you can use DeepSeeWeb to display dashboards with pivots. In there cells width can be easily modified.

Eduard Lebedyuk · Jul 8, 2016 go to post

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
}
Eduard Lebedyuk · Jul 8, 2016 go to post

 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.

Eduard Lebedyuk · Jul 7, 2016 go to post

This problem can be reproduced on all infinite scroll pages. The particular page I reported is just an example.

Eduard Lebedyuk · Jul 7, 2016 go to post

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) 
Eduard Lebedyuk · Jul 6, 2016 go to post

"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?

Eduard Lebedyuk · Jul 1, 2016 go to post

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.

Eduard Lebedyuk · Jun 29, 2016 go to post

I mean can you post it (or any other base64 string giving you an error) here?

I seem unable to reproduce the error.

Eduard Lebedyuk · Jun 29, 2016 go to post

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")
Eduard Lebedyuk · Jun 28, 2016 go to post

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.

Eduard Lebedyuk · Jun 28, 2016 go to post

Thanks.

In regards to the documentation, I'd like to clarify that it refers to a database page accessible at

SMP → System Operation → Databases, and not SMP → Menu → Configure Databases.

Eduard Lebedyuk · Jun 24, 2016 go to post

Triggers are, unfortunately, not an option as MySQL table gets updated via backup (so it's DROP TABLE -> CREATE TABLE -> INSERT)

Eduard Lebedyuk · Jun 22, 2016 go to post

OnStart gets executed before queries, see Ens.Director class, StartProduction method.

Or you can execute EnableConfigItem on an item of a disabled production.

Eduard Lebedyuk · Jun 21, 2016 go to post

You can close the cursor and open it again:

ClassMethod Test()
{
     &sql(  DECLARE C1 CURSOR FOR
            SELECT TOP 10 ID
            INTO :id
            FROM Sample.Person
    )

    &sql(OPEN C1)
    &sql(FETCH C1)
    
    Set first = $$$YES

    While (SQLCODE = 0) {
        Write id,!       
        &sql(FETCH C1)
        If id=9 && first {
            Set first = $$$NO
                &sql(CLOSE C1)
                &sql(OPEN C1)
                &sql(FETCH C1)
        }
    }

    &sql(CLOSE C1)
}