Unique index from base class doesn't work
Hi guys!
My student asked my why his unit tests don't work as they should, and I just could answer him. Here is the simplified case.
There is a class with a required unique property Name:
Class Test.NewClass [ Abstract ]
{
Property Name As %String [ Required ];
Index NameIndex On Name [ Unique ];
}
ObjectScriptObjectScript
And there is an inherited class:
Class Test.NewClass1 Extends (%Persistent, Test.NewClass)
{
}
ObjectScriptObjectScript
What I expect to happen, is when I save two objects of class Test.NewClass1
with the same value of property Name, for the second one to get an error stating that it violates the unique index.
set a = ##class(Test.NewClass1).%New()
set a.Name = "A1"
w a.%Save()
set a = ##class(Test.NewClass1).%New()
set a.Name = "A1"
w a.%Save()
ObjectScriptObjectScript
Right now, it saves both objects without any problems. And there's no index global as well.
What is wrong with this picture and how do I explain why it doesn't work as one would expect?
As I learned the hard way, abstract classes do not have storage implementation, hence no indices either
BINGO !
FWIW, Abstract classes inheriting from %Persistent (or even %SerialObject) do have a storage definition and can define indices too. They just can't be instantiated. To prevent the creation of a storage definition, NoExtent is the keyword to use.
To achieve the expected result of individual storage you may take this approach:
Class Test.NewClass Extends %Persistent [ NoExtent ] { Property Name As %String [ Required ]; Index NameIndex On Name [ Unique ]; }
and
Class Test.NewClass1 Extends Test.NewClass { Storage Default { <Data name="NewClass1DefaultData"> <Value name="1"> <Value>%%CLASSNAME</Value> </Value> <Value name="2"> <Value>Name</Value> </Value> </Data> <DataLocation>^Test.NewClass1D</DataLocation> <DefaultData>NewClass1DefaultData</DefaultData> <IdLocation>^Test.NewClass1D</IdLocation> <IndexLocation>^Test.NewClass1I</IndexLocation> <StreamLocation>^Test.NewClass1S</StreamLocation> <Type>%Library.CacheStorage</Type> } }
and you see:
ERROR #5808: Key not unique: Test.NewClass1:NameIndex:^Test.NewClass1I("NameIndex"," A1")
Thanks! Now it works
For the record: it's not the Abstract-ness that leads to the index being ignored, it's the fact that the index is not defined in the primary superclass of Test.NewClass1. From secondary superclasses, only parameters, properties and methods are inherited, but not indices. So you can keep Test.NewClass Abstract, but make it inherit from %Persistent itself and then have Test.NewClass1 point to it as its primary superclass. I'll ask the doc team to make this a little clearer, as it's not obvious from this section of the doc nor the one on indices right now.
This said, @Robert Cemper 's suggested use of NoExtent is likely what you wanted to achieve.
Yes, that's what I did (because, obviously, the class being abstract is not the cause - works great in other scenarios):
Class Test.NewClass Extends %Persistent [ Abstract, NoExtent ] {} Class Test.NewClass1 Extends Test.NewClass {}
I do know you don't inherit all from the secondary class, but I though the number of things I do inherit was more than just parameters, properties and methods. I take it queries, foreign keys, triggers, projections are out as well?
I think you get everything from secondary superclass(es) except indexes, storage, and relationships.
💡 This question is considered a Key Question. More details here.