Задать вопрос
overbeat
@overbeat

Сортировка по схожести?

Привет!



Существует база, допустим в миллион человек. У каждого может быть до 1000 в теории и до 100 реально параметров в формате 0/1. То есть или пользователь что-то имеет, или нет. Необходимо сделать быструю сортировку в результатах поиска по схожести с инициатором поискового запроса (у которого также есть эти параметры). Например у «искателя» есть a, c, d, f, k, m, r, s, y, z из алфавита, нужно отсортировать результаты, чтобы первым был тот, у кого есть максимальное количество подобных букв.



Понятно, что сравнивать автора с каждым из миллиона в результатах поиска любая база загнется, поэтому ищу более умные способы сделать это.



Движок на Rails, база MySQL, поиск видимо на Sphinx.



Сам ламер в данных вопросах, поэтому если вопрос совсем тупой — просто ткните где почитать, вам воздастся :)
  • Вопрос задан
  • 3324 просмотра
Подписаться 3 Оценить 2 комментария
Решения вопроса 1
Dzuba
@Dzuba
Предложение 1: поскольку поля с фильмами представляют собой биты, то имеет смысл хранить их в виде чисел. Максимальное целое в mysql — 8-байтовый BIGINT. То есть, если всего фильмов тысяча, то потребуется полтора-два десятка таких чисел в каждой записи. Пусть N — количество таких чисел-1, userF0, ..., userFN — эти числа в записи выбранного пользователя. Тогда поиск 10 похожих пользователей в таблице с полями (user_id, f0, ..., fN) будет выглядеть так:
SELECT user_id FROM таблица
ORDER BY (BIT_COUNT(f0 & userF0) + ... + BIT_COUNT(fN & userFN)) DESC LIMIT 10;
Минусы подхода: пробегать при запросе будет все записи, при добавлении новых фильмов нужно вызывать ALTER TABLE. За скорость тоже ручаться не могу.

Предложение 2: создать 1 таблицу с юзерами и столько таблиц, сколько фильмов, в каждой из которых хранить список id юзеров, выбравших фильм. Тогда поиск похожих юзеров сведется к:
SELECT tmp.user_id FROM (SELECT user_id FROM таблица1
    UNION ALL
    SELECT user_id FROM таблица2
    UNION ALL
    ...
    UNION ALL
    SELECT user_id FROM таблицаN) AS tmp
GROUP BY tmp.user_id ORDER BY COUNT(tmp.*) DESC LIMIT 10;
Минусы подхода: большое количество подзапросов, группировка.

Предложение 3: создать 1 таблицу с юзерами (users) и 1 таблицу с юзеро-фильмами (user_films), т.е. с записями о предпочтениях юзеров следующего вида (user_id, film_id). Тогда для списка фильмов выбранного юзера (film_id0, ..., film_idN) поиск похожих юзеров сведется к:
SELECT user_id FROM user_films
WHERE film_id IN (film_id0, ..., film_idN)
GROUP BY user_id ORDER BY COUNT(*) DESC LIMIT 10;
Минусы подхода: группировка.
Хотя при индексированном поле film_id может будет и не сильно медленно.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
AlexeyK
@AlexeyK
База не загнется, потому что будет искать Sphinx, если верить тому, что вы говорите.

Могу предложить только писать 0/1 значения в виде букв в отдельное поле, что-то вроде хеша «acdghjxyz» для каждого юзера, а потом искать по простому алгоритму похожести строк.
Ответ написан
Ваш ответ на вопрос

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

Похожие вопросы