Hoe vind je vergelijkbare resultaten en sorteer je op overeenkomst?

Hoe zoek ik naar records die zijn gerangschikt op overeenkomst?

Bijv. zoeken naar “Stock Overflow” zou terugkeren

  1. Stapeloverloop
  2. SharePoint-overloop
  3. Math Overflow
  4. Politieke overloop
  5. VFX-overloop

Bijv. zoeken naar “LO” zou terugkeren:

  1. pabLO picasso
  2. michelangeLO
  3. jackson polLOck

Waar ik hulp bij nodig heb:

  1. Een zoekmachine gebruiken om & zoek in een MySQL-tabel voor betere resultaten

    • De Sphinx-zoekmachine gebruiken, met PHP

    • De Lucene-engine gebruiken met PHP

  2. Indexering van volledige tekst gebruiken om vergelijkbare/bevattende tekenreeksen te vinden


Wat werkt niet goed

  • Levenshtein-afstandis erg grillig. (UDF, Query )
    Zoeken naar “hond” geeft me:

    1. hond
    2. moeras
    3. geleden
    4. groot
    5. echo
  • LIKEgeeft betere resultaten, maar retourneert niets voor lange zoekopdrachten, hoewel vergelijkbare strings bestaan
    1. hond
    2. hondje
    3. hondsdol
    4. dogma

Antwoord 1, autoriteit 100%

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, geeft deze methode (soms) niet de gewenste resultaten. 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 Lucenegebruiken 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 bij te werken 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 gegevensgeneratorom willekeurige gegevens te genereren als u niet de moeite wilt nemen om deze te maken jezelf…

** OPMERKING** : het kolomtype moet latin1_binzijn om hoofdlettergevoelig te zoeken in plaats van hoofdletterongevoelig met latin1. Voor unicode-tekenreeksen raad ik utf8_binaan voor hoofdlettergevoelige en utf8_general_civoor hoofdlettergevoelige 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”) waarbij deze job eenvoudig een PHP-script uitvoert (ig “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 principe 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, PHPen .Net.

Concluderendelke zoekmethode heeft zijn eigen voor- en nadelen:

  • U noemde Sphinx zoekenen het ziet er erg goed uit, zolang u de deamon maar op uw webhost.
  • 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.


Antwoord 2, autoriteit 28%

1. Gelijkenis

Voor Levenshtein in MySQL vond ik dit, van 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. Bevat, niet hoofdlettergevoelig

Gebruik de LIKE-instructie van MySQL, die standaard niet hoofdlettergevoelig is. De %is een jokerteken, dus er kan een tekenreeks voor en na search_stringstaan.

SELECT 
    *
FROM 
    table
WHERE 
    column_name LIKE "%search_string%"

3. Bevat, hoofdlettergevoelig

De MySQL-handleidinghelpt:

De standaardtekenset en sortering zijn latin1 en latin1_swedish_ci, dus niet-binaire tekenreeksvergelijkingen zijn standaard hoofdletterongevoelig. Dit betekent dat als je zoekt met col_name LIKE ‘a%’, je alle kolomwaarden krijgt die beginnen met A of a. Om deze zoekopdracht hoofdlettergevoelig te maken, moet u ervoor zorgen dat een van de operanden een hoofdlettergevoelige of binaire sortering heeft. Als u bijvoorbeeld een kolom en een tekenreeks vergelijkt die beide de tekenset latin1 hebben, kunt u de COLLATE-operator gebruiken om ervoor te zorgen dat een van beide operands de sortering latin1_general_cs of latin1_bin heeft…

Mijn MySQL-configuratie ondersteunt latin1_general_csof latin1_binniet, maar het werkte prima voor mij om de sortering utf8_binte gebruiken omdat binaire utf8 het geval is gevoelig:

SELECT 
    *
FROM 
    table
WHERE 
    column_name LIKE "%search_string%" COLLATE utf8_bin

2. / 3. gesorteerd op Levenshtein Distance

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

Antwoord 3, autoriteit 4%

Het lijkt erop dat uw definitie van overeenkomst semantische overeenkomst is. Dus om zo’n overeenkomstfunctie te bouwen, moet je semantische overeenkomstmetingen gebruiken.
Houd er rekening mee dat de omvang van het werk aan het probleem kan variëren van enkele uren tot jaren, dus het wordt aanbevolen om over de omvang te beslissen voordat u aan het werk gaat.
Ik ben er niet achter welke gegevens je hebt om de overeenkomst van overeenkomst op te bouwen. Ik neem aan dat je toegang hebt tot een dataset met documenten en een dataset met queries.
U kunt beginnen met het gelijktijdig voorkomen van de woorden (bijvoorbeeld voorwaardelijke kans).
U zult snel ontdekken dat u de lijst met stopwoordenkrijgt met betrekking tot de de meeste woorden gewoon omdat ze erg populair zijn.
Het gebruik van de lift van voorwaardelijke kans zorgt voor de stopwoorden, maar maakt de relatie vatbaar voor fouten in een klein aantal (de meeste van uw gevallen).
Je zou Jacardkunnen proberen, maar aangezien het symmetrisch is, zullen er veel relaties zijn die het heeft gewonnen niet vinden.
Dan zou je relaties kunnen beschouwen die alleen op korte afstand van het basiswoord voorkomen. U kunt (en moet) relaties overwegen op basis van algemene corpus (bijv. Wikipedia) en gebruikersspecifieke (bijv. zijn e-mails).

Zeer binnenkort zul je veel overeenkomsten hebben, als alle maten goed zijn en enig voordeel hebben ten opzichte van de andere.

Om dergelijke maatregelen te combineren, verklein ik het probleem graag tot een classificatieprobleem.

U moet een dataset van parijs woorden samenstellen en deze als ‘is gerelateerd’ labelen.
Om een ​​grote gelabelde dataset te bouwen, kunt u:

  • Gebruik bronnen van bekende verwante woorden (bijv. goede oude Wikipedia-categorieën) voor positieven
  • De meeste woorden die niet bekend staan ​​als verwant zijn niet verwant.

Gebruik dan alle maten die je hebt als kenmerken van de paren.
U bevindt zich nu in het domein van het gecontroleerde classificatieprobleem.
Bouw een classificatie op de dataset, geëvalueerd op basis van uw behoeften en krijg een overeenkomstmeting die past bij uw behoeften.

Other episodes