Eduard Lebedyuk · Jun 2, 2016 go to post

You can use %SerialObject for that:

Class Utils.Serial Extends %SerialObject
{

Property Payload As %String;

/// zw ##class(Utils.Serial).Test()
ClassMethod Test(input As %String = {$lb(1,2,3,",",5)}) As %String
{
    
    set obj = ##class(Utils.Serial).%New()
    set obj.Payload = input
    do obj.%SerializeObject(.str)
    kill (str)
    
    set obj = ##class(Utils.Serial).%Open(str)
    return obj.Payload
}
}

For example:

zw ##class(Utils.Serial).Test("1,2,3,,,5")
>"1,2,3,,,5"

zw ##class(Utils.Serial).Test($lb(1,2,3,",",5))
>$lb(1,2,3,",",5)
Eduard Lebedyuk · Jun 1, 2016 go to post

Q1:

1. JS client sends URL encoded string automatically or almost automatically (most frameworks do URL encoding for any URL)

2. In your broker override OnPreDispatch method and unescape pURL with:

set pURL = $ZCONVERT($ZCONVERT(pURL,"I","URL") ,"I","UTF8")

Q2: It is against core REST architecture principles, so I hope not.

Eduard Lebedyuk · May 31, 2016 go to post

Here it is:

/// Returns MDX string used to create pivot.<br>
/// <b>pPivotName</b> - fullname of pivot. Eg: "KPIs & Plugins/HoleFoods.pivot". Case insensitive.<br>
/// <b>pStatus</b> - Status of query execution.<br>
/// <b>MDX</b> - MDX query with filters<br> /// <b>BaseMDX</b> - MDX query without filters<br>
ClassMethod GetMdx(pPivotName As %String, Output MDX, Output BaseMDX) As %Status
{
    #dim tPivot As %DeepSee.Dashboard.Pivot
    #dim tPivotTable As %DeepSee.Component.pivotTable
    set MDX = ""
    set BaseMDX = ""

    set tPivot = ##class(%DeepSee.UserLibrary.Utils).%OpenFolderItem(pPivotName,.pStatus)
    return:'$IsObject(tPivot) $$$OK
    return:$$$ISERR(pStatus) pStatus

    set tPivotTable = ##class(%DeepSee.Component.pivotTable).%New()
    set pStatus = tPivot.%CopyToComponent(tPivotTable)
    return:$$$ISERR(pStatus) pStatus

     // returns tQueryText - mdx without filters
    set rs = tPivotTable.%CreateResultSet(.pStatus, .tParms, .tFilterInfo, .tAdvancedFilters, .BaseMDX)
    
    return:$$$ISERR(pStatus) pStatus

    set pStatus = tPivotTable.%GetFilterInfo(.tFilterInfo, .tAdvancedFilters)
    //return:$$$ISERR(pStatus) pStatus

    if (($d(tFilterInfo)=0) &&($d(tAdvancedFilters)=0)) {
        set MDX = BaseMDX // no filters, so we're good
    } else {
        // returns tQueryText - mdx with filters
        set rs = tPivotTable.%CreateResultSet(.pStatus, .tParms, .tFilterInfo, .tAdvancedFilters, .MDX)
        return:$$$ISERR(pStatus) pStatus
    }

    // Remove \n
    set MDX = $TR(MDX, $C(10), "")
    set BaseMDX = $TR(BaseMDX, $C(10), "")

    return pStatus
}

It is a part of MDX2JSON REST API.

Eduard Lebedyuk · May 25, 2016 go to post

If you're okay with /api/deepsee as it is - use it.

If, however, you want to change some settings (auth methods for one), then it's better to define your own application, since your changes may be lost during system update.

Eduard Lebedyuk · May 24, 2016 go to post

Save timestamp with user timezone (get timezone from request), for example (returns server timezone):

write $ZDATETIME($HOROLOG, 1, 5)

Return timestamp to a client, there are a lot of js libraries which automatically convert incoming timestamp with a different timezone into user's local time.

The advantage of this approach is that you only need to convert timezone once, the rest would be done on client.

