Here is an analysis of query 1 of post #6 above:
select o from ObjectNode o join o.linkedObjects l1
where o.classIdentifier = '(SI)'
and o.type = 0
and o.state = 0
and (o.linkedObjects.classIdentifier = '(TC)'
and o.linkedObjects.objectNameUpper = 'CAM-MILLING')
and (l1.classIdentifier = '(SY)'
and l1.objectNameUpper = 'TECHNOLOGY')
The issue is again suboptimal selection of a query plan. Versions 2.7.6 and 2.8.1 have more or less the same collection of query plans to select from. Version 2.8.1 is expected to make a better choice in most cases, but apparently in this case the selection of 2.8.1 was not optimal.
There are only 237 ObjectNode instances that satisfy the constraint:
(o.classIdentifier = '(SI)' and o.type = 0 and o.state = 0)
Therefore, the optimal query starts by using the composite index "ctso" to quickly reduce the examined nodes. This is actually what version 2.7.6 does.
However, version 2.8.1 selection algorithm is different. Since there are 11,170,730 ObjectNode instances, and there are 269 different combinations of (classIdentifier, type, state), it guesses that:
(o.classIdentifier = '(SI)' and o.type = 0 and o.state = 0)
will have about 11,170,730 / 269 = ~41,527 objects rather than 237, and therefore it doesn't select this option.
We will try to improve this in future versions of ObjectDB. However, as a quick solution you can force ObjectDB to use the query plan that uses the index "ctso".
For example in the Explorer:
select o from ObjectNode o join o.linkedObjects l1
where o.classIdentifier = '(SI)'
and o.type = 0
and o.state = 0
and (o.linkedObjects.classIdentifier = '(TC)'
and o.linkedObjects.objectNameUpper = 'CAM-MILLING')
and (l1.classIdentifier = '(SY)'
and l1.objectNameUpper = 'TECHNOLOGY')
[[objectdb.query-plan-text=ctso]]
Or in the code:
query.setHint("objectdb.query-plan-text", "ctso");
See also the discussion in this thread.