The AppS.REST Framework, described here: https://community.intersystems.com/post/appsrest-new-rest-framework-inte..., provides hooks for authentication (see "AuthenticationStrategy()" and "GetUserResource()" methods in AppS.REST.Handler class). You can use these hooks with the following "LDAP" package to interact with an LDAP database programmatically: https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...

The above solution is great if you’d like to keep the list in each record of one table. Depending on the nature of the data in your application, another option is to “normalize” the data a bit and create a separate table for the “numDossiersMER” values and link them back to the original “Titre” table as follows.

Convert the planned updated table from this:

User.TestList.Data.Titre

numTitre

millesime

codeProduit

numDossiersMER (list)

1

2021

X

1  2  3

2

2021

X

4  5  6

3

2021

X

4  2  3

4

2022

X

2  5  7  8

To the following 2 normalized tables

User.TestList.Data.TitreNew

Id (IRIS)

numTitre

millesime

codeProduit

1

1

2021

X

2

2

2021

X

3

3

2021

X

4

4

2022

X

User.TestList.Data.DossierMER

Id (IRIS)

titreID

numDossierMER

1

1

1

2

1

2

3

1

3

4

2

4

5

2

5

6

2

6

7

3

4

8

3

2

9

3

3

10

4

2

11

4

5

12

4

7

13

4

8

The “id (IRIS)” in each table is the “ROWID” assigned by IRIS as each entry is created in the table.

Using these two tables, the following “JOIN” query will get the results to be formatted as you like:

select numTitre, millesime, codeProduit, numDossierMER from User_TestList_Data.TitreNew t JOIN User_TestList_Data.DossierMER d on d.TitreId = t.id

Please note that this “normalized” solution is great if the “numDossierMER” values can be shared among various “Titre” records as shown in my made-up example above.

Sample code here:

Class User.TitreNew Extends (%Persistent, %Populate)
{ 
Property numTitre As %Integer; 
Property millesime As %Integer; 
Property codeProduit As %String; 
Index titreIdx On (numTitre, millesime, codeProduit) [ PrimaryKey ]; 
}
Class User.DossierMER Extends (%Persistent, %Populate) 
{ 
Property titreID As %Integer; 
Property numDossierMER As %Integer; 
}

(Please excuse the formatting)

Bonjour Michel,

Thank  you for your response. I'm not sure how one would delete query plans. Furthermore, I'm not sure how one would identify the query plans that used a new index.

35 seconds were indeed very important, especially for a REST service because the user needed to wait that long for the browser to respond.

The code that benefited from the new index looks at two tables of similar size, about 160K records. The method needs to look at every record in the first table to find records that match certain criteria. Then for each of those matching records, visit every record in the second table to find records that match other criteria (Order N-squared).

Adding the index to the 2nd table brought the number of checks down to "Order N".

After the change, the wait for a response at the user's browser is negligible.

You're welcome Alex. I had no experience purging cached queries, so I ran a little experiment and confirmed that purging cached queries does NOT unfreeze query plans:

1. I froze all the plans:

do $system.SQL.FreezePlans(1,1)

2. I confirmed query plans were frozen using the Management Portal:

3. I then purged the Cached Queries for the specific table (crm.Company):

do $system.SQL.PurgeForTable("crm.Company")

4. And confirmed that the Cached SQL Queries were gone, again, using Management Portal:

5. Checking the frozen plans again post-purge, we see the query plans are still there, still frozen:

 

Hope this helps.

Hello Kenneth,

Have you figured out how to get the embedded HTML (the "<i>italics</i>") to appear in your second result? I have a feeling that this parser can't return embedded HTML like that because it handles text and tags separately (and only expects to return text).

Please update if you have gotten past this with this or another approach (maybe the approach suggested by John H. below?)

Much appreciated,

Jean

Hi Pravin,

I'm 98% sure that a custom component that uses the %ZEN textarea as a superclass, adds a "maxlength" Property, and overwrites the %DrawHTML() method is the best (only?) way to go. Here's a code smippet that worked (but you likely already have such code :-)).

<codesnippet>

Class ZenApp.Component.TextArea Extends %ZEN.Component.textarea 
{
........

/// Maximum number of characters allowed within the textarea control.
Property maxlength As %ZEN.Datatype.integer(MINVAL = 0);


Method %DrawHTML()
{

.....

&html<<textarea id="#(..%MakeId("control"))#class="#(..controlClass)##(..%Attr("title",..title))# #(..%Name())# #(..%Attr("cols",..cols))# #(..%Attr("maxlength",..maxlength))# #(..%Attr("rows",..rows))# #(disabled)# #(ro)# #(spellcheck)# #(tStyle)# #(..%Attr("tabindex",..tabIndex))# #(..%GetEventHandlers())#>#(tValue)#</textarea>>
}

 

</codesnippet>

 

Hope this helps.

Jean

Hi Alex,

What happens if you call %BuildIndices for the "descriptionIndex" of Utility.contactTypes class after adding those 2 entries in DataSetup? I had an issue with a index "Find" call that went away after %BuildIndices on the index. Of course, I wouldn't expect that we'd need to build indices after each time we create a new object, so what I (and maybe we) found is a bug?

More investigation/diagnosis is needed for what I found; thought I'd bring it up in case it was related to what you found.

Thank for posting,

Jean

The system that I work on used to use InterSystems' Object Synchronization feature quite heavily to keep multiple servers around the world in sync. There was one main server with about 5 "satellite" servers. The InstanceGUID of the main server was used to support Object Synchronization. When we had to clone the main server, in order to maintain proper syncing with the "satellite" servers, we reset the cloned instance with the instance GUID of the original system.

To quote a colleague at ISC:

<quote>

The instance guid is used for more than one purpose but
it is supposed to be unique to the instance. Its probably best
after cloning an instance to set this to a new value (assuming the
cloned instance isn't supposed to replace the old instance). 

</quote>

Since our clone was replacing the old one, we used the steps below to set the instanceGUID of the new instance with the GUID of the old one (and retired the old system).

<another quote>

The original purpose of this document was to list the steps to create an exact copy of the original Hermes machine, including its role as the object-synchronization partner with other servers  around the world. In order to fill this role, some additional steps were needed and these are listed here:

  1. Get the unique “InstanceGUID” of the LEGACY instance of Caché by running the following command on LEGACY. Save the GUID that results from the command; it will be used later:
  • HERMES>w ##class(%SYS.System).InstanceGUID()
  • 34411A13-6CF9-4511-AC7D-49C2CEC6DB5B
  1. Assign this value to the new Hermes instance on NEW by issuing the following commands in the Caché Terminal on NEW:
    • s ns = “^^”_$zu(12)
    • s ^[ns]SYS(“INSTANCEGUID”)=”<Guid from Step 3 above>”
    • w ##class(%SYS.system).InstanceGUID()

</another quote>

Note that this is  something that should be done unless really necessary, but it is possible.