Ashok Kumar T · Jun 30, 2025 go to post

Hello @Evgeny Shvarov

Unfortunately, when the %JSONFIELDNAME parameter is not explicitly defined, the JSON adaptor treats the property name as a constant, making it fixed and case-sensitive.

However, you can define a custom mapping using an XData block to control how properties are mapped to JSON fields. This provides greater flexibility, and you can even use both %JSONFIELDNAME and XData mappings within the same class.

Example: Custom Mapping with XData

XData OnlyLowercaseTopLevel
{
  <Mappingxmlns="http://www.intersystems.com/jsonmapping"><PropertyName="Name"FieldName="eventName"/><PropertyName="Location"Include="INPUTONLY"/></Mapping>
}
  • Do obj.%JSONImport(dao) Default import (uses standard property names)
  • Do obj.%JSONImport(dao, "OnlyLowercaseTopLevel")Import using a custom mapping
Ashok Kumar T · Jun 29, 2025 go to post

That's my understanding as well. So, Once the response is written and sent, it cannot be tracked afterward—except through the Web Gateway HTTP trace.

Ashok Kumar T · Jun 29, 2025 go to post

Hello @Enrico Parisi 

I'm working on capturing and logging all REST request and response data for application endpoints. Currently, I'm able to extract the incoming request using the %request object and handle it within the OnPreDispatch method. My goal is to serialize this request into raw HTTP format for storage and analysis. So, I'm if any method available to create the HTTP format,If no built-in method exists to generate the raw HTTP request format, I plan to implement one manually.

The challenge arises from the manually created REST services are implemented: each REST call directly maps to a class method invocation <Route Url="/test" Method="POST" Call="User.Sample:Test" Cors="true"/> , As a result, capturing the response data would require modifying multiple class methods to intercept output, which is not feasible.

To avoid this, I'm looking for a way to extract the full HTTP response—ideally from a buffer it's been written and accessible, I specifically want to avoid enabling HTTP tracing on the Web Gateway due to performance and logging concerns.

Thanks!

Ashok Kumar T · Jun 28, 2025 go to post

Hello @David Hockenbroch 
The "type" column is partially useful in identifying some system-generated web applications. However, certain CSP applications such as /api/atelier, /api/docdb, /api/healthshare-rest/hssys, /api/iam, and /api/iknow are also system-generated by default in IRIS. I want to differentiate these system-generated applications from user-defined (/aktest1) web applications.

Ashok Kumar T · Jun 28, 2025 go to post

Hello @David Hockenbroch 

I'm looking for HTTP message format for both request from %request object and response from %response object.

HTTP/1.1 201 Created
Content-Type: application/json
Location: http://example.com/users/123
{
  "message": "New user created"
}
Ashok Kumar T · Jun 25, 2025 go to post

Index ZipIDX On ZipCode [ Data = (City, State) ];

Here, the index is built on the ZipCode field, and the City and State fields are included in the Data portion of the index structure (i.e., stored as part of the index global). When a query retrieves only City and State based on a ZipCode filter, the SQL engine can perform an index-only scan and avoid accessing the main "Master map". This behavior is similar to a covering index in SQL databases because the requested data is fully available within the index itself.

SELECT City, State FROM Sample.Person WHERE ZipCode = 19904;

In this case, the index global might look like:

^gblI("ZipIDX", ZipCode, ID) = $LB("", City, State)

The query will be satisfied using just the index, avoiding a full table (master map) access.

However, this index does not provide any benefit when the query uses City or State as filter conditions, since those fields are not part of the index key. So, It will search on the "Master map"

Now, consider another index:

Index CityStateIDX On (City, State);

This is a composite index (or multi-column index), where both City and State are stored as part of the index key (subscripts), not as index data. The global would look like:

^gblI("CityStateIDX", City, State, ID) = "

SELECT state FROM SAMPLE.PERSON1 WHERE city ='Newton'

When a query uses City or State in the WHERE clause, the index is used to locate the matching IDs, but the SQL engine must then perform a lookup in the master map to fetch any other columns, even if you're selecting only indexed fields like City or State.

So, composite indexes help filter rows efficiently, but they don't function like covering indexes unless the query involves only index keys and you don't need to retrieve other columns.

Here is the Index types documentation

Ashok Kumar T · Jun 11, 2025 go to post

