GemStone indexing allows you to specify the optionPathTerms option, which allows you to add objects to an indexed collection that do not have the indexed path term.
Attempting to remove an object that did not have the indexed path term, however, could encounter an error.
File in the following as SystemUser, and commit.
run GsOptionalPathTerm removeSelector: #'_nextObjectFor:'. GsOptionalPathTerm removeSelector: #'_errorInvalidOffset:'. % method: GsOptionalPathTerm _findAllValuesForIdenticalRootObject: rootObject " if index directly on NSC elements or anObject is nil " | ivOffset | ivOffset := self _ivOffsetFor: rootObject. ivOffset ifNil: [ "no values in index for rootObject" ^ IdentityBag new ]. ^ super _findAllValuesForIdenticalRootObject: rootObject % category: 'Accessing' method: GsOptionalPathTerm _nextObjectFor: anObject atInstVar: anIvOffset "Returns the object at the instance variable that corresponds to the receiver" | ivOffset | ivOffset := self _ivOffsetFor: anObject. ivOffset ifNil: [ ^ nil ]. ^ super _nextObjectFor: anObject atInstVar: anIvOffset % category: 'Adding' method: GsOptionalPathTerm addMappingsForObject: anObject root: rootObject logging: aBoolean "Add dependency list entries for anObject." | ivOffset | (nil == anObject or: [ self size == 0 ]) ifTrue: [ ^ self ]. (ivOffset := self _ivOffsetFor: anObject) == nil ifTrue: [ ^ self recordNilOnPathForRoot: rootObject ]. ^ super addMappingsForObject: anObject root: rootObject logging: aBoolean % category: 'Removing' method: GsOptionalPathTerm removeMappingsFor: anObject root: rootObject lastOne: aBoolean logging: doLogging "Remove entries in the btree and dependency lists for anObject." | ivOffset | (nil == anObject or: [ self size == 0 ]) ifTrue: [ ^ self ]. (ivOffset := self _ivOffsetFor: anObject) == nil ifTrue: [ ^ self removeNilOnPathForRoot: rootObject ]. ^ super removeMappingsFor: anObject root: rootObject lastOne: aBoolean logging: doLogging % category: 'Private' method: GsPathTerm _findAllValuesForIdenticalRootObject: rootObject " if index directly on NSC elements or anObject is nil " | key tmpList | (self indicatesIndexOnNscElements or: [ nil == rootObject ]) ifTrue: [ key := rootObject ] ifFalse: [ key := self _nextObjectFor: rootObject ]. tmpList := IdentityBag new. self updateBtree btreeRoot _findAllValuesForIdenticalKey: key into: tmpList. ^ (tmpList occurrencesOf: rootObject) <= 1 % category: 'private' method: PathTerm _findAllValuesForIdenticalRootObject: rootObject " if index directly on NSC elements or anObject is nil " | key tmpList | (self indicatesIndexOnNscElements or: [ nil == rootObject ]) ifTrue: [ key := rootObject ] ifFalse: [ key := self _nextObjectFor: rootObject ]. tmpList := IdentityBag new. self updateBtree btreeRoot _findAllValuesForIdenticalKey: key into: tmpList. ^ (tmpList occurrencesOf: rootObject) <= 1 % category: 'Indexing Support' method: UnorderedCollection _isLastOccurrenceInIndexObjects: anObject "Returns true if the given object is maintained in the indexing objects for one occurrence." | rootTerms pathTerm key val val2 num parentTerm | rootTerms := self _indexedPaths rootTerms. " find a path term with a mapping in the index dictionary " 1 to: rootTerms size do: [ :i | pathTerm := rootTerms at: i. (pathTerm _isObsoletePathTerm) ifFalse: [ pathTerm isRangeEqualityIndexLastPathTerm ifTrue: [ pathTerm offset == 1 ifTrue: [ ^ pathTerm _findAllValuesForIdenticalRootObject: anObject ] ifFalse: [ pathTerm hasIndexDictionary ifTrue: [ pathTerm := pathTerm getParentTerm. val := pathTerm updateDict at: anObject term: pathTerm otherwise: nil. (BucketValueBag _hasInstance: val) ifTrue: [ " see if more than one mapping " pathTerm indicatesNsc ifTrue: [ num := val occurrencesOf: self. num < val size ifTrue: [ " if anObject is contained in other NSCs " ^ false ]. pathTerm := pathTerm getParentTerm. " get path term before this one " val2 := pathTerm updateDict at: self term: pathTerm otherwise: nil. (BucketValueBag _hasInstance: val2) ifTrue: [ ^ val2 size == num ] ifFalse: [ ^ true ] ] ifFalse: [ ^ (val occurrencesOf: self) <= 1 ] ] ifFalse: [ ^ true ] ] ifFalse: [ ^ (self occurrencesOf: anObject) <= 1] ] ] ifFalse: [ pathTerm hasIndexDictionary ifTrue: [ " get key to look up in index dictionary " (pathTerm indicatesIndexOnNscElements or: [ nil == anObject ]) ifTrue: [ key := anObject ] ifFalse: [ " see if a path with '*.*' in it " pathTerm indicatesNsc ifTrue: [ val := pathTerm updateDict at: anObject term: pathTerm getParentTerm otherwise: nil. ^ (BucketValueBag _hasInstance: val) not ] ifFalse: [ key := pathTerm _nextObjectFor: anObject ] ]. val := pathTerm updateDict at: key term: pathTerm otherwise: nil. " look up the mapping in the index dictionary " (BucketValueBag _hasInstance: val) ifTrue: [ " see if more than one mapping " (num := val occurrencesOf: anObject) <= 1 ifTrue: [ ^ true ] ifFalse: [ " see if multiple occurrences are due to more than one object referencing self " " if there is a parent term, it is a SetValuedPathTerm " parentTerm := pathTerm getParentTerm. parentTerm == nil ifTrue: [ ^ false ]. val2 := pathTerm updateDict at: anObject term: parentTerm otherwise: nil. (BucketValueBag _hasInstance: val2) ifTrue: [ ^ (val2 occurrencesOf: self) == num ] ifFalse: [ ^ false ] ] ] ifFalse: [ ^ true ] ] ifFalse: [ ^ (self occurrencesOf: anObject) <= 1] ] ] ]. ^ true " if we get this far, there are only incomplete indexes " %
Last updated: 8/17/20