Sphinx и натуральная сортировка

Такая задача: реализовать сортировку (желательно натуральную) в sphinx по атрибуту.

Проблема в том, что в сфинксе при использовании дельта-индексов сортировка по атрибуту sql_attr_str2ordinal абсолютно не пригодна. Т.к. вот что нам говорит документация: Note that the ordinals are by construction local to each index, and it's therefore impossible to merge ordinals while retaining the proper order. The processed strings are replaced by their sequential number in the index they occurred in, but different indexes have different sets of strings. For instance, if 'main' index contains strings «aaa», «bbb», «ccc», and so on up to «zzz», they'll be assigned numbers 1, 2, 3, and so on up to 26, respectively. But then if 'delta' only contains «zzz» the assigned number will be 1. And after the merge, the order will be broken.

Есть следующая идея:
посчитать численный эквивалент строки, высчитываем название по первым 8 символам
Вот с таким алгоритмом:
По сути мы имеем дело с ограниченным диапазоном символов — цифры, латинский и русский алфавиты в нижнем регистре (68 символов). Такой набор символов можно представить как число в 68-ричной системе счисления. Всё, что мы делаем — переводим число из 68-ричной в 10-ричную систему. Символы мы переводим в десятиричные коды, после чего считаем по формуле a(n) * (68 ** 0) + a(n-1) * (68 ** 1) +… + a(1) * (68 ** n)

Вопрос: правильно ли это? Может есть у кого дельные предложения, вообще в идеале хотелось бы natural sorting.
  • Вопрос задан
  • 3254 просмотра
Пригласить эксперта
Ответы на вопрос 1
bibendi
@bibendi Автор вопроса
видимо в раздел q&a профи не заглядывают =)

предварительно решил сделать пока вот так, но уже понятно, что натуральной сортировки не видать, т.к. для этого надо полюбому сравнивать все строки с друг другом
функция на postgresql для преобразования строки в число, может кому пригодится:
CREATE OR REPLACE FUNCTION string_ordinal(str character varying)
  RETURNS double precision AS
$BODY$
DECLARE
  _norm VARCHAR;
  _ln SMALLINT;
  _max_ln SMALLINT;
  i SMALLINT;
  o DOUBLE PRECISION;
  sym CHAR;
  code SMALLINT;
  norm_code SMALLINT;
BEGIN
  _max_ln := 100;
  _norm := regexp_replace(lower(str), '[^A-Za-z0-9а-яА-ЯЁё]', '', 'g');
  _ln = length(_norm);
  
  IF _ln < _max_ln THEN 
    _norm = rpad(_norm, _max_ln, '.');
    _ln = length(_norm);
  END IF;
  
  o := 0.0;
  FOR i IN 1.._ln LOOP
    sym := SUBSTRING(_norm FROM i FOR 1)::CHAR;
    code := ascii(sym);
    norm_code := 0;

    IF code = 46 THEN 
      norm_code := 1;
    END IF;
    IF code >= 48 AND code <= 57 THEN -- 0-9
      norm_code := code - 46;
    END IF;
    IF code >= 97 AND code <= 122 THEN -- a-z
      norm_code := code - (96 - 10);
    END IF;
    IF code >= 1072 AND code <= 1105 THEN -- а-я + ё
      IF code = 1105 THEN -- ё имеет код больший, чем я, это плохо
        code := 1078;
      ELSIF code > 1077 THEN
        code := code + 1;
      END IF;
      norm_code := code - (1071 - 36);
    END IF;
    
    o := o + norm_code * power(68, (_ln - i));
  END LOOP;
  
  RETURN o;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы