Question
· 19 hr ago

How get properties of a class, sorted by Storage

Hello

I want to get the property of a class, sorted by Storage.

I know we can use
 

        Set definition = ##class(%Dictionary.ClassDefinition).%OpenId(className)
        Set listProperty = definition.Properties
        For ii = 1:1:listProperty.Count(){
                    write listProperty.GetAt(ii).Name

But using GetAt sorts the results alphabetically.

Example :

Class Test.class Extends (%SerialObject, %XML.Adaptor, %JSON.Adaptor)
{

    Property tiers As %String;
    Property journal As %String;
}

listProperty.GetAt(1).Name = "journal" and listProperty.GetAt(2).Name = "tiers"

But I would like it to be instead:  listProperty.GetAt(1).Name = "tiers" and listProperty.GetAt(2).Name = "journal"


Do you have a solution, please?

Corentin

Product version: IRIS 2024.1
$ZV: IRIS for Windows (x86-64) 2024.1.3 (Build 456U) Thu Jan 9 2025 12:47:03 EST
Discussion (4)3
Log in or sign up to continue

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

properties(1,"Name")=""
properties(2,"City")=""
properties(3,"IsActive")=""
properties(4,"State")=""

An easy and simple (assuming standard storage strategy) way to get those informations (the slot numbers, where a property is stored) could be achieved with a simple classmethod

Class DC.PropInfo [ Abstract ]
{

/// Info about properties of a class:
/// - list of all properties stored in a list
/// - slot number for a given property
/// 
/// 1) add this class to your class definition
///    class some.class extends (%Persistent, DC.StorageInfo)  or
///    class some.class extends (%SerialClass, DC.StorageInfo)
///           
/// 2) then use it as follows
///    write ##class(some.class).PropInfo()      --> list of property names
///    write ##class(some.class).PropInfo("Age") --> slot number for the Age property
/// 
/// write ##class(
ClassMethod PropInfo(name = "") As %String [ CodeMode = objectgenerator ]
{
	set sto=%compiledclass.Storages, prp=0, list=""
	
	if sto.Count()=1 {
		set dat=sto.GetAt(1).Data
		for i=1:1:dat.Count() if dat.GetAt(i).Structure="listnode" set prp=dat.GetAt(i) quit
	
		if prp {
			if %compiledclass.ClassType="serial" { set list="", i=1 } else { set list=$lb(""), i=2 }
			for i=i:1:prp.Values.Count() set list=list_$lb(prp.Values.GetAt(i).Value)
		}
		
		do %code.WriteLine(" if name="""" quit """_$lts(list)_"""")
		do %code.WriteLine(" quit $lf($lfs("""_$lts(list)_"""),name)")
	}
	
	if list="" write !,"*** No properties found! ***"
	quit $$$OK
}

}

Two test classes

Class DC.TestPerson Extends (%Persistent, DC.PropInfo)
{
Property Name As %String;
Property Addr As DC.Address;
Property Salary As %Numeric;
Property Expertise As list Of %String;
}


Class DC.Address Extends (%SerialObject,DC.PropInfo)
{
Property Street As %String;
Property City As %String;
}

Now you can do things like:

write ##class(DC.TestPerson).PropInfo()         --> ,Name,Addr,Salary,Expertise
write ##class(DC.TestPerson).PropInfo("Salary") --> 4
// you can use the slot number for direct data access:
write $list(^DC.TestPersonD(id),slotnumber)  gives you the same value as
write ##class(DC.TestPerson).SalaryGetStored(id)

// the same game for serial classes
write ##class(DC.Address).PropInfo()       --> Street,City
write ##class(DC.Address).PropInfo("City") --> 2

// in case you have an instance
set pers=##class(DC.TestPerson).%OpenId(id)
write pers.PropInfo()      --> ,Name,Addr,Salary,Expertise
write pers.Addr.PropInfo() --> Street,City

// etc.

Thanks for your responses !!!

I found a way to do it with SQL :

    Set sqlQuery = "SELECT * FROM %Dictionary.PropertyDefinition WHERE parent = '"_ className _"' ORDER BY SequenceNumber"
    Set resultSet = ##class(%SQL.Statement).%New()
    Set status = resultSet.%Prepare(sqlQuery)
    $$$ThrowOnError(status)
    Set tResult = resultSet.%Execute()
    
    While tResult.%Next() {
        Set Name = tResult.%Get("Name")
        ...
        }

Corentin