Joel Solon · Jan 6, 2020 go to post

Would you please try adding ReturnResultSets to your original method and let us know? This works for me.

ClassMethod Get(pId As %String) As %String [ SqlName = GetMsg, SqlProc,  ReturnResultSets]
Joel Solon · Feb 24, 2020 go to post

Even though Tim's article talks about this, I'll mention it briefly here. Since ObjectScript Try/Catch construct doesn't have a "Finally" block like some other languages, the code following the Try/Catch is often used for "Finally" code. Since Return inside Try/Catch exits the Try or Catch and terminates the method, this would bypass any "Finally" code at the end. Therefore, I'd recommend avoiding using Return inside Try/Catch.

So if $$$ISERR(tSc), convert tSc into an exception and Throw it, and then handle the error in the Catch. After that, any "Finally" code will run.

Joel Solon · Feb 24, 2020 go to post

We may need more clarity in order to answer this question. This is what I think you mean. You want to take an ObjectScript array of arbitrary structure, like this:

a(1)="a"
a(3,4)="b"
a(3,6)="c"
a(5,6,7)="d"

...and turn it into JSON. But what should the target JSON for this example look like? Something like this?

{"1":"a", "3":{"4":"b","6":"c"}, "5":{"6":{"7":"d"}}}

...or something different?

In any case, to loop through an ObjectScript array of arbitrary structure, you need to use $Query.

Joel Solon · Mar 6, 2020 go to post

To answer the original question: What is the difference?

There are 3 ways to use SQL in Caché/IRIS:

Dynamic SQL: The main use case for this approach is that you, the developer, don't know the complete SQL statement at design/compile time. Instead, inside your Method, you build the complete SQL statement at run time (adding columns to the SELECT, conditions to the WHERE clause, whatever you need), prepare the finished SQL, and execute it. This approach uses %SQL.Statement and %SQL.StatementResult. As you loop through the result set, to access the data in each column, you can use rs.columnname or rs.%Get("columnname"). But neither one will work if you don't actually know the names of all the columns, which can obviously happen when the SQL is dynamic. So the only other approach is to use rs.%GetData(columnnumber).

The other two alternatives are for the case where you, the developer, know the complete SQL statement as design/compile time. Note that you can use Dynamic SQL for this case also.

Embedded SQL: In this approach, you embed the complete SELECT statement inside your Method using &sql(). This is similar to embedding SQL in procedural languages provided by other vendors, such as PL/SQL (Oracle) and T-SQL (MS).

Class queries: In this approach, you create a Query in your class that contains the complete SELECT statement, and you use that query in a Method. For this, it's recommended to use the %PrepareClassQuery() method of %SQL.Statement.

Note: the original implementation of Class queries and Dynamic SQL also used %Library.ResultSet.

Joel Solon · Mar 3, 2020 go to post

%STARTSWITH is not faster or slower when comparing apples to apples.

LIKE can find a substring wherever it occurs, and has multi-character and single-character wildcards. %STARTSWITH is looking only at the beginning of the string, so it's equivalent to LIKE 'ABC%'.

Updating to match another updated post lower on this page. If the comparison string is parameterized, LIKE sometimes does an extra check, so %STARTSWITH will be slightly faster.

When the comparison string ('ABC%' and 'ABC') is fixed. The code that checks LIKE 'ABC%' is exactly the same as the code that checks %STARTSWITH 'ABC'

Joel Solon · Feb 26, 2020 go to post

I have never heard of anyone issuing the blanket statement that InterSystems predicates are faster or slower than the ANSI standard ones. I don't think there are that many predicates that have similar functionality. As I said in a different comment, %STARTWITH 'abc' is 100% equivalent to LIKE 'abc%'. InterSystems also provides %MATCHES and %PATTERN, but they are different.

Joel Solon · Mar 3, 2020 go to post

