Mysql
 sql >> Database >  >> RDS >> Mysql

Come trovare risultati simili e ordinare per somiglianza?

Ho scoperto che la distanza di Levenshtein può essere buona quando si cerca una stringa intera rispetto a un'altra stringa intera, ma quando si cercano parole chiave all'interno di una stringa, questo metodo non restituisce (a volte) i risultati desiderati. Inoltre, la funzione SOUNDEX non è adatta per lingue diverse dall'inglese, quindi è piuttosto limitata. Potresti farla franca con LIKE, ma è davvero per le ricerche di base. Potresti voler esaminare altri metodi di ricerca per ciò che vuoi ottenere. Ad esempio:

Puoi utilizzare Luce come base di ricerca per i tuoi progetti. È implementato nella maggior parte dei principali linguaggi di programmazione ed è abbastanza veloce e versatile. Questo metodo è probabilmente il migliore, in quanto non solo ricerca sottostringhe, ma anche trasposizione di lettere, prefissi e suffissi (tutti combinati). Tuttavia, è necessario mantenere un indice separato (l'utilizzo di CRON per aggiornarlo da uno script indipendente di tanto in tanto funziona).

Oppure, se si desidera una soluzione MySQL, la funzionalità fulltext è piuttosto buona e sicuramente più veloce di una procedura memorizzata. Se le tue tabelle non sono MyISAM, puoi creare una tabella temporanea, quindi eseguire la ricerca full-text :

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 ;

Utilizzare un generatore di dati per generare alcuni dati casuali se non vuoi preoccuparti di crearli da solo...

** NOTA ** :il tipo di colonna deve essere latin1_bin per eseguire una ricerca con distinzione tra maiuscole e minuscole anziché con distinzione tra maiuscole e minuscole con latin1 . Per le stringhe unicode, consiglierei utf8_bin per maiuscole e minuscole e utf8_general_ci per ricerche senza distinzione tra maiuscole e minuscole.

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`;

Maggiori informazioni a riguardo dalla pagina di riferimento dell'API MySQL

Lo svantaggio di questo è che non cercherà la trasposizione di lettere o parole "simili, suona come".

** AGGIORNAMENTO **

Usando Lucene per la tua ricerca, dovrai semplicemente creare un lavoro cron (tutti gli host web hanno questa "funzione") in cui questo lavoro eseguirà semplicemente uno script PHP (ad esempio "cd /path/to/script; php searchindexer.php" ) che aggiornerà gli indici. Il motivo è che l'indicizzazione di migliaia di "documenti" (righe, dati, ecc.) può richiedere diversi secondi, anche minuti, ma ciò serve a garantire che tutte le ricerche vengano eseguite il più velocemente possibile. Pertanto, potresti voler creare un processo di ritardo che venga eseguito dal server. Potrebbe essere durante la notte o nella prossima ora, dipende da te. Lo script PHP dovrebbe assomigliare a questo:

$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

Quindi, questo è fondamentalmente il modo in cui cerchi (ricerca di 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.
}

Ecco ottimi siti su Lucene in Java , PHP e .Net .

In conclusione ogni metodo di ricerca ha i suoi pro e contro:

  • Hai menzionato Ricerca Sphinx e sembra molto buono, purché tu possa far funzionare il demone sul tuo host web.
  • Zend Lucene richiede un cron job per reindicizzare il database. Sebbene sia abbastanza trasparente per l'utente, ciò significa che eventuali nuovi dati (o dati eliminati!) Non sono sempre sincronizzati con i dati nel database e quindi non verranno visualizzati immediatamente nella ricerca dell'utente.
  • La ricerca MySQL FULLTEXT è buona e veloce, ma non ti darà tutta la potenza e la flessibilità dei primi due.

Sentiti libero di commentare se ho dimenticato/perso qualcosa.