Discussion
Dmitriy Maslennikov · Nov 9, 2020

%session.Data with unlimited amount of indexes

Let's imagine you have to implement a method with a definition

/// Set value to %session.Data
ClassMethod setValue(params...) As %Status
{
}

How it should work

do ..setValue("key1", "val")

is equal to

set %session.Data("key1") = "val"

and 

do ..setValue("key1", "key2", "key3", "key4", "val")

is equal to 

set %session.Data("key1", "key2", "key3", "key4") = "val"

so, quite simple, any amount of arguments, while the latest one is a value, and any previous is an index, should accept at least 2 arguments.

 

How would you implement this method?

10
0 15 128 0

Replies

/// Set value to %session.Data 
ClassMethod setValue(params...) As %Status
{
 quit:params<2 0
 set var="%session.Data("
 for i=1:1:params-1 set var=var_""""_params(i)_""","
 set $e(var,*)=")"
 set @var=params(params)
 quit $$$OK
}

Quite simple, but could be even better, with no useless quotations

did you mean that ?

/// Set value to %session.Data 
ClassMethod setValue(params...) As %Status
{
quit:params<2 0
set var="%session.Data("
for i=1:1:params-1 set var=var_"params("_i_"),"
set $e(var,*)=")"
set @var=params(params)
quit $$$OK
}

Nope, still not what I mean, I have a better solution, just would like to see if the community will find it.

it doesn' trap this cases yet

do ..setValue("key1", "key2", , "key4", "val")
do ..setValue("key1", "key2", "", "key4", "val")

Hey.
It's easier ?
```
ClassMethod 
setValue(params...) As %Status
{
  quit:params<2 0
  s name="%session.Data"
  for i=1:1:params-1 set:$GET(params(i))'="" name=$NA(@name@(params(i)))
  set @name=params(params)
  quit $$$OK
}
```

Yes, this is better, I would even use this

s name=$name(%session.Data)

with error trapping

/// Set value to %session.Data 
ClassMethod setValue(params...) As %Status
{
 try {
   set var=$name(%session.Data)
   for i=1:1:params-1 set var=$name(@var@(params(i)))
   set @var=$g(params(params))
   set status=$$$OK
  }
  catch { set status=$system.Status.Error(710)
  }
  quit status 
 }

I'm not sure about what you mean with "unlimited amount of indexes"...

first, there is a limit of 255 for the parameters, this means you can have 

do ..setValue(key1,key2,...key254,value)

but you can't have 

 do ..setValue(key1,key2,...key254,key255,value)

second, if you meant to have many-many values (in sense of, we have to much data)

  do ..setValue(key1,value1)
  do ..setValue(key2,value2)
  // etc.

then, at some point you will reach the and of the available RAM (incl. swap/paging space.  Usually swap/paging space is much less then the space for the real data).

thirdly, if you meant variable number of indexes (in sense of variable number of parameters) then I can just refine Robert's solution to

ClassMethod setValue(params...) As %Status
{
   set base=$name(%session.Data)
   for i=1:1:params-1 { set base=$name(@base@(params(i)))
   set @base=params(params)
   quit 1
}

In case, you have both, variable number of indices and too much data then you could use a mix of the above with data placed in a temp file.

Somewhere, at the program start, let you point %sessionData to a temp file

   set %sessionData=$nam(^||SessionTemp)

and then consider this in the setValue method

ClassMethod setValue(params...) As %Status
{
   set bas=%sessionData
   ...
}

Of course, in your application, you have to change only those %session.Data(...) to  @%sessionData@(...)  which uses the "variable-big-data" part of %session.Data(). All other (the standard ones) stay unchanged.

For sure, you have good points, but some points just out of scope at all. Yes, it's limited anyway.

My main point is to not have a limited number of parameters like below

ClassMethod setValue(key1, key2, key3, key4, value) As %Status

And I was just curious if the community knows how to deal with MultiDimensional property, in such a case.

Sorry, I don't quite get you.

One used to say, if I don't understand something, then this something is either too complicated or very simple. So where belongs your case?

Sorry, it's just for discussion, to see how people would do some simple task.

I'd mimic %DispatchSetMultidimProperty signature. Shame local cannot be casted into a multi-argument call. Wrap in $g if values can be skipped.

/// do ##class().Test()
ClassMethod Test()
{
    kill ^data
    do ..setValue("val", "key1")
    do ..setValue("val", "key1", "key2", "key3", "key4")
    zw ^data
}

ClassMethod setValue(val, args...)
{
    if args=1 {
        set ^data(args(1)) = val
    } elseif args=2 {
        set ^data(args(1), args(2)) = val
    } elseif args=3 {
        set ^data(args(1), args(2), args(3)) = val
    } elseif args=4 {
        set ^data(args(1), args(2), args(3), args(4)) = val
    }
}

Another option just for the fun:

Include %SYS.PTools.Stats

Class dc.test Abstract ]
{

ClassMethod setValue(args...) As %Status
{
  
quit:args<2 $$$ERROR($$$DataMissing)

  $$$convertArrayToList(args,list)
  
quit:$listlength(list)'=args $$$ERROR($$$RequiredArgumentMissing)
  
  
set $list(list,*,*)="",
         
var=##class(%Utility).FormatString(list),
         
$extract(var,1,3)=$name(%sessionData), ##; or $name(%session.Data)
         
@var=args(args)
  
quit $$$OK
}

/// do ##class(dc.test).Test()
ClassMethod 
Test()
{
  
new %sessionData

  try{

    do $system.OBJ.DisplayError(..setValue("key1""val1")),
          
$system.OBJ.DisplayError(..setValue("key1""key2""key3""key4""val2")),
          
$system.OBJ.DisplayError(..setValue("key1""key2", , "key4""val3")),
          
$system.OBJ.DisplayError(..setValue())
    
    
write !
    
zwrite %sessionData
  
}
  
catch(ex){
    
write ex.DisplayString(),!
  
}
}

}