I did a little bit more research.

  • Maybe %STARTSWITH 'abc' was at one time faster than the equivalent predicate LIKE 'abc%'.
  • The quote comes from the FOR SOME %ELEMENT predicate documentation. This predicate can be used with Collections and an old feature called Free Text Search. The quote was actually only meant to apply to the Free Text Search usage.
  • I've tested %STARTSWITH 'abc' and LIKE 'abc%' today using FOR SOME %ELEMENT with Collections and Free Text Search. The code is identical.

Conclusion: the quote will be removed from the documentation since it's no longer true.

Thanks, @Vitaliy Serdtsev, for making me realize that I should have been testing with placeholders rather than fixed values to the right of %STARTSWITH or LIKE. I was testing with Embedded SQL; with fixed values, my earlier statements are true. But if the query itself uses placeholders (? or host variables), or the WHERE clause is parameterized automatically (thanks, @Eduard Lebedyuk, for mentioning that) then the generated code differs, and LIKE sometimes does do an extra (slightly slower) comparison, because at runtime, LIKE could get a simple pattern ("abc%") or a complex one ("a_b%g_i") and the code has to cope with those possibilities.

New conclusion: the quote will be clarified so that it mentions placeholders/paramaterization and moved to the %STARTSWITH and LIKE documentation, instead of being buried in FOR SOME %ELEMENT.

And thanks to @Hao Ma for bringing this up!

Joel Solon · Feb 27, 2020 go to post

Then besides this, in the documentation for %STARTSWITH need to add the note DEPRECATED and the recommendation "use LIKE 'XXX%'"

