Eduard Lebedyuk · Jun 21, 2016 go to post

If you are familiar (proficent) with ZEN use ZEN Mojo.

If not, write HTML/JS/CSS client and REST server.

That said, DeepSee would definitely be an easier solution to implement and more robust overall.

Eduard Lebedyuk · Jun 21, 2016 go to post

Zen Mojo is a set of classes that enable you to create web pages, suitable for either mobile devices or desktops.

ZEN Mojo is a new and improved ZEN.

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 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 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 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 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 · 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 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 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 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 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

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

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 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 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 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 18, 2016 go to post

Here's working example:

Class Sample.XSLTransform [ Abstract ]
{

ClassMethod test(tData = "<HHSOS><DIAGNOSES><DIAGNOSIS_DATA><DIAGNOSIS_DATA_GUID>3762875</DIAGNOSIS_DATA_GUID><DIAGNOSIS_DATA_GUID>37628752</DIAGNOSIS_DATA_GUID></DIAGNOSIS_DATA><DIAGNOSIS_DATA></DIAGNOSIS_DATA><DIAGNOSIS_DATA></DIAGNOSIS_DATA><DIAGNOSIS_DATA_GUID>37628753</DIAGNOSIS_DATA_GUID></DIAGNOSES></HHSOS>", tSelect = "//DIAGNOSIS_DATA_GUID[1]", tXSL = "ExampleXSL")
{
    set tXML= ##class(%GlobalCharacterStream).%New()
    do tXML.Write(tData)
    set tXSL=##class(%Dictionary.CompiledXData).%OpenId($classname() _ "||" _ tXSL ).Data

    kill tParams
    set tParams("selectParam") = tSelect
    set tSC=##class(%XML.XSLT.Transformer).TransformStream(tXML,tXSL,.tOutput,,.tParams)
    zwrite tSC
    set tSC=tOutput.OutputToDevice()
}

XData ExampleXSL
{
<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="selectParam"/>
<xsl:template match="/">
    <xsl:copy-of select="$selectParam"/>
</xsl:template>
</xsl:stylesheet>
}

}

Example:

Do ##class(Sample.XSLTransform).test()
tSC=1
<?xml version="1.0" encoding="UTF-8"?><DIAGNOSIS_DATA_GUID>3762875</DIAGNOSIS_DATA_GUID><DIAGNOSIS_DATA_GUID>37628753</DIAGNOSIS_DATA_GUID>
Eduard Lebedyuk · Jul 18, 2016 go to post

Why use JSON object instead of a usual signature?

Method %Connect(IP = "127.0.0.1", Port = {^%SYS("SSPort")}, Namespace = "%SYS", Username, Password, ClientIP, ClientPort ) As Sample.RemoteProxy
{
}

Also, it can be moved into the %OnNew method.

Eduard Lebedyuk · Jul 18, 2016 go to post

This is not, generally a good idea to insert potentially long and slow code inside of object constructor.

Why? %OnNew should contain code which is absolutely required on object initiation regardless of the execution speed. There is no use case for this class to construct an object and not call %Connect, so %Connect should be moved into %OnNew. That way client code needs to make one mandatory call instead of two.

Eduard Lebedyuk · Jul 18, 2016 go to post

I don't think SPA works with browser back/forward buttons. You need to code back/forward buttons in your application.

Eduard Lebedyuk · Jul 19, 2016 go to post

That's useful.

set %Stream=##class(%Stream.TmpCharacter).%New() 

Is there any particular reason to use % variable here? I think local variable would be enough.

Eduard Lebedyuk · Jul 25, 2016 go to post

I removed InvalidGet method and object access to the property stopped working.

Class Utils.GlobalProp Extends %Persistent
{

Parameter InvalidGLVN = "^Utils.GlobalPropP";

Property Invalid As %String [ SqlComputeCode = {set {*} = ##class(Utils.GlobalProp).InvalidStatic()}, SqlComputed, Transient ];

ClassMethod InvalidStatic() As %String
{
    Return $Get(@..#InvalidGLVN)
}

Method InvalidSet(val As %String) As %Status
{
    Set @..#InvalidGLVN = val
    Return $$$OK
}

/// Do ##class(Utils.GlobalProp).Test()
ClassMethod Test()
{
    Do ..%KillExtent()
    Set obj = ..%New()
    Write "Invalid old: " _ obj.Invalid,!
    Set obj.Invalid = $Random(100)
    Write "Invalid new: " _ obj.Invalid,!
    Do obj.%Save()
    Kill obj
    &sql(SELECT Invalid INTO :invalid FROM Utils.GlobalProp WHERE Id = 1)
    Write "SQLCODE: " _ SQLCODE,!
    Write "Invalid sql: " _ invalid,!
}

Outputs:

Invalid old:
Invalid new:
SQLCODE: 0
Invalid sql: 65

Related Int code:

zInvalidCompute(%id)
    New %tException,%val set %val = ""
    try {
    set %val = ##class(Utils.GlobalProp).InvalidStatic()
    } catch %tException { throw %tException }
    Quit %val
zInvalidGet() public {
    If i%Invalid = "" { Set ..Invalid=..InvalidCompute($listget(i%"%%OID")) } Quit i%Invalid }
zInvalidSQLCompute()
    // Compute code for field Invalid
 set %d(2) = ##class(Utils.GlobalProp).InvalidStatic()
 QUIT

    Do $System.Status.DisplayError(tStatus)
        Write !
    }
    Quit
}
}