Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment trouver des résultats similaires et trier par similarité ?

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 tirer avec LIKE, mais c'est vraiment pour les recherches de base. Vous voudrez peut-être examiner d'autres méthodes de recherche pour ce que vous voulez réaliser. Par exemple :

Vous pouvez utiliser Lucene comme base de recherche pour vos projets. Il est implémenté dans la plupart des principaux langages de programmation et il serait assez rapide et polyvalent. Cette méthode est probablement la meilleure, car elle recherche non seulement les sous-chaînes, mais également la transposition des lettres, les préfixes et les suffixes (tous combinés). Cependant, vous devez conserver un index séparé (utiliser CRON pour le mettre à jour à partir d'un script indépendant fonctionne de temps en temps).

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 fulltext :

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 vous embêter à les créer vous-même...

** REMARQUE ** :le type de colonne doit être latin1_bin pour effectuer une recherche sensible à la casse au lieu d'insensible à la casse avec latin1 . Pour les chaînes unicode, je recommanderais utf8_bin pour la casse et utf8_general_ci pour les recherches insensibles à la 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`;

En savoir plus à ce sujet sur la page de référence de l'API MySQL

L'inconvénient est qu'il ne recherchera pas la transposition des lettres ou les mots "similaire, sonne comme".

** MISE À JOUR **

En utilisant Lucene pour votre recherche, vous aurez simplement besoin de créer une tâche cron (tous les hébergeurs ont cette "fonctionnalité") où cette tâche exécutera simplement un script PHP (par exemple "cd /path/to/script; php searchindexer.php" ) qui mettra à jour les index. La raison en est que l'indexation de milliers de "documents" (lignes, données, etc.) peut prendre plusieurs secondes, voire minutes, mais c'est pour s'assurer que toutes les recherches sont effectuées le plus rapidement possible. Par conséquent, vous souhaiterez peut-être créer un travail différé à exécuter par le serveur. Cela peut être du jour au lendemain ou dans l'heure qui suit, c'est à vous de décider. Le script PHP devrait ressembler à 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, voici en gros comment 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 en Java , PHP , et .Net .

En conclusion chaque méthode de recherche a ses avantages et inconvénients :

  • Vous avez mentionné Recherche Sphinx et ça a l'air très bien, tant que vous pouvez faire fonctionner le démon sur votre hébergeur.
  • 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 les nouvelles données (ou les données supprimées !) ne sont pas toujours synchronisées avec les données de votre base de données et ne s'afficheront donc pas immédiatement lors de la recherche d'utilisateurs.
  • 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é / raté quelque chose.