J'ai découvert que la distance de Levenshtein peut être bonne lorsque vous recherchez une chaîne complète par rapport à une autre chaîne complète, mais lorsque vous recherchez des mots-clés dans une chaîne, cette méthode ne renvoie pas (parfois) les résultats souhaités. De plus, la fonction SOUNDEX n'est pas adaptée aux langues autres que l'anglais, elle est donc assez limitée. Vous pourriez vous en sortir avec LIKE, mais c'est vraiment pour les recherches de base. Vous pouvez envisager d'autres méthodes de recherche pour ce que vous voulez obtenir. Par exemple :
Vous pouvez utiliser Lucene comme base de recherche pour vos projets. Elle est mise en œuvre dans la plupart des principaux langages de programmation et elle est assez rapide et polyvalente. Cette méthode est probablement la meilleure, car elle ne recherche pas seulement les sous-chaînes, mais aussi la transposition des lettres, les préfixes et les suffixes (tous combinés). Cependant, vous devez conserver un index séparé (l'utilisation de CRON pour le mettre à jour à partir d'un script indépendant de temps en temps fonctionne cependant).
Ou, si vous voulez une solution MySQL, la fonctionnalité de texte intégral est assez bonne, et certainement plus rapide qu'une procédure stockée. Si vos tables ne sont pas MyISAM, vous pouvez créer une table temporaire, puis effectuer votre recherche plein texte :
CREATE TABLE IF NOT EXISTS `tests`.`data_table` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(2000) CHARACTER SET latin1 NOT NULL,
`description` text CHARACTER SET latin1 NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1 ;
Utilisez un générateur de données pour générer des données aléatoires si vous ne voulez pas prendre la peine de les créer vous-même...
** NOTE ** : le type de colonne doit être latin1_bin
pour effectuer une recherche sensible à la casse au lieu d'une recherche insensible à la casse avec latin1
. Pour les chaînes unicode, je recommande utf8_bin
pour la casse et utf8_general_ci
pour les recherches sans distinction de casse.
DROP TABLE IF EXISTS `tests`.`data_table_temp`;
CREATE TEMPORARY TABLE `tests`.`data_table_temp`
SELECT * FROM `tests`.`data_table`;
ALTER TABLE `tests`.`data_table_temp` ENGINE = MYISAM;
ALTER TABLE `tests`.`data_table_temp` ADD FULLTEXT `FTK_title_description` (
`title` ,
`description`
);
SELECT *,
MATCH (`title`,`description`)
AGAINST ('+so* +nullam lorem' IN BOOLEAN MODE) as `score`
FROM `tests`.`data_table_temp`
WHERE MATCH (`title`,`description`)
AGAINST ('+so* +nullam lorem' IN BOOLEAN MODE)
ORDER BY `score` DESC;
DROP TABLE `tests`.`data_table_temp`;
Pour en savoir plus, consultez le site Page de référence de l'API MySQL
L'inconvénient de cette méthode est qu'elle ne recherche pas la transposition des lettres ou les mots "similaires, ressemblant à".
** UPDATE **
En utilisant Lucene pour votre recherche, vous devrez simplement créer une tâche cron (tous les hôtes web ont cette "fonctionnalité") où cette tâche exécutera simplement un script PHP (c'est-à-dire "cd /path/to/script ; php searchindexer.php") qui mettra à jour les index. La raison étant que l'indexation de milliers de "documents" (lignes, données, etc.) peut prendre plusieurs secondes, voire des minutes, mais il s'agit de s'assurer que toutes les recherches sont effectuées aussi rapidement que possible. Par conséquent, vous pouvez créer une tâche de retardement qui sera exécutée par le serveur. Cela peut être pendant la nuit, ou dans l'heure qui suit, cela dépend de vous. Le script PHP devrait ressembler à quelque chose comme ceci :
$indexer = Zend_Search_Lucene::create('/path/to/lucene/data');
Zend_Search_Lucene_Analysis_Analyzer::setDefault(
// change this option for your need
new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive()
);
$rowSet = getDataRowSet(); // perform your SQL query to fetch whatever you need to index
foreach ($rowSet as $row) {
$doc = new Zend_Search_Lucene_Document();
$doc->addField(Zend_Search_Lucene_Field::text('field1', $row->field1, 'utf-8'))
->addField(Zend_Search_Lucene_Field::text('field2', $row->field2, 'utf-8'))
->addField(Zend_Search_Lucene_Field::unIndexed('someValue', $someVariable))
->addField(Zend_Search_Lucene_Field::unIndexed('someObj', serialize($obj), 'utf-8'))
;
$indexer->addDocument($doc);
}
// ... you can get as many $rowSet as you want and create as many documents
// as you wish... each document doesn't necessarily need the same fields...
// Lucene is pretty flexible on this
$indexer->optimize(); // do this every time you add more data to you indexer...
$indexer->commit(); // finalize the process
Ensuite, c'est en gros la façon dont vous effectuez une recherche (recherche de base) :
$index = Zend_Search_Lucene::open('/path/to/lucene/data');
// same search options
Zend_Search_Lucene_Analysis_Analyzer::setDefault(
new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive()
);
Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('utf-8');
$query = 'php +field1:foo'; // search for the word 'php' in any field,
// +search for 'foo' in field 'field1'
$hits = $index->find($query);
$numHits = count($hits);
foreach ($hits as $hit) {
$score = $hit->score; // the hit weight
$field1 = $hit->field1;
// etc.
}
Voici d'excellents sites sur Lucene dans Java , PHP et .Net .
En conclusion chaque méthode de recherche a ses avantages et ses inconvénients :
- Vous avez mentionné Recherche de Sphynx et ça a l'air très bien, tant que vous pouvez faire fonctionner le deamon sur votre hôte web.
- Zend Lucene nécessite une tâche cron pour réindexer la base de données. Bien que cela soit assez transparent pour l'utilisateur, cela signifie que toute nouvelle donnée (ou donnée supprimée !) n'est pas toujours synchronisée avec les données de votre base de données et n'apparaîtra donc pas immédiatement lors d'une recherche utilisateur.
- La recherche MySQL FULLTEXT est bonne et rapide, mais ne vous donnera pas toute la puissance et la flexibilité des deux premières.
N'hésitez pas à commenter si j'ai oublié/manqué quelque chose.