select count(*from del.where like 'test7%'
Row count: 1 Performance: 0.291 seconds 333340 global references 2000537 lines executed

select count(*from del.where %startswith 'test7'
Row count: 1 Performance: 0.215 seconds 333340 global references 1889349 lines executed

I'm not sure what you mean here. %STARTSWITH executed fewer lines so why would we recommend LIKE instead?

Joel Solon · Jan 29, 2021 go to post

Interesting, thanks for this. Related question: when running the Portal with this method of enabling ssl, do the Help links to docs.intersystems.com also become https links? For example, the Help links on this page:
System Administration > Configuration > Additional Settings > Startup

Joel Solon · Feb 25, 2021 go to post

That looks like a fun course! winkOne correction on point 20: the default datatype for variables is string, not %String. And there's more to know about what that means, but come and take the course to find out...

Joel Solon · Mar 2, 2021 go to post

On #5: %IsModified() and propertynameIsModified() methods tell you whether the object in memory has been changed in memory (not on disk).

Joel Solon · Mar 2, 2021 go to post

Point #16: There is only one type of database. Databases can hold code only, data only, or code and data together.

Joel Solon · Mar 2, 2021 go to post

Point 5-5: Relationship on the object side is bi-directional. Class A has a reference to class B, and class B has a collection of references to class A. On the SQL side, table A has a reference to table B, along with automatic referential integrity.

Point 16: Important: for a class to be persistent, %Persistent must be the first class. So the example should be: Class Person extends (%Persistent, Animal)

Joel Solon · Mar 2, 2021 go to post

Point #11: one-to-one is not supported but can be imperfectly simulated. many-to-many is supported by combining one-to-many among 3 classes.

Oops! I posted this without noticing that Robert already handled this one!

Joel Solon · Apr 5, 2021 go to post

zen(id) is shorthand for zenPage.getComponentById(id) which as @Vitaliy Serdtsev said gives you access to the Zen component identified by id. document.getElementById(id) gives you access to the element identified by id in HTML/JavaScript document object model. Since a Zen component could comprise several HTML elements, it's usually better to use zen(id).

Joel Solon · Apr 7, 2021 go to post

...and learn about other useful shortcuts such as zenGetProp() and zenSetProp().

Joel Solon · Apr 8, 2021 go to post

For those of you that like GUI tools in an IDE, I just installed the Microsoft Docker extension for VS Code. It shows my Containers and Images just like Docker Desktop Dashboard does, and lets me start/stop/launch CLI/inspect etc.

But the real reason I installed it is because there's also a Registries section with a Connect Registry icon (looks like a plug).

Using that, I chose "Generic Docker Registry" (other choices: Azure, Docker Hub, GitLab). I supplied the registry URL. It prompted me for my username and password (Docker login token as described above) and I can now browse the registry. Beautiful!

Joel Solon · Apr 28, 2021 go to post

I haven’t used any of the Gateways before, nor have I played with any of the External Language Servers. It looks like the new skill demoed here is that an IRIS object (persons in this example) is able to hold a reference to a Java class outside of IRIS, and you can call methods from that Java class as if they are methods inside IRIS, including making use of Java libraries (AsciiTable in this example). So Java System.out redirects to the IRIS Terminal window? If so, that all seems amazing! Do you have some examples you can share about how we expect developers to use this?

Joel Solon · May 17, 2021 go to post

If controlling order is important, and you don't need the bi-directionality of 1-M relationships, you can use a list or an array collection instead.

Joel Solon · May 19, 2021 go to post

Nice article Tim! For posterity, I will try to clarify two statements you made:

  1. "Note that this solution assumes uniform depth of subscripts/values across the whole local array; weird things would happen if this assumption was violated."
  2. "$query, $qlength, and $qsubscript are handy for dealing with globals/local arrays of arbitrary depth."

Some readers might see a conflict in these two statements (I added the bold), but there is no conflict. Statement #2 is correct. Statement #1 applies to using $query for the specific goal Tim had in mind.

Joel Solon · May 26, 2021 go to post

Also note, for the name of the global that is logging statuses:

  • ^mtempinitials works (since the old days), like  ^mtemptl in Tim's example above.
  • ^CacheTempinitials also works (since the Caché days). Maybe Tim would use ^CacheTempTL sometimes  and rock a little camelcase?
  • ^IRIS.Tempinitials also works (for IRIS). Maybe Tim would use ^IRIS.Temp.TL periodically? (see what I did there?)
Joel Solon · Jun 8, 2021 go to post

I have been using VS Code with client-side editing exclusively. Now I'm playing with server-side editing, and I'm hoping that @John Murray or someone else can clear up my confusion about something. Before Server Manager had a GUI, my (possibly incorrect) understanding of server-side editing was that there were two ways to configure it:

  • The preferred way was to use the isfs "technique" as described in the documentation
  • Just click "Choose Server and Namespace" without creating a Workspace (no local folder)

It now appears that clicking "Choose Server and Namespace" automatically uses the isfs technique. Now I'm wondering:

  • Did the behavior of "Choose Server and Namespace" actually change? I'm kind of hoping that it didn't change, but if it did...
  • What's the difference (if any) between the two techniques for server-side editing, and is there any reason to still use the non-isfs approach, and if so, how would I configure that?
Joel Solon · Jun 9, 2021 go to post

The popup help for "links" states "Several ${...} substitution symbols are supported in the value." Is there a list somewhere of what is supported? I tried ${file} but that didn't work.

Joel Solon · Jun 9, 2021 go to post

Thanks Dmitry. I added this, which will launch the current class if it's a Zen page. Not perfect, but fun!

"links":{"Launch Zen page": "http://${host}:${port}/csp/${namespace}/${classname}.cls"

Joel Solon · Jul 23, 2021 go to post

Thanks! One more followup question: Does all of the above now supersede the following setting?

Joel Solon · Oct 12, 2021 go to post

Hi Robert,

Why did you say that the %TimeStamp datatype is the equivalent of the full $h? It's true that %Date/%Time do the conversion from internal date/time values to/from external. But %TimeStamp, %DateTime, and %StringTimeStamp  do not do any internal/external conversion. It's just for the YYYY-MM-DD HH:MM:SS external timestamp. Is there something I'm missing?

%PosixTimeStamp does convert, but it's not $h internally.

Joel Solon · Oct 28, 2021 go to post

All I can say is Wow! I experienced many of the same things over my career, but I was living in safe, protected, training-room-land, not the real world of development like Nigel. Great article!

I'll just note that Nigel says "might" in the title. laugh

One thing that confused me, Nigel. You wrote "If one of the early MUMPS creators had called $order "$next", it would immediately be recognizable as Next()...". But I know that you know that the original $order was called $next.