2010-11-02 3 views
1

J'essaie de construire un petit moteur de recherche d'exercices en utilisant mysql. Chaque exercice peut avoir un nombre arbitraire de balises de recherche.Besoin d'aide avec SQL pour le classement des résultats de recherche

Voici ma structure de données:

TABLE exercises 
    ID 
    title 

TABLE searchtags 
    ID 
    title 

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

... où exerciseSearchtags est plusieurs à plusieurs table de jointure exprimant la relation entre les exercices et searchtags.

Le moteur de recherche accepte un nombre inconnu de mots-clés entrés par l'utilisateur. Je souhaite classer les résultats de la recherche en fonction du nombre de correspondances mot-clé/recherche.

Voici le sql que j'utilise actuellement pour sélectionner des exercices. Les règles CASE et les règles WHERE sont générées dynamiquement, une pour chaque mot clé. Par exemple, si un utilisateur entre 3 mots-clés, il y aura 3 règles CASE et 3 règles 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 

Cette presque œuvres. Cependant, les résultats ne sont pas classés dans l'ordre auquel je m'attendrais.

Ma meilleure idée de la raison pour laquelle cela se produit, c'est que le score de pertinence est calculé AVANT que les lignes ne soient groupées par exercise.ID. Ainsi, si la jointure gauche fait apparaître un exercice particulier 10 fois dans le jeu de résultats et un autre exercice 4 fois, le premier exercice peut obtenir un score de pertinence plus élevé, même s'il ne contient pas plus de correspondances mot clé/recherche.

Est-ce que quelqu'un a des suggestions/conseils sur la façon dont je peux éviter que cela se produise/résoudre ce problème?

Merci d'avance pour votre aide.

Répondre

0

Diviser et conquérir. Au lieu d'essayer de tout faire en une seule déclaration, essayez de décomposer le problème en plus petits morceaux. Par exemple, créez d'abord une table temporaire avec tous les exercices contenant au moins une des balises de recherche. Faites ensuite une deuxième passe pour classer chaque exercice dans la table temporaire. Enfin, sélectionnez le résultat trié par classement.

0

Je n'ai fait quelque chose de similaire pour MSSQL pas mySQL ... donc ce ne serait pas pertinent du tout, mais sa vaut le coup :)

je devais mettre le CASE est dans le cadre de la clause ORDER BY pour l'obtenir pour le ramasser correctement par exemple:

 
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 

tout en les laissant dans le SELECT pour que je puisse sortir la pertinence de la page (comme demandé)

de toute façon, bonne chance avec elle!

+0

merci pour la suggestion. J'ai essayé de coder en dur les règles CASE dans l'instruction ORDER BY, mais malheureusement cela n'a pas changé l'ordre des résultats. Donc je suppose que cela est aussi calculé avant que les lignes ne soient groupées dans mysql – Travis

+0

Ah bien; Dommage, j'ai vérifié mon code juste au cas où il y avait quelque chose d'autre que j'ai fait pour le faire fonctionner mais c'était tout. Bonne chance! :) –

1

J'ai trouvé une solution de travail au problème ci-dessus, et je l'affiche ici, au cas où quelqu'un d'autre rencontrerait un problème similaire.

La solution consiste à utiliser une sous-sélection, au lieu d'une instruction de cas. Voici le divet de code ci-dessus, corrigé. (Je ne sais pas si c'est la solution la meilleure ou la plus efficace, mais elle a arrangé le problème pour moi, momentanément, et semble renvoyer des résultats de recherche raisonnablement rapidement.)

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 
Questions connexes