Check out ISO 8601  for timestamp formats with timezone support and which js understands.

This approach works best, when you don't need to access your application data from the systems without support for this type of timestamp.

Eduard Lebedyuk · May 23, 2016 go to post
 s array = []
 While (result.Next()) {
     s object = {}
     s object.data = {}

     s object.data.id = result.Data("ID")
     s object.data.reg = result.Data("Registration")
     s object.data.snNum = result.Data("SatNavVehNumber")
     do array.$push(object)
     }
  w !!,array.$toJSON()

This should produce the desired output.

Eduard Lebedyuk · May 19, 2016 go to post

1) I tried all three with the same result.

2) I linked the procedure, and it does not work when I select data from a linked table. When I check the int code for a query, there is no call to my procedure or linked procedure to be found.

Eduard Lebedyuk · May 13, 2016 go to post

Some comments on code.

  • rename fileToImport to mysqldumpFile
  • remove 2 references to other classes

 

That said, it does not work on my sql script on the table creation part, despite throwing no errors (data insert throws an error -  Table not found, but previous  statement which creates the table seems to run okay, but the table does not get created)

 

UPD: Reread your post, only INSERT, ok.

Eduard Lebedyuk · May 12, 2016 go to post

Tried it again, got the same results as you. $find performs better on both short (20 chars per piece, 100 pieces) and long strings (2000 chars per piece, 100 pieces).

Eduard Lebedyuk · May 12, 2016 go to post

Graphs (and measurements in a method) include conversion times.

>$find

I tried this:

$$$START("Using $find (hardcoded delimiter): ")
For i=1:1:pIterations {
    Set tPosition = 0
    Do {
        Set tNewPosition = $Find(tString,",",tPosition)
        Set tValue = $Select(tNewPosition'=0:$Extract(tString,tPosition,tNewPosition-2),1:$Extract(tString,tPosition,*))
        Set tPosition = tNewPosition
    } While tPosition '= 0
}
$$$END

It was slow. Is there any way to write this faster - eg. without select (or other condition for the last piece)?

Eduard Lebedyuk · May 12, 2016 go to post

Also

Set a = $List(list, *) 

is slower than

Set a = $List(list, 3) 

for 3-element list,  on the other hand:

Set a = $List(list) 

is faster than

 Set a = $List(list, 1)  
Eduard Lebedyuk · May 12, 2016 go to post

> Pros/Cons for multiple simple deployments vs single more complex deployment)

If you have a virtualization platform and clients are heavy users, it would probably make a lot of sense to go with multiple simple deployments, and if you'll have many clients who can't load a single smallest node then "single complex"  would be better.