Thanks for the quick response on the issue — improving the clarity of the error message will definitely help.

Ashok Kumar T · Jun 5, 2025 go to post

Here is the simplified version of the LIKE operator with SQL Procedure

Stored procedure

/// add multiple parameters depends on needs
Query NAMEINLIKE(p1 As%String = "", p2 As%String = "") As%Library.SQLQuery [ SqlProc ]
{
    SELECT  Name,Age FROM Sample.Person 
    WHERE Name like :p1 or Name like :p2
}

 SQL query

select * FROM SAMPLE.PERSON_NAMEINLIKE('%Eisenstien%','Xenia%')
ClassMethod INLIKE(pSQLColumnValue, pSearchValues...) As%Boolean [ SqlProc ]
{
    Set rtn=0For i=1:1:$O(pSearchValues(""),-1) {
        Set data = pSearchValues(i)
        If$E(data,1)="%"&&($E(data,*)="%") {
            Set data = $TR(data,"%")
            If pSQLColumnValue[data s rtn=1
        }
        ElseIf$E(data)="%" {
            Set data = $TR(data,"%")
            Set pat = ".ANPC1"""_data_""".ANPC"If pSQLColumnValue?@pat Set rtn = 1
        }
        ElseIf$E(data,*)="%" {
            Set data = $TR(data,"%")
            Set pat = "1"""_data_""".ANPC"If pSQLColumnValue?@pat Set rtn = 1
        } 
    }
    quit rtn
}

SQL Query

SELECT * FROM SAMPLE.PERSON
WHERE1=SAMPLE.PERSON_INLIKE(FirstName,'%vid','Zelda%') 
Ashok Kumar T · Jun 5, 2025 go to post

Hello @Evgeny Shvarov 

We can define the global name using either a compile-time class parameter or a runtime expression (COSEXPRESSION):

  • Compile-time:
    Parameter GlobalName = {$NA(^AGlobal)};
  • Runtime (COSEXPRESSION):
    Parameter GlobalName As COSEXPRESSION = "$NA(^AGlobal)";

Instead of assigning just the global name to the parameter and then later generating the full reference using $NAME, you can directly assign the full $NA(^AGlobal) expression to the parameter.

This eliminates the need to do something like:
set gn = $name(..#GlobalName)

Parameter GlobalName As COSEXPRESSION = "$NA(^AGlobal)";Parameter GlobalName1 = {$NA(^AGlobal)};ClassMethod SetGbl()
{
	Set @..#GlobalName1("test")=112zw @..#GlobalName
}
Ashok Kumar T · Jun 3, 2025 go to post

Yeah, that's true. the date should be in MM/DD/YYYY format. I updated the code.

Ashok Kumar T · Jun 3, 2025 go to post

You can delete the application error logs for all days by executing the below code for specific namespace 

ClassMethod DeleteAppErrorLog(Namespace As%String = {$Namespace}) As%Status
{
    New$NamespaceSet$Namespace = "%SYS"Return##class(SYS.ApplicationError).DeleteByNamespace(Namespace)
}

Delete by date

ClassMethod DeleteAppErrorLogByDT(pNamespace As%String = {$Namespace},pDate ={$ZD(+$H)}) As%Status
{
    New$NamespaceSet$Namespace = "%SYS"Return##class(SYS.ApplicationError).DeleteByDate(pNamespace,pDate)
}
Ashok Kumar T · May 28, 2025 go to post

As always there is a possibility to get <INVALID OREF> while direct access of objects. So, we can use responseData.items.%Get(0).titles.%Get(0).value.%Get("en_US") with some additional checks like below.

If$IsObject(responseData.items) && (responseData.items.%Size()) {
    dao1 =responseData.items.%Get(0) 
    If$IsObject(dao1.titles) {
        dao1.titles.%Get(0).value.%Get("en_US")
    }
}
Ashok Kumar T · May 22, 2025 go to post

The code is simple and no hidden implementation. always undefined is set as 2 in our environment so it prevents the <UNDEFINED>.

I keep the if statement in both single line as well as block structure

s mr=15824,vs="EV1"sx=$S(^pmr(1):$NA(^pmr),1:"")
 if$d(@x@(mr,vs)) {
    s^zxq($now())=@x@(mr,vs)
 }
 q
Ashok Kumar T · May 22, 2025 go to post

I set $T to 0 before the if statement. After the syntax error occurred, I checked the value of $T. It seems that the interpreter doesn’t differentiate between a block of code and a single line — it still executes the subsequent lines inside the condition

Ashok Kumar T · May 22, 2025 go to post

I modified the post slightly. I ran a routine via the terminal, and as expected, it threw a <SYNTAX> error because the variable x is undefined. Then I continued the program, and now both the single-line if statement and the block if statement (with braces) set all the global variable. I thought that block of code wouldn’t be executed

if$D(@x@(a,b,c)) s^zz1=1,^x=1,^y=2,^xx=1;;if$D(@x@(a,b,c)) {
     set^zz1=1212set^dd=1set^fg=2
 }
Ashok Kumar T · May 13, 2025 go to post

Maybe can you try to wrap the HL7 message like below format and set the ContentType As "application/xml"  in additional setting in the Business operation "EnsLib.HL7.Operation.HTTPOperation" and check for the response headers as well if required.

<?xml version="1.0" encoding="UTF-8"?><HL7Message>
<![CDATA[
MSH|^~\&|... your message ...
PID|... etc ...
]]>
</HL7Message>
Ashok Kumar T · May 13, 2025 go to post

Hello @Jonathan Perry

The rules for routine name /Class name is "If you start a routine name with %, use z or Z as the next character after that" So, You have to create routine in the name of %zTest/%ZTest

Ashok Kumar T · May 12, 2025 go to post

Thank you for the quick resolution and the update. Glad to hear it's been addressed!

Ashok Kumar T · May 9, 2025 go to post

We can use the %IsDefined method  to check the key is defined, and %Size() to determine the number of the elements in an Object or Array. These methods help to prevent from the <INVALID OREF> and <UNDEFINED>

// verify the "items" is present and it has valuesIf responseData.%IsDefined("items")&&(responseData.items.%Size()) {
    Set item1 = responseData.items.%Get(0)
    If$IsObject(item1) {
        Write item1.portalUrl
    }
    /*another*/If$IsObject(item1)&&(item1.%IsDefined("portalUrl")) {
        Write item1.portalUrl
    }
}

The %GetTypeOf method is used to determine the type of a key, and it returns 'unassigned' if the key does not exist

If responseData.%GetTypeOf("items")="array" {
    Set item1 = responseData.items.%Get(0)
    If$IsObject(item1) {
        Write item1.portalUrl
    }
}
Ashok Kumar T · May 9, 2025 go to post

Thank you for sharing this—it’s truly insightful. I learned a lot, and it was very useful.

Ashok Kumar T · May 8, 2025 go to post

I agree that both Streams extend from %Stream.Object, and yes, I noticed that the storage mechanisms are also different. I'm curious—what are the advantages of using DynamicBinary instead of TmpCharacter?

Ashok Kumar T · May 7, 2025 go to post

We can use the %SYS.LockQuery class and its Listquery function to check whether the global is already locked. If it is, we can skip attempting to acquire the lock.

Check for the specific process

ClassMethodLOCK(Lock, Mode)
{
    If '..IsLocked("^A","X") {
        Lock +^A
    }
    Else {
        Write"Locked"
    }
}
// X - Exclusive// S - SharedClassMethod IsLocked(Global As%String, Mode As%String)
{
    #dim status = 0Set tResult = ##class(%SYS.LockQuery).ListFunc($J)
    While tResult.%Next() {
        If tResult.Mode=Mode&&(tResult.LockString=Global) Set status= 1
    }
    Return status
}

However, the above code only checks for a specific process and does not account for other processes with Xclusive or Shared locks. The sample below checks for all acquired locks, returning their status and lock type.

ClassMethod IsLocked(Global As%String, Mode As%String)
{
	#dim status = 0Set tResult = ##class(%SYS.LockQuery).ListFunc()
	While tResult.%Next() {
		If tResult.LockString=Global {
			If tResult.Mode?1(1"X",1"S") Set status= 1_","_tResult.Mode
		}
	}
	Return status #; status be like "1,S" or "1,X"
}
Ashok Kumar T · May 7, 2025 go to post

The *C - Control character removes works from ASCII (0-31, 127-159). However, the unicodes ascii's are greater than CC.