Bug 27908

GemStone/S

6.7, 6.6.5, 6.6.4, 6.6.3.3, 6.6.3.2, 6.6.3, 6.6.2, 6.6.1, 6.6, 6.5.8, 6.5.7.5, 6.5.7, 6.5.6, 6.5.5, 6.5.4, 6.5.2, 6.5.1, 6.5, 6.3.1, 6.3, 6.2.x, 6.2, 6.1.6, 6.1.5, 6.1.x, 6.0.x, 5.1.5.1, 5.1.5, 5.1.4

Set-valued indexes are known to have problems

Avoid creating set-valued indexes (indexes with a * in the path).
Set valued indexes are known in practice to be less reliable than
standard indexes.  In addition, due to the nature of these indexes,
there may be excessive volume of indexing infrastructure created,
and the same functionality will often have similar performance if
the code is refactored to use standard indexes.

GemStone may choose to deprecate this functionality at some future
time.

Workaround

If you refer to section 5.6 in the 6.0 GS Programming Guide (page 5-28
in the 6.x version, 5-31 in the 5.x version), there are two examples
of using set-valued indexes.  Taking the first one, finding all employees
with children under age 18:

   AllEmployees select:  { :emp | emp.children.*.age <= 18 }

This is equivalent to the code:

  | results |
  results := AllEmployees class new.
  AllEmployees do: [ :anEmployee |
       anEmployee.children do: [ :aChild |
           (aChild.age <= 18) ifTrue: [results add: anEmployee]]].
  results

The set-valued query (on emp.children.*.age) on can be used without
creating a set valued index.

Note that, due to the semantics of select over set-valued indexes,
the resulting collection may contain multiple instances of the same
Employee (if, for example, the Employee has more than one child under
18).  The resulting collection will contain one element for each
occasion the predicate evaluates to true, which can mean the
resulting collection is larger than the original collection.

While using the set-valued query, you might expect that creating an
index of the form:

   AllEmployees createEqualityIndexOn: 'children.*.age'

would make the query faster, in actual practice this may or may not
be the case, and excessive overhead objects may be generated.

If you wish to use indexes for this query, avoiding set-valued
indexes, you can refactor by putting all of the children objects into
a separate collection, called AllChildren.  The index then goes on
AllChildren.  You would need to modify code for adding/deleting
children to a particular employee so that this collection is also
kept in synch.  You also need to add a back pointer from the children
object to point to the owning parent object.

Here is how that query might be written:

(AllChildren select: { :child | child.age <= 18 })
    collect: [ :child | child parent ]

or perhaps more efficiently:

  | result |
  result := AllChildren speciesForSelect new.
  (AllChildren select: { :child | child.age <= 18 }) do: [:child |
    result add: child parent ].
  result


Last updated: 8/11/03