'Args:
ostring (str): A search criterion.
exact (bool, optional): Require exact match of ostring
(still case-insensitive). If `False`, will do fuzzy matching
using `evennia.utils.utils.string_partial_matching` algorithm.
candidates (list): Only match among these candidates.
typeclasses (list): Only match objects with typeclasses having thess path strings.
Returns:
matches (list): A list of matches of length 0, 1 or more.'

@returns_typeclass_list
def get_objs_with_key_or_alias(self, ostring, exact=True, candidates=None, typeclasses=None):

    if (not isinstance(ostring, basestring)):
        if hasattr(ostring, 'key'):
            ostring = ostring.key
        else:
            return []
    if (is_iter(candidates) and (not len(candidates))):
        return []
    candidates_id = [_GA(obj, 'id') for obj in make_iter(candidates) if obj]
    cand_restriction = (((candidates is not None) and Q(pk__in=candidates_id)) or Q())
    type_restriction = ((typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses))) or Q())
    if exact:
        return self.filter(((cand_restriction & type_restriction) & (Q(db_key__iexact=ostring) | (Q(db_tags__db_key__iexact=ostring) & Q(db_tags__db_tagtype__iexact='alias'))))).distinct()
    elif candidates:
        search_candidates = self.filter((cand_restriction & type_restriction))
    else:
        search_candidates = self.filter((type_restriction & (Q(db_key__istartswith=ostring) | Q(db_tags__db_key__istartswith=ostring)))).distinct()
    key_strings = search_candidates.values_list('db_key', flat=True).order_by('id')
    index_matches = string_partial_matching(key_strings, ostring, ret_index=True)
    if index_matches:
        return [obj for (ind, obj) in enumerate(search_candidates) if (ind in index_matches)]
    else:
        search_candidates = search_candidates.filter(db_tags__db_tagtype__iexact='alias', db_tags__db_key__icontains=ostring)
        alias_strings = []
        alias_candidates = []
        for candidate in search_candidates:
            for alias in candidate.aliases.all():
                alias_strings.append(alias)
                alias_candidates.append(candidate)
        index_matches = string_partial_matching(alias_strings, ostring, ret_index=True)
        if index_matches:
            return [alias_candidates[ind] for ind in index_matches]
        return []