Developing "lots of simple" system (so 1 simple system with multiple deploys) would be cheaper (as you don't have tyo think about domains and security would be simpler, maybe some other simplifications would be possible), but hosting would (maybe) be more expensive as there is a lot of overhead in resources (to run OS/Cache for each deploy).

Eduard Lebedyuk · May 10, 2016 go to post

As I understand, you need Caché users + some way to:

  • Store additional information
  • Provide methods for web-login-account management
  • Provide additional security checks

I guess domains could be used, okay. Also, here's somewhat related thread.

>And if so, when deploying updates to the applications, would each domain/user-pool context be replicated environments

> that would each need to be updated?

Why? You have production server with real users and a test one(s) with some test users.

Eduard Lebedyuk · May 9, 2016 go to post

What's the advantage of using $$$ERRORCODE macro? ^%qCacheObjectErrors global contains the same values.

Eduard Lebedyuk · May 8, 2016 go to post

See %ZEN.proxyObject class, SaveDocument / OpenDocument methods for sample global storage of dynamic objects. You can convert JSON to %ZEN.proxyObject to Global (and back) with it.

There is also MonCache project -  implementation of basic MongoDB functions using Caché as a database engine. It implements json-global engine.

Eduard Lebedyuk · Apr 28, 2016 go to post

Use $Piece function to return a substring identified by a delimiter. :

SELECT $PIECE('96842-2-AEV.00001-63561-001958-100728760I-42', '-', 2)

Yields 2.

Eduard Lebedyuk · Apr 28, 2016 go to post

Note, that $list and, for example, %ListOfDataTypes are different concepts.  The first one is a datatype and a second one is an object.

Eduard Lebedyuk · Apr 25, 2016 go to post

One approach would be to use %XML.DataSet to convert SQL results into XML:

Set result=##class(%XML.DataSet).%New()                                  
Do result.Prepare("SELECT TOP 3 ID, Name FROM Sample.Person")
Do result.Execute()                                                      1
Do result.WriteXML("root",,,,,1) 

Outputs:

<root>
<s:schema id="DefaultDataSet" xmlns="" attributeFormDefault="qualified" elementFormDefault="qualified" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <s:element name="DefaultDataSet" msdata:IsDataSet="true">
    <s:complexType>
      <s:choice maxOccurs="unbounded">
        <s:element name="SQL">
          <s:complexType>
            <s:sequence>
              <s:element name="ID" type="s:long" minOccurs="0" />
              <s:element name="Name" type="s:string" minOccurs="0" />
            </s:sequence>
          </s:complexType>
        </s:element>
      </s:choice>
    </s:complexType>
    <s:unique name="Constraint1" msdata:PrimaryKey="true">
      <s:selector xpath=".//SQL" />
      <s:field xpath="ID" />
    </s:unique>
  </s:element>
</s:schema>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<DefaultDataSet xmlns="">
  <SQL diffgr:id="SQL1" msdata:rowOrder="0">
    <ID>96</ID>
    <Name>Adam,Wolfgang F.</Name>
  </SQL>
  <SQL diffgr:id="SQL2" msdata:rowOrder="1">
    <ID>188</ID>
    <Name>Adams,Phil H.</Name>
  </SQL>
  <SQL diffgr:id="SQL3" msdata:rowOrder="2">
    <ID>84</ID>
    <Name>Ahmed,Edward V.</Name>
  </SQL>
</DefaultDataSet>
</diffgr:diffgram>
</root>

There is also %SQL.Export.Mgr class, which does SQL export.

Eduard Lebedyuk · Apr 21, 2016 go to post

Thanks!

I often need to run queries from a terminal, so I extended Caché ObjectScript with zsql command to run queries and display the results. Here's how it works:

 zsql "SELECT TOP 2 Name FROM Sample.Person"

Would output:

Name
Adams,Chris Z.
Adams,Danielle P

To achieve it I created %ZLANGC00 mac routine with the following code:

 ; %ZLANGC00
 ; custom commands for ObjectScript
 ; http://docs.intersystems.com/cache20141/csp/docbook/DocBook.UI.Page.cls?KEY=GSTU_customize
  Quit  

/// Execute Query and display the results
/// Call like this:
/// zsql "SELECT TOP 10 Name FROM Sample.Person"
ZSQL(Query)
  #Dim ResultSet As %SQL.StatementResult
  Set ResultSet = ##class(%SQL.Statement).%ExecDirect(, Query)
  Do ResultSet.%Display()
  Quit

Save and compile it and then you can execute sql (class queries too) in a terminal with zsql command:

zsql "SELECT * FROM Sample.SP_Sample_By_Name('Z')"

That said I myself prefer executing SQL queries in SMP because you don't need to type them there (drag&drop from the left panel or copy&paste from the code) - it's very convenient.

Eduard Lebedyuk · Apr 21, 2016 go to post

Since project is a collection of classes (and other items, yes, but we're talking about classes here) the  flow would be something like that:

  • Get project name
  • Project name to class list
  • Get unit tests for class list
  • Run unit tests

I'm just saying that 3rd step can have more callers (alternative routes to 3rd step if you will), for example:

  • Package to class list
  • Wildcard expression to class list
  • Namespace to class list
Eduard Lebedyuk · Apr 21, 2016 go to post

I usually add my classes and system classes I often reference from my classes into the project, so they can be opened faster (without dialog).

Eduard Lebedyuk · Apr 21, 2016 go to post

I would recommend you forego projects entirely and iterate over classes directly with %Dictionary package.