Aide pour requete SQL un peu complexe

Le problème exposé dans ce sujet a été résolu.

Bonjour!

Je suis en train de finaliser un petit site web perso à destination des étudiants en économie qui à pour but de fournir une base de donnée fiable des principales théories et auteurs de la disciple (au moins jusqu'au niveau L2/L3).

La page d'accueil comporte une barre de recherche qui permet à l'utilisateur de directement rechercher ce qu'il souhaite sur le site. Les informations qu'on peut récupérer sont de plusieurs types. On peut ainsi pour un mot clef rechercher: -un auteur -des théories -un thème (exemple: monnaie) et afficher la liste des auteurs ou théories qui correspondent à ce thème

Une théorie en base de donnée comporte plusieurs champs: auteurs, contenu, thèmes et tags. Quand l'internaute entre un mot clef et recherche des théories, je ne sais pas il recherche par exemple toutes les théories d'un auteur car il s’intéresse à cet auteur, ou toutes les théories qui ont pour thème la monnaie. Je dois donc vérifier sur le mot clé entrée match avec le champ auteur OU le champ contenu OU le champ théme OU le champ tags d'une théorie. Si oui, on considère que la théorie correspond à la recherche de l'utilisateur.

Voici la requête associée (avec Active Record de codeIgniter) qui fonctionne bien:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public function get_theories($search)
    {   
        $resultat = $this->db->select('*')
                     ->from('theories')
                     ->like('nomTheorie',$search)
                     ->or_like('auteurs',$search)
                     ->or_like('themes',$search)
                     ->or_like('tags',$search)
                     ->get()
                     ->result();

        return $resultat;  
    }

Le soucis arrive quand l'utilisateur entre plusieurs mots clef. Par exemple il recherche la théorie appelée "théorie du revenu permanent", énoncée par Milton Friedman. Il tape donc "revenu permanent Friedman" dans le champ de recherche. Or ni le nom de la théorie ni le nom de l'auteur ne contient la chaine "revenu permanent Friedman", donc la théorie n'est pas trouvée avec ma requête. Elle n'est trouvé que si l'internaute cherche "Friedman" ou "revenu permanent".

j'ai cherché dans la documentation et je n'ai pas trouvé mon bonheur… A priori on ne peut pas passer à LIKE de tableau du type:

1
like('champ',explode(' ',$search))

Ce qui répondrait parfaitement! La fonction WHERE_IN correspond à ce que je veux faire…mais avec un Where :( Manifestement il n'existe pas de LIKE_IN… Pourtant c'est exactement ce que je cherche! Il me faudrait en fait un LIKE inversé: le contenu du champ indiqué est contenu dans le masque, et non l'inverse..^^ Je ne peux pas utiliser de OR_LIKE car je ne sais à l'avance le nombre de termes que la recherche de l’utilisateur comprends…

Merci d'avance pour votre aide, et désolé si ma demande peut sembler simple ou stupide^^ J'ai essaye de mieux lire la documentation que la dernière fois :p

+0 -0

Bonjour,

Je te conseille d'oublier active record et de faire ta requête en SQL qui fait ce que tu as envie. Une fois que tu as ça, essaye de la faire avec Active Record.

Parfois (même souvent) c'est impossible, il manque pas mal de truc a CodeIgniter de ce coté la malheureusement.

N’hésite pas a nous montrer ta requête SQL pour qu'on puisse t'aider si besoin.

En fait, j'ai l'impression que tu es en train d'atteindre les limites de SQL pour ce type de recherche…

Sinon, pour répondre à ta question, ce n'est pas possible de construire ta requête dynamiquement ? Genre :

1
2
3
4
5
6
7
$words = explode(' ',$search);
$req = $this->db->select('*')->from('theories');
foreach ($words as $word) {
    $req = $req->or_like('nomTheorie',$search);
    // autres trucs éventuels ici
}
$resultat = $req->get()->result();

Mais tu vas bien vite t'apercevoir que cette façon de faire est plutôt lourde, et a facilement tendance à renvoyer des tas de trucs sans rapport à la requête utilisateur. Qui plus est, tu ne peux pas trier les résultats par pertinence.

Bha dit donc, ca va vite à atteindre les limites en SQL :( Pourtant un simple LIKE IN me sauverait la vie, alors qu'il existe bien un WHERE IN.... Je ne comprends pas trop qu'une telle fonction n'existe pas Oo.

Oui je commençais à réfléchir à la solution que tu proposes mais comme tu dis c'est plutôt lourd et on va avoir à la fin des résultats en doubles (enfin ça dépendra des termes entrées par l’utilisateur). Donc il faudrait à la fin filtrer le tableau pour enlever les éléments en plusieurs exemplaires....

Sans compter le fait que si l'utilisateur à taper disons 4 mots clefs on va faire 4 boucles de requetes au lieu d'une seule, et tout ça pour avoir au final des résultats en double, triple ou quadruple exemplaires… Ce n'est pas très optimisé :(

Mais bon si ce n'est pas possible de faire autrement je vais en passer par là.. je suis quand même très surpris que LIKE IN n'existe pas, et je me demande bien pourquoi et si un futur version de SQL l'intégrera! Ca veux quand même dire que SQL est très vite limité pour des choses un minimum complexes…

+0 -0

Si c'est une base MySQL, tu devrais t'intéresser aux recherches fulltext.

D'autres SGBDR doivent avoir des systèmes similaires, ou au pire il va falloir faire un système de fouille de texte dans un domaine vectoriel à la mano…

+0 -0

Bha dit donc, ca va vite à atteindre les limites en SQL :(

SQL n'est pas prévu pour la "vraie" recherche textuelle, on peut bien sûr bricoler jusqu'à un certain point mais on touche assez vite les limites.

Oui je commençais à réfléchir à la solution que tu proposes mais comme tu dis c'est plutôt lourd et on va avoir à la fin des résultats en doubles (enfin ça dépendra des termes entrées par l’utilisateur). Donc il faudrait à la fin filtrer le tableau pour enlever les éléments en plusieurs exemplaires....

Sans compter le fait que si l'utilisateur à taper disons 4 mots clefs on va faire 4 boucles de requetes au lieu d'une seule, et tout ça pour avoir au final des résultats en double, triple ou quadruple exemplaires…

Dans mon exemple, il n'y a au final qu'une seule requête et pas plusieurs. Mais il faut prendre en compte qu'une requête bourrée de OR n'est sans doute plus très optimisée.

Il est également possible d'employer des REGEXP pour ton problème, attention dans ce cas aux caractères spéciaux qui pourraient se trouver dans la chaîne à rechercher.

Mais bon si ce n'est pas possible de faire autrement je vais en passer par là.. je suis quand même très surpris que LIKE IN n'existe pas, et je me demande bien pourquoi et si un futur version de SQL l'intégrera! Ca veux quand même dire que SQL est très vite limité pour des choses un minimum complexes…

Si tu veux rester en SQL simple et propre, tu peux proposer à tes utilisateurs un champs de recherche "avancé" qui donne à l'utilisateur la possibilité de choisir dans quels champs il veut chercher, etc.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte