Thank you for the clarification!
- Log in to post comments
Thank you for the clarification!
Hello @PhoebeK
If emailAttachmentList is a DynamicArray, then the following property declaration is correct:
Attempt 2 //
Property emailAttachmentList As List of Request.EmailAttachment(%JSONFIELDNAME = "emailAttachmentList");It looks like you're trying to set the value directly like:
callrequest.emailAttachmentList.attachmentClass = "application/xml"
However, since emailAttachmentList is a list, you need to either:
- Use %JSONImport(response) to populate the list properly (e.g., `callrequest.%JSONImport(response)`), or
- Loop through the list and set the property on each individual item, like this:
For i=1:1:callrequest.emailAttachmentList.Count() {
Set callrequest.emailAttachmentList.GetAt(i).attachmentClass = "application/xml"
}The name property is Property name As HS.FHIRModel.R4.SeqOfHumanName; and the SeqOfHumanName class have Property list As %Library.DynamicArray;
This list property holds the object of HS.FHIRModel.R4.HumanName
Hello @Scott Roth
I hope the target is a object reference of HS.FHIRModel.R4.Patientand the "name" property in in R4.Patient is an object property(HS.FHIRModel.R4.SeqOfHumanName ) and thisSeqOfHumanName stores the %DynamicArray of HS.FHIRModel.R4.HumanName object values into property called "list"
So, First we have to check the name is $IsObject(r4Patient.name)&&(r4Patient.name.%IsA("HS.FHIRModel.R4.SeqOfHumanName")) and if it's true you can iterate the list (%DynamicArray) Set iter = r4Patient.name.list.%GetIterator()
You can directly copy and run this example directly and it works for me.
ClassMethod ParseHumanName()
{
Set r4Patient = ##class(HS.FHIRModel.R4.Patient).%New()
;
Set seqHuman = ##class(HS.FHIRModel.R4.SeqOfHumanName).%New()
; create human name for seqHuman
Set human = ##class(HS.FHIRModel.R4.HumanName).%New()
Set human.family="pil"
Set human.text="Ashok,kumar"
;
;push the HumanName into SeqOfHumanName
Do seqHuman.add(human)
; Set SeqOfHumanName into Patient.name
Set r4Patient.name = seqHuman
If $IsObject(r4Patient.name)&&(r4Patient.name.%IsA("HS.FHIRModel.R4.SeqOfHumanName")) {
Set iter = r4Patient.name.list.%GetIterator()
#;
while iter.%GetNext(,.humanName) {
zw humanName.toDao()
}
}
quit
}I have full access and permissions for the directory and %All is my role. I'm able to connect to the %SYS and USER namespaces, but I'm unable to access the namespaces mapped the HS package( c:\intersystems\irishealthcomm\mgr\hscustom\) hscustom db.
The HS.FHIRModel.R4.Patient class in IRIS directly represents the FHIR R4 Patient resource as defined. When a FHIR Bundle is loaded using HS.FHIRModel.R4.Bundle.fromDao(), each resource in the bundle's entry.resource is automatically mapped to the correct HS.FHIRModel.R4.* subclass (e.g., Patient, Encounter, AllergyIntolerance) based on its resourceType. There’s no need for manual casting — the object model handles the typing internally.
here is the sample code
Set bundle={"resourceType":"Bundle","type":"searchset","total":3,"entry":[{"fullUrl":"https://example.org/fhir/Patient/patient-1","resource":{"resourceType":"Patient","id":"patient-1","name":[{"family":"Doe","given":["John"]}],"gender":"male","birthDate":"1985-02-15"},"search":{"mode":"match"}},{"fullUrl":"https://example.org/fhir/Encounter/encounter-1","resource":{"resourceType":"Encounter","id":"encounter-1","status":"finished","class":{"system":"http://terminology.hl7.org/CodeSystem/v3-ActCode","code":"AMB","display":"ambulatory"},"subject":{"reference":"Patient/patient-1"},"period":{"start":"2023-07-01T10:00:00Z","end":"2023-07-01T10:30:00Z"}},"search":{"mode":"include"}},{"fullUrl":"https://example.org/fhir/AllergyIntolerance/allergy-1","resource":{"resourceType":"AllergyIntolerance","id":"allergy-1","clinicalStatus":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical","code":"active"}]},"verificationStatus":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/allergyintolerance-verification","code":"confirmed"}]},"type":"allergy","category":["food"],"criticality":"high","code":{"coding":[{"system":"http://snomed.info/sct","code":"227493005","display":"Cashew nuts"}],"text":"Allergy to cashews"},"patient":{"reference":"Patient/patient-1"},"reaction":[{"manifestation":[{"coding":[{"system":"http://snomed.info/sct","code":"271807003","display":"Skin rash"}]}],"severity":"moderate"}]},"search":{"mode":"include"}}]}
set fhirModelBundle = ##class(HS.FHIRModel.R4.Bundle).fromDao(bundle)
;
Zwrite fhirModelBundle.entry.list.size()
3
;
Zwrite fhirModelBundle.entry.list.get(0).toDao() ; patient
{"fullUrl":"https://example.org/fhir/Patient/patient-1","resource":{"resourceType":"Patient","id":"patient-1","name":[{"family":"Doe","given":["John"]}],"gender":"male","birthDate":"1985-02-15"},"search":{"mode":"match"}} ; <DYNAMIC OBJECT>
;
Zwrite fhirModelBundle.entry.list.get(1).toDao() ; encounter
{"fullUrl":"https://example.org/fhir/Encounter/encounter-1","resource":{"resourceType":"Encounter","id":"encounter-1","status":"finished","class":{"system":"http://terminology.hl7.org/CodeSystem/v3-ActCode","code":"AMB","display":"ambulatory"},"subject":{"reference":"Patient/patient-1"},"period":{"start":"2023-07-01T10:00:00Z","end":"2023-07-01T10:30:00Z"}},"search":{"mode":"include"}} ; <DYNAMIC OBJECT>
;
Zwrite fhirModelBundle.entry.list.get(2).toDao() ; Allergy
{"fullUrl":"https://example.org/fhir/AllergyIntolerance/allergy-1","resource":{"resourceType":"AllergyIntolerance","id":"allergy-1","clinicalStatus":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical","code":"active"}]},"verificationStatus":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/allergyintolerance-verification","code":"confirmed"}]},"type":"allergy","category":["food"],"criticality":"high","code":{"coding":[{"system":"http://snomed.info/sct","code":"227493005","display":"Cashew nuts"}],"text":"Allergy to cashews"},"patient":{"reference":"Patient/patient-1"},"reaction":[{"manifestation":[{"coding":[{"system":"http://snomed.info/sct","code":"271807003","display":"Skin rash"}]}],"severity":"moderate"}]},"search":{"mode":"include"}} ; <DYNAMIC OBJECT>Hello @Scott Roth
I directly import JSON via fromDao method and It works for me. Did you get any errors while importing?
Set patient = {"resourceType":"Patient","id":"example","text":{"status":"generated","div":"<div xmlns=\"http://www.w3.org/1999/xhtml\">John Doe</div>"},"identifier":[{"use":"usual","type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"MR","display":"Medical Record Number"}]},"system":"http://hospital.smarthealth.org","value":"123456"}],"active":true,"name":[{"use":"official","family":"Doe","given":["John"]}],"telecom":[{"system":"phone","value":"+1 555-123-4567","use":"mobile"},{"system":"email","value":"john.doe@example.com","use":"home"}],"gender":"male","birthDate":"1980-01-01","address":[{"use":"home","line":["123 Main Street"],"city":"Anytown","state":"CA","postalCode":"90210","country":"USA"}]}
Set r4Patient = ##class(HS.FHIRModel.R4.Patient).fromDao(patient)
Write r4Patient.toString()Thank you, I use the same query to differentiate between system web application APIs and user-defined web applications. It's much easier if there's a distinct column for this purpose.
In the above example, the list property holds a %DynamicObject value, not a string literal. Therefore, you can use DAO (Dynamic Access Object) methods to work with it. However, if you're using the latest IRIS version (2023.2 or later), you can also use the apply method to retrieve the family and given fields directly from the %DynamicObject.
Version 2024.1
Write r4Patient.name.list.apply("$[*].family").%ToJSON()
write r4Patient.name.list.apply("$[*].given").%ToJSON()For older versions that do not support the apply method, you can use traditional JSON access techniques:
/// loop by size
for i=0:1:r4Patient.name.list.size()-1 {
Set humanName = r4Patient.name.list.%Get(i)
Write humanName.family_" "_humanName.given
}
Using an iterator:
/// By iterator
Set iter = r4Patient.name.list.%GetIterator()
While iter.%GetNext(,.humanName) {
Write humanName.family_" "_humanName.given
}Sample code for the human name testing
ClassMethod FHIRHunmanNameTest()
{
Set r4Patient = ##class(HS.FHIRModel.R4.Patient).%New()
;
Set seqHuman = ##class(HS.FHIRModel.R4.SeqOfHumanName).%New()
Set human = ##class(HS.FHIRModel.R4.HumanName).%New()
Set human.family="pil"
Set human.text="Ashok,kumar"
;
Set human1 = ##class(HS.FHIRModel.R4.HumanName).%New()
Set human1.family="pil"
Set human1.text="Ashok,kumar"
;
; seq string
Set seqString = ##class(HS.FHIRModel.R4.SeqOfString).%New()
Set seqString.list=["ashok","kumar"]
;
Set human1.given=seqString
;
Do seqHuman.add(human.toDao())
Do seqHuman.add(human1.toDao())
Set r4Patient.name = seqHuman
Set iter = r4Patient.name.list.%GetIterator()
While iter.%GetNext(,.humanName) {
Write humanName.family_" "_humanName.given
}
For i=0:1:r4Patient.name.list.size()-1 {
Set humanName = r4Patient.name.list.%Get(i)
Write humanName.family_" "_humanName.given
}
Write r4Patient.name.list.apply("$[*].family").%ToJSON(),!
Write r4Patient.name.list.apply("$[*].given").%ToJSON()
}Hello @Corentin Blondeau
Here is the code to retrieve the properties in the same order they are defined in the class.
ClassMethod GetPropOnDefinedSequence(pClass As %String = "", Output pProperties)
{
For {
Set property=$$$comMemberNext(pClass,$$$cCLASSproperty,property)
If property="" Quit
If property="%%OID"!(property="%Concurrency") continue
Set pProperties(
+$$$defMemberKeyGet(pClass,$$$cCLASSproperty,property,$$$cPROPsequencenumber),
property)=""
}
Return $$$OK
} The properties assigned to the pProperties parameter
Hello @Scott Roth
The HS.FHIRModel.R4.SeqOfHumanName has property "list" which is %DynamicArray. So, If you access via object r4Patient.name.list.%Get(0) for get 0 index value $ZCVT(r4Patient.name.list.toString(),"I","JSON") get entire value. Here r4Patient is a object of HS.FHIRModel.R4.Patient
FHIRDEV>Write r4Patient.name.list.%Get(0)
{"use":"official","text":"Ashok,Kumar"}
FHIRDEV>Write $ZCVT(r4Patient.name.list.toString(),"I","JSON")
["{"use":"official","text":"Ashok,Kumar"}","{"text":"Ashok,Kumar"}"]
FHIRDEV>Write $ClassName(r4Patient)
HS.FHIRModel.R4.PatientHello @Vitaliy Serdtsev
Thank you for pointing the %CSP.SessionEvents class to capture the end of request. However, my requirement is to intercept the written API response by class method before it's sent to UI.
Thank you!
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.
XData OnlyLowercaseTopLevel
{
<Mapping xmlns="http://www.intersystems.com/jsonmapping">
<Property Name="Name" FieldName="eventName"/>
<Property Name="Location" Include="INPUTONLY"/>
</Mapping>
}Do obj.%JSONImport(dao) Default import (uses standard property names)Do obj.%JSONImport(dao, "OnlyLowercaseTopLevel")Import using a custom mappingThat'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.
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!
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.
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"
}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
Thank you, @Dmitry Maslennikov, for your incredible journey and inspiring contributions to the community. Wishing you continued success and endless learning ahead!
Thanks for the quick response on the issue — improving the clarity of the error message will definitely help.
The issue is caused by the PythonRuntimeLibrary not being configured. Starting from version 2024.2, Python is no longer bundled with IRIS due to the introduction of the Flexible Python Runtime feature. As a result, the Python directory is missing, preventing the installation of the Wheel file.
Check this post about this configuration.
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=0
For 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
WHERE 1=SAMPLE.PERSON_INLIKE(FirstName,'%vid','Zelda%') Welcome to the DC, @DC AI Bot
Hello @Evgeny Shvarov
We can define the global name using either a compile-time class parameter or a runtime expression (COSEXPRESSION):
Parameter GlobalName = {$NA(^AGlobal)};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")=112
zw @..#GlobalName
}
Yeah, that's true. the date should be in MM/DD/YYYY format. I updated the code.
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 $Namespace
Set $Namespace = "%SYS"
Return ##class(SYS.ApplicationError).DeleteByNamespace(Namespace)
}Delete by date
ClassMethod DeleteAppErrorLogByDT(pNamespace As %String = {$Namespace},pDate ={$ZD(+$H)}) As %Status
{
New $Namespace
Set $Namespace = "%SYS"
Return ##class(SYS.ApplicationError).DeleteByDate(pNamespace,pDate)
}Kudos to all the winners! 👏
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")
}
}