Artículo
· 16 ene, 2024 Lectura de 9 min

Base de datos documental de IRIS (DocDB)

La base de datos documental de InterSystems IRIS (DocDB) ofrece un enfoque flexible y dinámico para gestionar datos. DocDB aprovecha el poder de JSON (JavaScript Object Notation), proporcionando un entorno sin esquemas para el almacenamiento y la recuperación de datos.

Es una herramienta poderosa que permite a los desarrolladores evitar un montón de código repetitivo cuando se interactúra con aplicaciones existentes, serialización, paginación e integración. La sencillez del uso de DocDB con los servicios y operaciones API REST permite mejorar en la producción y gestión de APIs.

 Aquí podéis revisar la documentación oficial. En este artículo mostraremos un caso de uso en el que DocDB encaja perfectamente.

Las organizaciones que utilizan productos de InterSystems como IRIS, Health Connect, TrakCare,... generan continuamente requisitos que exigen compartir sus datos con otros sistemas.  Mientras que InterSystems ofrece un adaptador JSON (JSON Adapter) integrado para permitir que las clases persistentes sean serializables en JSON a nivel de registro, sin embargo, ni reescribir su aplicación para el aprovechar el adaptador JSON es una tarea fácil ni sus datos de interés se administrarán siempre a nivel de registro directamente desde su aplicación. clases. Aquí entra en escena DocDB, porque puede crear consultas sobre el código de su aplicación y volcarlas en DocDB como JSON, listas para enviarse a las aplicaciones de consumo.

