Я обнаружил, что расстояние Левенштейна может быть хорошим, когда вы ищете полную строку по другой полной строке, но когда вы ищете ключевые слова внутри строки, этот метод не возвращает (иногда) требуемые результаты. Более того, функция SOUNDEX не подходит для языков, отличных от английского, поэтому она довольно ограничена. Вы можете сойти с рук как, но это действительно для базовых поисков. Возможно, вы захотите посмотреть на другие методы поиска того, чего вы хотите достичь. Например:
Вы можете использовать Lucene в качестве базы поиска для ваших проектов. Он реализован на большинстве основных языков программирования, и он довольно быстрый и универсальный. Этот метод, вероятно, является лучшим, поскольку он ищет не только подстроки, но и транспонирование букв, префиксы и суффиксы (все вместе). Однако вам нужно сохранить отдельный индекс (хотя время от времени работает CRON для обновления его из независимого скрипта).
Или, если вам нужно решение MySQL, полнотекстовая функциональность довольно хорошая и, безусловно, быстрее, чем хранимая процедура. Если ваши таблицы не MyISAM, вы можете создать временную таблицу, а затем выполнить полнотекстовый поиск:
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 ;
Используйте генератор данных , чтобы сгенерировать случайные данные, если вы не хотите создавать их самостоятельно ...
** ПРИМЕЧАНИЕ **: тип столбца должен быть latin1_bin
, чтобы выполнять поиск с учетом регистра, а не без учета регистра с latin1
. Для строк в кодировке Unicode я бы порекомендовал utf8_bin
для регистрозависимых символов и utf8_general_ci
для регистрозависимых поисков.
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`;
Подробнее об этом можно узнать на справочной странице MySQL API
Недостатком этого является то, что он не будет искать транспонирование букв или "похожие звуки" слов.
** ОБНОВЛЕНИЕ **
Используя Lucene для поиска, вам просто нужно будет создать задание cron (у всех веб-хостов есть эта «особенность»), где это задание будет просто выполнять скрипт PHP (ig «cd / path / to / script; php searchindexer»). .php "), которая обновит индексы. Причина в том, что индексация тысяч «документов» (строк, данных и т. Д.) Может занять несколько секунд, даже минут, но это необходимо для того, чтобы все поиски выполнялись максимально быстро. Поэтому вы можете захотеть создать задание задержки, которое будет запускаться сервером. Это может произойти в одночасье или в следующий час, это зависит от вас. PHP-скрипт должен выглядеть примерно так:
$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
Тогда, в основном, вы ищете (основной поиск):
$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.
}
Вот отличные сайты о Lucene в Java , PHP и .Net .
В заключение У каждого метода поиска есть свои плюсы и минусы:
- Вы упомянули Поиск по сфинксу , и это выглядит очень хорошо, если вы можете заставить демона работать на вашем веб-хосте.
- Zend Lucene требует задания cron для повторной индексации базы данных. Хотя он достаточно прозрачен для пользователя, это означает, что любые новые данные (или удаленные данные!) Не всегда синхронизируются с данными в вашей базе данных и, следовательно, не будут сразу отображаться при поиске пользователя.
- Поиск в MySQL FULLTEXT хорош и быстр, но не даст вам всей силы и гибкости первых двух.
Пожалуйста, не стесняйтесь комментировать, если я что-то забыл / пропустил.