Making a plan that works for the general case, but is also efficient, is rather trivial. Here's pseudocode from spending two minutes on the problem:
# INPUT: lookfor: unicode
var lower, upper: ascii
lower = ascii_lower_bound(lookfor)
upper = ascii_upper_bound(lookfor)
for candidate:ascii in index_lookup(lower .. upper):
if expensive_correct_compare_equal(candidate.field, lookfor):
yield candidate
The magic is to have functions ascii_lower_bound and ascii_upper_bound, that compute an ASCII string such that all ASCII strings that compare smaller (greater) cannot be equal to the input. Those functions are not hard to write. Although you might have to implement versions for each supported locale-dependent text comparison algorithm, but still, not a big deal.Worst case, 'lower' and 'upper' span the whole table - could happen if you have some really gnarly string comparison rules to deal with. But then you're no worse off than before. And most of the time you'll have lower==upper and excellent performance.