sql >> Databáze >  >> RDS >> Mysql

Jak najít podobné výsledky a seřadit je podle podobnosti?

Zjistil jsem, že Levenshteinova vzdálenost může být dobrá, když hledáte celý řetězec proti jinému úplnému řetězci, ale když hledáte klíčová slova v řetězci, tato metoda nevrací (někdy) požadované výsledky. Navíc funkce SOUNDEX není vhodná pro jiné jazyky než angličtinu, takže je značně omezená. S LIKE by vám to prošlo, ale je to opravdu pro základní vyhledávání. Možná se budete chtít podívat na jiné metody hledání toho, čeho chcete dosáhnout. Například:

Můžete použít Lucene jako vyhledávací základnu pro vaše projekty. Je implementován ve většině hlavních programovacích jazyků a je poměrně rychlý a všestranný. Tato metoda je pravděpodobně nejlepší, protože nevyhledává pouze podřetězce, ale také transpozici písmen, předpony a přípony (vše dohromady). Musíte si však ponechat samostatný index (používání CRON k jeho aktualizaci z nezávislého skriptu však jednou za čas funguje).

Nebo, pokud chcete řešení MySQL, fulltextová funkčnost je docela dobrá a určitě rychlejší než uložená procedura. Pokud vaše tabulky nejsou MyISAM, můžete vytvořit dočasnou tabulku a poté provést fulltextové vyhledávání:

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 ;

Použijte generátor dat vygenerovat nějaká náhodná data, pokud se nechcete obtěžovat jejich vytvářením sami...

** POZNÁMKA ** :typ sloupce by měl být latin1_bin chcete-li pomocí latin1 provést vyhledávání rozlišující malá a velká písmena místo nerozlišování malých a velkých písmen . Pro řetězce unicode bych doporučil utf8_bin pro velká a malá písmena a utf8_general_ci pro vyhledávání bez rozlišení malých a velkých písmen.

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

Přečtěte si o něm více na referenční stránce rozhraní MySQL API

Nevýhodou je, že nebude hledat transpozici písmen nebo slova „podobně, zní jako“.

** AKTUALIZACE **

Pomocí Lucene pro své vyhledávání budete muset jednoduše vytvořit úlohu cron (tuto „funkci mají všichni weboví hostitelé“), kde tato úloha jednoduše spustí skript PHP (např. „cd /cesta/k/scriptu; php searchindexer.php“ ), který aktualizuje indexy. Důvodem je to, že indexování tisíců „dokumentů“ (řádků, dat atd.) může trvat několik sekund, dokonce i minut, ale je to proto, aby bylo zajištěno, že všechna vyhledávání budou provedena co nejrychleji. Proto můžete chtít vytvořit úlohu zpoždění, která bude spuštěna serverem. Může to být přes noc nebo během příští hodiny, záleží na vás. PHP skript by měl vypadat nějak takto:

$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

Pak v zásadě hledáte (základní vyhledávání):

$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.
}

Zde jsou skvělé stránky o Lucene v Java , PHP a .Net .

Na závěr každá metoda vyhledávání má své výhody a nevýhody:

  • Zmínil jste Sphinx search a vypadá to velmi dobře, pokud dokážete spustit démona na svém webovém hostiteli.
  • Zend Lucene vyžaduje k přeindexování databáze úlohu cron. I když je to pro uživatele zcela transparentní, znamená to, že jakákoli nová data (nebo smazaná data!) nejsou vždy synchronizována s daty ve vaší databázi, a proto se při vyhledávání uživatelů hned nezobrazí.
  • Fulltextové vyhledávání MySQL je dobré a rychlé, ale neposkytne vám veškerý výkon a flexibilitu prvních dvou.

Pokud jsem na něco zapomněl/zapomněl, neváhejte se vyjádřit.



  1. CS50:Operátor LIKE, substituce proměnné s % expanzí

  2. Indexované zobrazení údržby v prováděcích plánech

  3. Použití aliasu v klauzuli WHERE

  4. Získejte dotaz ze souboru v SPRING BOOT pomocí @Query