Нужна помощь с SQL для ранжирования результатов поиска - PullRequest
1 голос
/ 02 ноября 2010

Я пытаюсь создать крошечную поисковую систему с использованием mysql.

Каждое упражнение может иметь произвольное количество поисковых тегов.

Вот моя структура данных:

TABLE exercises
  ID
  title

TABLE searchtags
  ID
  title

TABLE exerciseSearchtags
  exerciseID -> exercises.ID
  searchtagID -> searchtags.ID

... где exercSearchtags - это таблица объединения «многие ко многим», выражающая взаимосвязь между упражнениями и тегами поиска.

Поисковая система принимает неизвестное количество введенных пользователем ключевых слов.

Я хочу ранжировать результаты поиска по количеству совпадений ключевых слов / тегов поиска.

Вот sql, который я сейчас использую для выбора упражнений. Правила CASE и правила WHERE генерируются динамически, по одному для каждого ключевого слова. Например, если пользователь вводит 3 ключевых слова, будет 3 правила CASE и 3 правила WHERE.

    SELECT 
        exercises.ID AS ID,
        exercises.title AS title, 
        (
            (CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END)+
            (CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END)+
            ...etc...
            (CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END)
        ) AS relevance

    FROM 
        exercises

    LEFT JOIN exerciseSearchtags
        ON exerciseSearchtags.exerciseID = exercises.ID 

    LEFT JOIN searchtags
        ON searchtags.ID = exerciseSearchtags.searchtagID

    WHERE
        searchtags.title LIKE CONCAT('%',?,'%') OR
        searchtags.title LIKE CONCAT('%',?,'%') OR
        ...etc...
        searchtags.title LIKE CONCAT('%',?,'%') 

    GROUP BY 
        exercises.ID                

    ORDER BY 
        relevance DESC

Это почти работает. Однако результаты не ранжируются в порядке, который я ожидаю.

Мое лучшее предположение относительно того, почему это происходит, заключается в том, что оценка релевантности вычисляется ДО того, как строки сгруппированы по упражнению .ID. Поэтому, если при левом соединении конкретное упражнение появляется в наборе результатов 10 раз, а другое - 4 раза, то первое упражнение может получить более высокий балл релевантности, даже если оно не содержит больше соответствий ключевого слова / поискового тега.

У кого-нибудь есть какие-либо предложения / советы о том, как я могу предотвратить это / исправить это?

Спасибо (заранее) за вашу помощь.

Ответы [ 3 ]

1 голос
/ 02 ноября 2010

Я нашел рабочее решение вышеуказанной проблемы и выкладываю его здесь, на случай, если кто-то еще столкнется с подобной проблемой.

Решение состоит в том, чтобы использовать вспомогательный выбор вместо оператора case. Вот вышеприведенный код, исправленный. (Я не знаю, является ли это лучшим или наиболее эффективным решением, но в настоящее время оно устранило проблему для меня и, по-видимому, довольно быстро возвращает результаты поиска.)

SELECT 
    exercises.ID AS ID,
    exercises.title AS title, 
    (
        (
            SELECT COUNT(1) 
            FROM searchtags 
            LEFT JOIN exerciseSearchtags 
            ON exerciseSearchtags.searchtagID = searchtags.ID 
            WHERE searchtags.title LIKE CONCAT('%',?,'%') 
            AND exerciseSearchtags.exerciseID = exercises.ID
        )+
        (
            SELECT COUNT(1) 
            FROM searchtags 
            LEFT JOIN exerciseSearchtags 
            ON exerciseSearchtags.searchtagID = searchtags.ID 
            WHERE searchtags.title LIKE CONCAT('%',?,'%') 
            AND exerciseSearchtags.exerciseID = exercises.ID
        )+
        ...etc...
        (
            SELECT COUNT(1) 
            FROM searchtags 
            LEFT JOIN exerciseSearchtags 
            ON exerciseSearchtags.searchtagID = searchtags.ID 
            WHERE searchtags.title LIKE CONCAT('%',?,'%') 
            AND exerciseSearchtags.exerciseID = exercises.ID
        )
    ) AS relevance

FROM 
    exercises

LEFT JOIN exerciseSearchtags
    ON exerciseSearchtags.exerciseID = exercises.ID 

LEFT JOIN searchtags
    ON searchtags.ID = exerciseSearchtags.searchtagID

WHERE
    searchtags.title LIKE CONCAT('%',?,'%') OR
    searchtags.title LIKE CONCAT('%',?,'%') OR
    ...etc...
    searchtags.title LIKE CONCAT('%',?,'%') 

GROUP BY 
    exercises.ID                

ORDER BY 
    relevance DESC
0 голосов
/ 02 ноября 2010

Я только что сделал что-то подобное для MSSQL, а не для MySQL ... так что это может вообще не относиться, но это стоит попробовать :)

Я должен был указать CASE как часть предложения ORDER BY, чтобы он правильно его поднял, например ::1003*

ORDER BY
    CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END +
    CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END +
    ...etc...
    CASE WHEN searchtags.title LIKE CONCAT('%',?,'%') THEN 1 ELSE 0 END DESC

Оставляя их также в SELECT, чтобы я мог вывести релевантность на странице (по запросу)

В любом случае, удачи вам!

0 голосов
/ 02 ноября 2010

Разделяй и властвуй.Вместо того, чтобы пытаться сделать все в одном утверждении, попробуйте разложить проблему на более мелкие части.Например, сначала создайте временную таблицу со всеми упражнениями, которые содержат хотя бы один из поисковых тегов.Затем сделайте второй проход, чтобы оценить каждое упражнение во временной таблице.Наконец выберите результат, упорядоченный по рангу.

...