Ik heb ontdekt dat de Levenshtein-afstand goed kan zijn als je een volledige tekenreeks zoekt tegen een andere volledige tekenreeks, maar wanneer je trefwoorden binnen een tekenreeks zoekt, levert deze methode (soms) niet de gewenste resultaten op. Bovendien is de SOUNDEX-functie niet geschikt voor andere talen dan Engels, dus vrij beperkt. Je zou weg kunnen komen met LIKE, maar het is echt voor basiszoekopdrachten. Misschien wilt u andere zoekmethoden bekijken voor wat u wilt bereiken. Bijvoorbeeld:
U kunt Lucene gebruiken als zoekbasis voor uw projecten. Het is geïmplementeerd in de meeste belangrijke programmeertalen en het zou behoorlijk snel en veelzijdig zijn. Deze methode is waarschijnlijk de beste, omdat er niet alleen naar substrings wordt gezocht, maar ook naar lettertranspositie, voor- en achtervoegsels (allemaal gecombineerd). U moet echter een aparte index bijhouden (af en toe CRON gebruiken om het te updaten vanuit een onafhankelijk script werkt wel).
Of, als u een MySQL-oplossing wilt, is de fulltext-functionaliteit redelijk goed, en zeker sneller dan een opgeslagen procedure. Als uw tabellen niet MyISAM zijn, kunt u een tijdelijke tabel maken en vervolgens uw volledige tekstzoekopdracht uitvoeren:
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 ;
Gebruik een gegevensgenerator om wat willekeurige gegevens te genereren als u niet de moeite wilt nemen om deze zelf te maken...
** OPMERKING ** :het kolomtype moet latin1_bin
. zijn om een hoofdlettergevoelige zoekopdracht uit te voeren in plaats van hoofdletterongevoelig met latin1
. Voor unicode-strings raad ik utf8_bin
aan voor hoofdlettergevoelig en utf8_general_ci
voor hoofdletterongevoelige zoekopdrachten.
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`;
Lees er meer over op de MySQL API-referentiepagina
Het nadeel hiervan is dat het niet zoekt naar lettertranspositie of "vergelijkbare, klinkt als" woorden.
** UPDATE **
Als u Lucene gebruikt voor uw zoekopdracht, hoeft u alleen maar een cron-job aan te maken (alle webhosts hebben deze "functie") waar deze job eenvoudig een PHP-script uitvoert (bijv. "cd /path/to/script; php searchindexer.php" ) waarmee de indexen worden bijgewerkt. De reden hiervoor is dat het indexeren van duizenden "documenten" (rijen, gegevens, enz.) enkele seconden, zelfs minuten kan duren, maar dit is om ervoor te zorgen dat alle zoekopdrachten zo snel mogelijk worden uitgevoerd. Daarom wilt u misschien een vertragingstaak maken die door de server wordt uitgevoerd. Het kan 's nachts zijn, of in het volgende uur, dit is aan jou. Het PHP-script zou er ongeveer zo uit moeten zien:
$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
Dan is dit in feite hoe u zoekt (basiszoekopdracht):
$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.
}
Hier zijn geweldige sites over Lucene in Java , PHP , en .Net .
Tot slot elke zoekmethode heeft zijn eigen voor- en nadelen:
- Je noemde Sphinx zoeken en het ziet er erg goed uit, zolang je de deamon maar op je webhost kunt laten draaien.
- Zend Lucene vereist een cron-taak om de database opnieuw te indexeren. Hoewel het vrij transparant is voor de gebruiker, betekent dit dat nieuwe gegevens (of verwijderde gegevens!) niet altijd synchroon lopen met de gegevens in uw database en daarom niet meteen worden weergegeven bij zoekopdrachten van gebruikers.
- MySQL FULLTEXT zoeken is goed en snel, maar geeft je niet alle kracht en flexibiliteit van de eerste twee.
Voel je vrij om te reageren als ik iets ben vergeten/gemist.