Как я запрашиваю для записей, заказанных подобием?
Например, поиск "Переполнения Запаса" возвратился бы
Например, поиск "LO" возвратился бы:
Используя поисковую систему, чтобы индексировать и искать таблицу MySQL, для лучших результатов
Используя полнотекстовое индексирование, для нахождения подобных/содержащих строк
LIKE
лучшие результаты возвратов, но возвраты ничто для долгих запросов, хотя подобные строки действительно существуют Я обнаружил, что расстояние Левенштейна может быть хорошим, когда вы ищете полную строку по сравнению с другой полной строкой, но когда вы ищете ключевые слова внутри строки, этот метод не возвращает ( иногда) желаемые результаты. Кроме того, функция SOUNDEX не подходит для других языков, кроме английского, поэтому она весьма ограничена. Вы можете обойтись без LIKE, но это действительно для базового поиска. Возможно, вы захотите изучить другие методы поиска того, чего вы хотите достичь. Например:
Вы можете использовать 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 скрипт (например, «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 .
В заключение у каждого метода поиска есть свои плюсы и минусы:
Не стесняйтесь комментировать, если я что-то забыл / пропустил.
1. Сходство
Для Левенштейна в MySQL я нашел это из www.codejanitor.com/wp/2007/02/10/levenshtein-distance-as-a-mysql-stored-function
SELECT
column,
LEVENSHTEIN(column, 'search_string') AS distance
FROM table
WHERE
LEVENSHTEIN(column, 'search_string') < distance_limit
ORDER BY distance DESC
2. Содержит, нечувствителен к регистру
Используйте оператор MySQL LIKE
, который по умолчанию не чувствителен к регистру. %
является подстановочным знаком, поэтому может быть любая строка до и после search_string
.
SELECT
*
FROM
table
WHERE
column_name LIKE "%search_string%"
3. Содержит, чувствителен к регистру
Справочник MySQL помогает:
Набор символов и сопоставление по умолчанию - latin1 и latin1_swedish_ci, поэтому небинарные сравнения строк по умолчанию нечувствительны к регистру. Это означает, что если вы выполняете поиск с col_name LIKE 'a%', вы получите все значения столбцов, которые начинаются с A или a. Чтобы сделать этот поиск чувствительным к регистру, убедитесь, что один из операндов имеет чувствительное к регистру или двоичное сопоставление. Например, если вы сравниваете столбец и строку, которые оба имеют набор символов latin1, вы можете использовать оператор COLLATE, чтобы заставить любой операнд иметь сопоставление latin1_general_cs или latin1_bin ...
Моя установка MySQL не поддерживает latin1_general_cs
или latin1_bin
, но у меня было нормально использовать сопоставление utf8_bin
, поскольку двоичный utf8 чувствителен к регистру:
SELECT
*
FROM
table
WHERE
column_name LIKE "%search_string%" COLLATE utf8_bin
2. / 3. отсортировано по Левенштейну Расстояние
SELECT
column,
LEVENSHTEIN(column, 'search_string') AS distance // for sorting
FROM table
WHERE
column_name LIKE "%search_string%"
COLLATE utf8_bin // for case sensitivity, just leave out for CI
ORDER BY
distance
DESC