sql >> Database teknologi >  >> RDS >> Mysql

Hvordan finder man lignende resultater og sorterer efter lighed?

Jeg har fundet ud af, at Levenshtein-afstanden kan være god, når du søger en hel streng mod en anden hel streng, men når du leder efter nøgleord i en streng, returnerer denne metode ikke (nogle gange) de ønskede resultater. Desuden er SOUNDEX-funktionen ikke egnet til andre sprog end engelsk, så den er ret begrænset. Du kunne slippe afsted med LIKE, men det er egentlig til grundlæggende søgninger. Du vil måske undersøge andre søgemetoder for, hvad du vil opnå. For eksempel:

Du kan bruge Lucene som søgebase for dine projekter. Det er implementeret i de fleste større programmeringssprog, og det ville være ret hurtigt og alsidigt. Denne metode er nok den bedste, da den ikke kun søger efter understrenge, men også bogstavtransponering, præfikser og suffikser (alt sammen). Du skal dog holde et separat indeks (det virker dog at bruge CRON til at opdatere det fra et uafhængigt script en gang imellem).

Eller, hvis du ønsker en MySQL-løsning, er fuldtekst-funktionaliteten ret god og bestemt hurtigere end en lagret procedure. Hvis dine tabeller ikke er MyISAM, kan du oprette en midlertidig tabel og derefter udføre din fuldtekstsøgning :

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 ;

Brug en datagenerator at generere nogle tilfældige data, hvis du ikke vil genere at oprette det selv...

** BEMÆRK ** :kolonnetypen skal være latin1_bin at udføre en søgning, der skelner mellem store og små bogstaver, i stedet for ufølsom med latin1 . Til unicode-strenge vil jeg anbefale utf8_bin for store og små bogstaver og utf8_general_ci for store og små bogstaver.

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

Læs mere om det på MySQL API-referencesiden

Ulempen ved dette er, at det ikke leder efter bogstavtransponering eller "lignende, lyder som" ord.

** OPDATERING **

Ved at bruge Lucene til din søgning skal du blot oprette et cron-job (alle webhosts har denne "funktion"), hvor dette job blot vil udføre et PHP-script (f.eks. "cd /path/to/script; php searchindexer.php" ), der opdaterer indekserne. Årsagen er, at indeksering af tusindvis af "dokumenter" (rækker, data osv.) kan tage flere sekunder, endda minutter, men dette er for at sikre, at alle søgninger udføres så hurtigt som muligt. Derfor vil du måske oprette et forsinkelsesjob, der skal køres af serveren. Det kan være natten over, eller i den næste time er det op til dig. PHP-scriptet skulle se nogenlunde sådan ud:

$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

Så er det dybest set sådan, du søger (grundlæggende søgning):

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

Her er gode sider om Lucene i Java , PHP , og .Net .

Som konklusion hver søgemetode har deres egne fordele og ulemper:

  • Du nævnte Sphinx-søgning og det ser meget godt ud, så længe du kan få dæmonen til at køre på din webhost.
  • Zend Lucene kræver et cron-job for at genindeksere databasen. Selvom det er ret gennemsigtigt for brugeren, betyder det, at alle nye data (eller slettede data!) ikke altid er synkroniserede med dataene i din database og derfor ikke vises med det samme ved brugersøgning.
  • MySQL FULLTEXT-søgning er god og hurtig, men giver dig ikke al kraften og fleksibiliteten fra de to første.

Du er velkommen til at kommentere, hvis jeg har glemt/gået glip af noget.



  1. FILE_ID() vs FILE_IDEX() i SQL Server:Hvad er forskellen?

  2. Brug af JavaFX-tabeller til at organisere data

  3. PDO::__construct():Server sendte tegnsæt (255) ukendt for klienten. Venligst rapporter til udviklerne

  4. Sådan fungerer JulianDay()-funktionen i SQLite