需要帮助SQL来排名搜索结果

时间:2010-11-02 05:01:00

标签: php sql mysql search-engine ranking

我正在尝试使用mysql构建一个小型的练习搜索引擎。

每个练习可以有任意数量的搜索标签。

这是我的数据结构:

TABLE exercises
  ID
  title

TABLE searchtags
  ID
  title

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

... exerciseSearchtags是一个多对多的连接表,表示练习和搜索标签之间的关系。

搜索引擎接受未知数量的用户输入关键字。

我想根据关键字/搜索标签匹配的数量对搜索结果进行排名。

这是我目前用于选择练习的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

几乎有效。然而,结果并没有按照我期望的顺序排列。

关于为什么会发生这种情况的最佳猜测是,在通过exercise.ID对行进行分组之前,正在计算相关分数。因此,如果左连接导致特定练习在结果集中出现10次,而另一练习出现4次,则第一次练习可能会获得更高的相关分数,即使它可能没有更多的关键字/搜索标签匹配。 / p>

有没有人对如何防止这种情况发生/解决此问题有任何建议/意见?

先谢谢你的帮助。

3 个答案:

答案 0 :(得分:1)

我找到了解决上述问题的有效方法,并将其发布在此处,以防其他人遇到类似问题。

解决方案是使用子选择而不是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

答案 1 :(得分:0)

分而治之。尝试将问题分解为更小的部分,而不是尝试在一个语句中完成所有操作。例如,首先创建一个临时表,其中包含至少包含一个搜索标记的所有练习。然后进行第二遍以对临时表中的每个练习进行排名。最后选择按排名排序的结果。

答案 2 :(得分:0)

我只为MSSQL而不是mySQL做了类似的事情......所以这可能根本不相关,但值得一试:)

我必须将CASE作为ORDER BY子句的一部分,以使其正确地拾取它,例如:

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中,这样我就可以在页面上输出相关性(按要求)

无论哪种方式,祝你好运!