A continuación se muestra un fragmento que ejecuta cualquier procedimiento almacenado IRIS estándar de forma genérica y guarda el resultado como registros en formato json.

 

        //assuming parameters exist as P1,P2,...Pn in context
        set queryData=$lb("queryclassname:queryname",<no of parameters>)
		set queryString=$lg(queryData,1)
		set paramCount=$lg(queryData,2)
		set args=$lb()
		for ix=1:1:paramCount{
			set var="P"_ix
			XECUTE ("(in,out) SET out=$g(in)", @var, .y)
			set $li(args,ix)=$s(y'="":""""_y_"""",1:""""_"""")
			}
		set resultSet=##class(%ResultSet).%New(queryString)
		XECUTE ("(in,out) set out=in.%Execute("_$lts(args)_")",resultSet,.scx)
		set sc=scx
		If $$$ISERR(sc) $$$ThrowStatus(sc)
		set columnNo=resultSet.GetColumnCount()
		
		While resultSet.%Next(.sc) {
			If $$$ISERR(sc) $$$ThrowStatus(sc)
			
			set row={}
			for iz=1:1:columnNo{
				set columnName=resultSet.GetColumnName(iz)
				do row.%Set(columnName,resultSet.GetDataByName(columnName))
			}
			set sc=..AddRecordToReportResult(ID,row)
			If $$$ISERR(sc) $$$ThrowStatus(sc)
		}
ClassMethod AddRecordToReportResult(doc As %DynamicObject)As %Status {
    set sc=$$$OK
	try {
		if (##class(%DocDB.Database).xNExists("PreparedReportDB")){
			set db=##class(%DocDB.Database).%GetDatabase("PreparedReportDB")
		} else {
			set db= ##class(%DocDB.Database).%CreateDatabase("PreparedReportDB")
		}
		set nwRecord=db.%SaveDocument(doc)
		do nwRecord.%Save(0)
		
		k nwRecord
	}catch err{
		set sc=$$$ADDSC(sc,$$$ERROR("5001",err.DisplayString()))
	}
	return sc
    
}

Eso es todo, tus datos (cualesquiera que sean) están listos para ser consumidos por otras aplicaciones.

Como ejemplo veremos el caso de uso del monitoreo de integraciones, las consultas verificarán el estado de las producciiones de interoperabilidad de InterSystems, los errores, alertas, etc. y los datos se guardarán en DocDB a intervalos de tiempo.

Class ProdEye.DataTask Extends %SYS.Task.Definition
{

Property IntervalMinutes As %Integer [ InitialExpression = 5 ];
Method OnTask() As %Status
{
	try {
		set packageName=$P($classname(),".",1)
		
		set targetDocument={}
		
		set ZeroTimeStamp= $$HorologAddSecs^EnsUtil($ztimestamp,-(..IntervalMinutes*60))
		
		set targetDocument.TimeOfQuery=##class(Ens.DataType.UTC).timeUTC()
		set targetDocument.TimeOfQueryIdx=$classmethod(packageName_".Query","getDateTimeUTCIdx")
		do $classmethod(packageName_".Query","GetNameSpaceList")
		k prodArray
		
		do $classmethod(packageName_".Query","GetProductionsList",.prodArray)
		
		
		set productionObjectList=[]
		
		set currNameSpace=""
		for  {
			set currNameSpace=$O(prodArray(currNameSpace))
			quit:currNameSpace=""
			set currProd=""
			for  {
				set currProd=$O(prodArray(currNameSpace,currProd))
				quit:currProd=""
				set newProdData=$g(prodArray(currNameSpace,currProd))
				set newProd=$classmethod(packageName_".Data.Production","%New",currProd,$lg(newProdData,1),$lg(newProdData,2))
				
				k compArray
				do $classmethod(packageName_".Query","GetComponentListByProd",.compArray,currProd,currNameSpace)
				
				set components=""
				set currComp=""
				for  {
					set currComp=$O(compArray(currProd,currComp))
					quit:currComp=""
					set newComp=$classmethod(packageName_".Data.ProductionConfigItem","%New",currComp,$g(compArray(currProd,currComp)))
					set compId=$classmethod(packageName_".Query","GetComponentId",currComp,currProd,currNameSpace)
					set newComp.Type= $classmethod(packageName_".Query","GetComponentType",compId,currNameSpace)
					set newComp.QueueSize=$classmethod(packageName_".Query","GetComponentQueueData",currComp,currNameSpace)
					
					set MessageStats=$classmethod(packageName_".Query","GetComponentMessageCountAndAvg",currComp,ZeroTimeStamp,currNameSpace)
					set:$lv(MessageStats) newComp.MessageCount=$lg(MessageStats,1)
					set:$lv(MessageStats) newComp.MessageAVGProcessingMilliseconds=$lg(MessageStats,2)
					
					set newComp.JobsStatus=$classmethod(packageName_".Data.JobStatus","BuildPropertyFromList",$classmethod(packageName_".Query","GetComponentJobs",currComp,currNameSpace))
					set newComp.Errors=$classmethod(packageName_".Data.Log","BuildPropertyFromList",$classmethod(packageName_".Query","GetComponentErrors",currComp,ZeroTimeStamp,currNameSpace))
					set newComp.Warnings=$classmethod(packageName_".Data.Log","BuildPropertyFromList",$classmethod(packageName_".Query","GetComponentWarnings",currComp,ZeroTimeStamp,currNameSpace))
					set newComp.Alerts=$classmethod(packageName_".Data.Log","BuildPropertyFromList",$classmethod(packageName_".Query","GetComponentAlerts",currComp,ZeroTimeStamp,currNameSpace))
					
					do newProd.Components.Insert(newComp)
				}
				
				
				set newProdJSONStr=""
				do newProd.%JSONExportToString(.newProdJSONStr)
				set newProdJSON={}.%FromJSON(newProdJSONStr)
				do:newProdJSON'="" productionObjectList.%Push(newProdJSON)
				}
			}
		
		do targetDocument.%Set("ProductionList",productionObjectList)
		if (##class(%DocDB.Database).xNExists(packageName_".DB.ProdEyeDocument")){
			set db=##class(%DocDB.Database).%GetDatabase(packageName_".DB.ProdEyeDocument")
		} else {
			set db= ##class(%DocDB.Database).%CreateDatabase("ProdEye.DB.ProdEyeDocument")
			do db.%CreateProperty("TimeOfQueryIdx","%Integer","$.TimeOFQueryIdx",0)
			}
		set nwRecord=db.%SaveDocument(targetDocument)
		
		if $$$ISERR(nwRecord){$$$ThrowStatus(nwRecord)}
		
		set nwRecord.ProfileName=$get(^ProdEyeProfileName(0))
		set nwRecord.TimeOfQueryIdx=targetDocument.TimeOfQueryIdx
		do nwRecord.%Save()
		
		return $$$OK
	} catch error {
		do BACK^%ETN
		return $$$ERROR("5001",error.AsStatus())
		}
}

}

Ahora cualquier aplicación externa podrá recuperar los datos para verificar el estado de las producciones, alertas, errores y advertencias utilizando un simple servicio REST de InterSystems como el siguiente

Include Ensemble

Class ProdEye.Prod.ProdEyeRest Extends EnsLib.REST.Service
{

Property PageSize As %Integer [ InitialExpression = 10 ];
Property TokenValidationURL As %String;
Parameter SETTINGS = "PageSize,TokenValidationURL";
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
	<Route Url="/getdata" Method="POST" Call="GetData"/>
	</Routes>
}

Method GetData(pInput As %Library.AbstractStream, Output poutput As %Stream) As %Status
{
	quit:'$IsObject(pInput) $$$OK
	quit:(pInput.SizeGet()=0) $$$OK
	set paramsObj={}.%FromJSON(pInput.Read())
	set cutoff=paramsObj.cutoff
	set authorisation=paramsObj.Authorisation
	set profilename=paramsObj.profilename
	
	$$$TRACE(..Adapter.IOAddr_" Profile "_profilename_" cutoff parameter: "_paramsObj.%ToJSON())
	
	if ('..Authorise(authorisation,..TokenValidationURL)){
		do poutput.Write("{""Error"":""Invalid Credentials""}")
		do ..ReportHttpStatusCode(403)
		 return $$$OK
	}
	
	if ('..ValidateCutoff(cutoff)){
		do poutput.Write("{""Error"":""Invalid Request Parameters""}")
		do ..ReportHttpStatusCode(400)
		 return $$$OK
	}
	
	
	try{
		set packageName=$P($classname(),".",1)
		SET db = ##class(%DocDB.Database).%GetDatabase(packageName_".DB.ProdEyeDocument")
		set restriction=[]
		do restriction.%Push("TimeOfQueryIdx")
		do restriction.%Push(cutoff)
		do restriction.%Push(">")
		
		do restriction.%Push("ProfileName")
		do restriction.%Push(profilename)
		do restriction.%Push("=")
		
		SET result = db.%FindDocuments(restriction)
		
		if (($IsObject(result))&&($Isobject(result.content))){
			set resultPage={}
			set resultPage.Next=0
			set resultList=[]
			
			set iter=result.content.%GetIterator()
			for dx=1:1:..PageSize{
				set resultItem=iter.%GetNext(.key,.resultItemvalue)
				if $IsObject(resultItemvalue){
					set resultItemvalue={}.%FromJSON(resultItemvalue.%Doc)
					do resultList.%Push(resultItemvalue)
					set:dx>1 resultPage.Next=resultItemvalue.TimeOfQueryIdx
				}
				}
		set resultPage.content=resultList
		
		
		do poutput.Write(resultPage.%ToJSON())
		}else {
			Throw $$$ERROR("5001","Error During Processing Data")
			}
	} catch error {
		do poutput.Write("{""Error"":""Error During Processing Data : """_error.AsSystemError()_""" ""}")
		do ..ReportHttpStatusCode(500)
		}
	
	return $$$OK
}

ClassMethod ValidateCutoff(val As %String) As %Boolean
{
	set sc=1
	quit:+val<=0 0
	quit:$l(+val)<5 0
	set valdt=$E(val,1,5)_","_$E(val,6,10)
	
	try {
		set date=$zdt(valdt)
		
	}catch err{
			
			set sc=0
		}
	
	return sc
}

ClassMethod Authorise(val As %String, TokenValidationURL As %String) As %Boolean
{
	quit:$g(val)="" 0
	if (TokenValidationURL=""){
		set type=$P(val," ",1)
		set type=$zstrip($zstrip(type,"*c"),"<=>w")
		quit:$ZCVT(type,"L")'="basic" 0
		set userpass=$P(val," ",2)
		set userpassDecoded=$system.Encryption.Base64Decode(userpass)
		
		set user=$P(userpassDecoded,":",1)
		set user=$zstrip($zstrip(user,"*c"),"<=>w")
		set pass=$P(userpassDecoded,":",2)
		set pass=$zstrip($zstrip(pass,"*c"),"<=>w")
		
		set prodeyeUser=##class(Ens.Config.Credentials).GetValue("ProdEyeUser","Username")
		set prodeyePass=##class(Ens.Config.Credentials).GetValue("ProdEyeUser","Password")
		
		return ((prodeyeUser=user)&&(prodeyePass=pass))
	}elseif(TokenValidationURL'=""){
		set prodeyeAuthUser=##class(Ens.Config.Credentials).GetValue("ProdEyeAuthUser","Username")
		set prodeyeAuthPass=##class(Ens.Config.Credentials).GetValue("ProdEyeAuthUser","Password")
		
		set tokenVerificationRequest=##class(%Net.HttpRequest).%New()
		do tokenVerificationRequest.SetHeader("content-type","application/json")
		do tokenVerificationRequest.SetHeader("Authorisation","BASIC "_$system.Encryption.Base64Encode(prodeyeAuthUser_":"_prodeyeAuthPass))
		set token=$P(val," ",2)
		set data={}
		set data.Token=token
		do tokenVerificationRequest.EntityBody.Write(data.%ToJSON())
		do tokenVerificationRequest.Post(TokenValidationURL)
		
		set response=tokenVerificationRequest.HttpResponse
		if ($IsObject(response) && $IsObject(response.Data) && response.StatusCode=200 ){
			set message="" while 'response.Data.AtEnd {set message=message_response.Data.Read(,.tSC) if 'tSC quit}
			set responseMessage={}.%FromJSON(message)
			if (responseMessage.%IsDefined("IsValid") && (responseMessage.IsValid="1")){
				return 1
			}else{
				return 0
					}
		}else{
			return 0
				}
		}
}

}

Aquí tenéis en Open Exchange un ejemplo real de una aplicación consumidora del caso de uso anterior. Es una aplicación móvil desarrollada en dart para Flutter framework.

Además de eso, DocDB expone una API directa sobre todas las tablas de documentos, descritas en detalle aquí.

La ventaja que ofrece la funcionalidad DocDB consiste en desarrollar una API para compartir información entre sistemas impulsados por el enfoque en casos de uso y, al mismo tiempo, realizar un seguimiento rápido y estandarizar toda la serialización, preparación e integración de datos.

Comentarios (0)1
Inicie sesión o regístrese para continuar