Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Jak znaleźć podobne wyniki i posortować według podobieństwa?

Odkryłem, że odległość Levenshteina może być dobra, gdy szukasz pełnego ciągu w porównaniu z innym pełnym ciągiem, ale gdy szukasz słów kluczowych w ciągu, ta metoda nie zwraca (czasami) pożądanych wyników. Co więcej, funkcja SOUNDEX nie jest odpowiednia dla języków innych niż angielski, więc jest dość ograniczona. Możesz uciec z LIKE, ale tak naprawdę jest to podstawowe wyszukiwanie. Możesz przyjrzeć się innym metodom wyszukiwania tego, co chcesz osiągnąć. Na przykład:

Możesz użyć Lucene jako baza wyszukiwania dla Twoich projektów. Jest zaimplementowany w większości głównych języków programowania i jest dość szybki i wszechstronny. Ta metoda jest prawdopodobnie najlepsza, ponieważ wyszukuje nie tylko podciągi, ale także transpozycję liter, przedrostki i przyrostki (wszystkie połączone). Musisz jednak zachować osobny indeks (jednak użycie CRON do aktualizacji go z niezależnego skryptu raz na jakiś czas działa).

Lub, jeśli potrzebujesz rozwiązania MySQL, funkcjonalność pełnego tekstu jest całkiem dobra i na pewno szybsza niż procedura składowana. Jeśli Twoje tabele nie są MyISAM, możesz utworzyć tabelę tymczasową, a następnie przeprowadzić wyszukiwanie pełnotekstowe:

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 ;

Użyj generatora danych aby wygenerować losowe dane, jeśli nie chcesz zawracać sobie głowy tworzeniem ich samodzielnie...

** UWAGA ** :typ kolumny powinien być latin1_bin aby przeprowadzić wyszukiwanie z rozróżnianiem wielkości liter zamiast bez rozróżniania wielkości liter za pomocą latin1 . W przypadku ciągów Unicode polecam utf8_bin dla rozróżniania wielkości liter i utf8_general_ci dla wyszukiwań bez uwzględniania wielkości liter.

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

Przeczytaj więcej na ten temat na stronie referencyjnej interfejsu API MySQL

Wadą tego jest to, że nie będzie szukał transpozycji liter lub słów „podobnych, brzmiących jak”.

** AKTUALIZACJA **

Używając Lucene do wyszukiwania, będziesz musiał po prostu utworzyć zadanie cron (wszystkie hosty internetowe mają tę "funkcję"), w którym to zadanie po prostu wykona skrypt PHP (np. "cd /ścieżka/do/skryptu; php searchindexer.php" ), który zaktualizuje indeksy. Powodem jest to, że indeksowanie tysięcy „dokumentów” (wierszy, danych itp.) może zająć kilka sekund, a nawet minut, ale ma to na celu zapewnienie, że wszystkie wyszukiwania zostaną wykonane tak szybko, jak to możliwe. Dlatego możesz chcieć utworzyć zadanie opóźniające, które będzie uruchamiane przez serwer. Może to nastąpić w nocy lub w ciągu następnej godziny, to zależy od Ciebie. Skrypt PHP powinien wyglądać mniej więcej tak:

$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

Następnie wygląda to tak, jak wyszukujesz (wyszukiwanie podstawowe):

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

Oto świetne strony o Lucene w Java , PHP i .Net .

Na zakończenie każda metoda wyszukiwania ma swoje zalety i wady:

  • Wspomniałeś o wyszukiwarce Sphinx i wygląda bardzo dobrze, o ile możesz uruchomić demona na swoim hoście internetowym.
  • Zend Lucene wymaga zadania cron do ponownego indeksowania bazy danych. Chociaż jest to dość przejrzyste dla użytkownika, oznacza to, że wszelkie nowe dane (lub usunięte dane!) nie zawsze są zsynchronizowane z danymi w Twojej bazie danych i dlatego nie pojawią się od razu w wyszukiwaniu użytkowników.
  • Wyszukiwanie w MySQL FULLTEXT jest dobre i szybkie, ale nie zapewnia wszystkich możliwości i elastyczności pierwszych dwóch.

Prosimy o komentowanie, jeśli czegoś zapomniałem/przeoczyłem.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MySQL czy/i pierwszeństwo?

  2. Zainstaluj MySQL na komputerze Mac

  3. LOAD DATA INFILE łatwo przekonwertować RRRRMMDD na RRRR-MM-DD?

  4. MySQL utf8mb4, Błędy podczas zapisywania emotikonów

  5. Wyzwalacze MySQL nie mogą aktualizować wierszy w tej samej tabeli, do której przypisany jest wyzwalacz. Sugerowane obejście?