Article
· Jan 9 9m de lecture

IRIS Document Database - DocDB

InterSystems IRIS Document Database (DocDB) offre une approche flexible et dynamique de la gestion des données de base de données. DocDB exploite la puissance de JSON (JavaScript Object Notation), fournissant un environnement sans schéma pour le stockage et la récupération des données.

Il s'agit d'un outil puissant qui permet aux développeurs de contourner une tonne de code standard en interaction avec les applications existantes, la sérialisation, la pagination et l'intégration. le flux transparent de DocDB avec les services et opérations Interoperability Rest constitue un grand pas en avant dans la production et la gestion des API.

pour la documentation complète de DocDB Here. dans le contexte de cet article, je présenterai un cas d'utilisation dans lequel DocDB s'adaptera parfaitement.

Les organisations utilisant les produits InterSystems comme IRIS, Interoperability, TrakCare,... génèrent constamment de nouvelles exigences agiles en matière de partage de données via d'autres systèmes à consommer. Alors qu'InterSystems propose un adaptateur JSON intégré pour permettre aux classes persistantes d'être sérialisables JSON au niveau de l'enregistrement. Cependant, ni la réécriture de votre application dans la mesure où l'adaptateur JSON n'est une tâche facile et vos données ne seront toujours gérées au niveau de l'enregistrement directement à partir de votre application. Voici DocDB qui entre en scène, car vous pouvez créer des requêtes sur le code de votre application et le transférer dans DocDB au format JSON, prêt à être expédié aux applications grand public.

 

Voici un extrait exécutant n'importe quelle procédure stockée IRIS standard de manière générique et enregistrant la sortie sous forme d'enregistrements au format 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
    
}

ça y est, vos données (quelles qu'elles soient) sont prêtes à être exposées aux applications consommatrices.

Prenons le cas d'usage de la surveillance des intégrations, vos requêtes vérifieront l'état de votre production d'interopérabilité InterSystems, les erreurs, les alertes,..

et les données seront enregistrées dans une DocDB à intervalles réguliers.

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())
		}
}

}

Désormais, toute application consommatrice externe peut récupérer les données pour vérifier l'état des productions, les alertes, les erreurs et les avertissements à l'aide d'un simple service REST InterSystems comme suit

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
				}
		}
}

}

Un exemple réel d'application consommatrice du cas d'utilisation ci-dessus est partagé ici sur open exchange. Il s'agit d'une application mobile développée dans le framework Dart pour Flutter.

En plus de cela, DocDB expose une API directe sur toutes les tables de documents, décrites en détail Ici

La fonctionnalité Edge offerte par DocDB permet le développement d'API pour le partage d'informations entre les systèmes en fonction de l'accent mis sur les cas d'utilisation, tout en accélérant et en standardisant toutes les sérialisations, mises en scène et intégrations des données.

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer