go to post Joel Solon · Apr 9, 2024 $increment is (or used to be) unique in that it was the only function that changed its argument. It reminded me of learning about "destructive functions" in LISP a long time ago. No matter where/how you use it, it increments. So "set a = $increment(a)" is redundant, but still OK to use. As you saw, developers started to use "if $increment(a)" alone, because that was shorter but also works fine, even thought it looks strange. I wasn't aware of "do $increment(a)" being allowed, which does look nicer, so I also learned something new from this thread, thanks. And let's not forget to mention the popular and useful "set a($increment(a)) = something"
go to post Joel Solon · Aug 24, 2023 Does this example make it clear? Does this meet your needs? USER>set human = ##class(Simple.Human).%OpenId(1) USER>zw human human=20@Simple.Human ; <OREF> +----------------- general information --------------- | oref value: 20 | class name: Simple.Human | %%OID: $lb("1","Simple.Human") | reference count: 2 +----------------- attribute values ------------------ | %Concurrency = 1 <Set> | Company = "GlobaDyne Inc." | Name = "Smith,John" | Phone = "265-288-5681" | Version = 2 +----------------- swizzled references --------------- | i%Home = $lb("6489 Clinton Street","Denver","NJ",26882) <Set> | r%Home = "" <Set> | i%Work = $lb("9353 Main Drive","Hialeah","MI",72997) <Set> | r%Work = "" <Set> +----------------------------------------------------- USER>write human.%IsModified() 0 USER>write human.PhoneIsModified() 0 USER>set human.Phone = "111-222-3333" USER>write human.%IsModified() 1 USER>write human.PhoneIsModified() 1 USER>write human.Home.Street 6489 Clinton Street USER>write human.Home.StreetIsModified() 0 USER>set human.Home.Street = "111 High Street" USER>write human.Home.StreetIsModified() 1 USER>
go to post Joel Solon · Jul 31, 2023 %Library.ResultSet (aka %ResultSet) is deprecated. You should switch to using %SQL.Statement instead. It's better in every way. Doc is here: Dynamic SQL and within that there's a section on Metadata.
go to post Joel Solon · Dec 8, 2022 It's not a big difference between Studio and VS Code - ObjectScript here. Studio automatically does the #dim for you when you do a %New() or %OpenId(), and VS Code - ObjectScript doesn't. But #dim is still necessary in both IDEs for referenced or returned objects: set person = ##class(Simple.Person).%New() // Studio WILL provide code completion for person, VS Code WON'T #dim address as Simple.Address // without #dim, neither Studio nor VS Code will provide code completion for address set address = person.Address #dim rs as %SQL.StatementResult set rs = statement.%Execute(args) // you'll get code completion for rs thanks to #dim
go to post Joel Solon · Nov 1, 2022 Thanks Michael. You could code it the oop way, and use $sortbegin on all the index globals for your multiple tables, which means you'd have to know or look up what the index global names are (since they're not always ^D and ^I anymore), and test 1000000 inserts (main table and referenced tables) with the index build deferred to the end, and time it to see how long it takes. And then code it the sql way, inserting the same data into the multiple tables using %NOINDEX, and calling %BuildIndices() on all the classes at the end, and time it to see how long that takes. The sql way is supposed to be faster...
go to post Joel Solon · Aug 15, 2022 Would <foreach> help here? https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls...
go to post Joel Solon · Mar 17, 2022 ...and it appears that Mr C is also trying to sneak in a new abbreviation for ObjectScript: ISOS.
go to post Joel Solon · Apr 7, 2021 ...and learn about other useful shortcuts such as zenGetProp() and zenSetProp().
go to post Joel Solon · Apr 5, 2021 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).
go to post Joel Solon · Mar 6, 2020 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.
go to post Joel Solon · Mar 3, 2020 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!
go to post Joel Solon · Feb 24, 2020 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.
go to post Joel Solon · Jan 8, 2020 Not sure exactly what you're asking for... You can add additional properties to your custom unit test class (inheriting from %UnitTest.TestCase) and use them to share data between the methods in the class. But this doesn't show up automatically in the results. You can use the $$$LogMessage macro to display whatever you want in the results, but this is just text, not a new property. do $$$LogMessage(key _ ":" _ value)
go to post Joel Solon · Oct 3, 2019 If you want to allow subclasses of class A with a property that references Abstract %Persistent Class B, and allow A objects to use that property to reference any of the subclasses of B, class B must have its own extent (all subclass data stored in the same global). Otherwise, since the A objects only store the ID of the referenced B objects, how would the system determine which subclass of B ID #52 refers to? When all the subclass data is stored in the same global, IDs are unique across all subclasses, and ID #52 refers to one object only. Adding a BitMap extent index to class B helps compensate for the fact that all B objects are stored together, and improves the performance of queries on the subclasses.
go to post Joel Solon · Oct 3, 2019 OK. In your original post, you wrote "storing data in separate globals for each class" which made me ask my question. You should probably edit that to be "storing data in separate globals for each subclass". And, yes, that is the correct way to achieve that.
go to post Joel Solon · Jul 29, 2019 As Eduard said, use a custom resource. Now it depends on what you want to do:If you want to restrict certain Portal pages to a subset of users, create a custom resource (R1 for this example), add it to the pages you want the subset of users to access, and then add Use permission on the R1 resource to an existing or new role. Only users that are members of that role will have access to the pages.If you want to restrict certain users to a subset of Portal pages (your question), create a custom resource (R2 for this example), add it to all the pages the users should not access, and then add Use permission on the R2 resource to an existing or new role. All other users should be in this role, and they will have access to all the pages, as they did before these changes. Any pages that aren't protected by R2 will be the only pages available to the users that aren't in the role. Hope that's clear.
go to post Joel Solon · Jul 29, 2019 James, for this one, there is no easy answer. But first, here are some corrections FYI:Since what you're passing in to execute contains quotes ("SYSTEM"), surround that with single quotes instead of double quotesSince the Security.System class is in %SYS, specify the namespace in your command.So it's this:csession TEST -U %SYS '##class(Security.System).AutheEnabledGetStored("SYSTEM")'But the problem is that this expression doesn't output its value, and you can't include "write" at the beginning. You should probably describe exactly what you're trying to do in more detail, and someone here will make a suggestion.
go to post Joel Solon · Jul 29, 2019 Security.System is a persistent class with 1 object in it. The ID of the object is "SYSTEM". So this works set ss = ##class(Security.System).%OpenId("SYSTEM") write ss.AutheEnabled The approach above is best if you want to access several properties. But if all you really want is one or two properties of Security.System, you can do this: write ##class(Security.System).AutheEnabledGetStored("SYSTEM")
go to post Joel Solon · Mar 5, 2019 Brendan's comment from the other thread summarizes it nicely. Here's a little more...you have 2 good alternatives to parent-children:Without using either relationship option, just use a simple reference from the "many/child" class to the "one/parent" class, and add the FK constraint yourself (using "cascade"). This allows bitmap indexes in either class.Simulate parent-children relationship using one-many. After setting up the one-many relationship, in the Relationship definition in the "many" class, add the bold items shown here: [Cardinality = one, Inverse = Orders, Required, OnDelete = cascade]. This allows bitmap indexes in either class.