sql >> Database >  >> RDS >> Mysql

Hoe MySQL Boolean Full-Text Search optimaliseren? (Of waar het door te vervangen?) - C#

Ten eerste moet u zich realiseren dat RDBMS-ondersteuning voor indexering van volledige tekst een hack is om een ​​technologie te forceren die is ontworpen om efficiënte toegang tot gestructureerde gegevens mogelijk te maken om ongestructureerde tekst te verwerken. (Ja, dat is gewoon mijn mening. Indien nodig kan ik het verdedigen, aangezien ik beide technologieën zeer goed begrijp.;)

Dus, wat kan er worden gedaan om de zoekprestaties te verbeteren?

Optie één - "Het beste hulpmiddel voor de taak"

De beste manier om zoeken in volledige tekst in een corpus van documenten uit te voeren, is het gebruik van technologie die speciaal hiervoor is ontworpen, zoals SOLR (Lucene) van Apache of Sphinx van fout, Sphinx.

Om redenen die hieronder duidelijk zullen worden, raad ik deze aanpak ten zeerste aan.

Optie twee - laad uw resultaten vooraf

Bij het maken van op tekst gebaseerde zoekoplossingen is de gebruikelijke benadering om alle documenten te indexeren in een enkele doorzoekbare index en hoewel dit misschien het handigst is, is het niet de enige benadering.

Ervan uitgaande dat wat u zoekt gemakkelijk kan worden gekwantificeerd in een reeks bekende regels, zou u meer een "geleide" zoekstijl kunnen bieden dan alleen ongekwalificeerde volledige tekst. Wat ik hiermee bedoel, is dat als uw toepassing er baat bij zou kunnen hebben om gebruikers naar resultaten te verwijzen, u verschillende reeksen resultaten op basis van een bekende reeks regels vooraf in hun eigen tabellen kunt laden, en zo het grootste deel van de te doorzoeken gegevens kunt verminderen.

Als u verwacht dat een meerderheid van uw gebruikers baat zal hebben bij een bekende reeks zoektermen in een bekende volgorde, kunt u uw zoek-UI zo samenstellen dat deze termen de voorkeur geven.

Dus ervan uitgaande dat de meeste gebruikers op zoek zijn naar een verscheidenheid aan auto's, kunt u vooraf gedefinieerde zoekopdrachten aanbieden op basis van model, jaar, staat, enz. Uw gebruikersinterface voor zoeken zou worden gemaakt als een reeks vervolgkeuzemenu's om gebruikers naar specifieke resultaten te "leiden".

Of als de meeste zoekopdrachten betrekking hebben op een specifiek hoofdonderwerp (bijvoorbeeld 'auto's'), kunt u vooraf een tabel definiëren met alleen die records die u eerder hebt geïdentificeerd als gerelateerd aan auto's.

Beide benaderingen zouden het aantal records dat moet worden doorzocht verminderen en dus de responstijden verhogen.

Optie drie - "Roll Your Own"

Als u geen externe zoektechnologie in uw project kunt integreren en vooraf laden geen optie is, zijn er nog steeds manieren om de responstijden van zoekopdrachten enorm te verbeteren, maar deze verschillen afhankelijk van wat u moet bereiken en hoe u verwacht dat zoekopdrachten worden uitgevoerd .

Als u verwacht dat gebruikers zoeken met enkele trefwoorden of woordgroepen en booleaanse relaties daartussen, kunt u overwegen uw eigen 'omgekeerde index ' van je corpus. (Dit is wat MySQL's Boolean Full-Text Search al doet, maar als je het zelf doet, heb je meer controle over zowel de snelheid als de nauwkeurigheid van het zoeken.)

Om een ​​geïnverteerde index te maken van uw bestaande gegevens:

Stap 1. Maak drie tabellen

    // dict - a dictionary containing one row per unique word in corpus  
    create table dict (    
      id int primary key,  
      word varchar  
    )

    // invert - an inverted_index to map words to records in corpus  
    create table invert (    
      id int primary key,  
      rec_id int,  
      word_id int  
    )

    // stopwords - to contain words to ignore when indexing (like a, an, the, etc)
    create table stopwords ( 
      id int primary key,  
      word varchar  
    )

Opmerking:dit is slechts een schets. U wilt indexen en beperkingen, enz. toevoegen wanneer u deze tabellen daadwerkelijk maakt.

De tabel met stopwoorden wordt gebruikt om de grootte van uw index te verkleinen tot alleen die woorden die van belang zijn voor de verwachte zoekopdrachten van gebruikers. Het is bijvoorbeeld zelden nuttig om Engelse artikelen, zoals 'a', 'an', 'the', te indexeren, omdat ze geen bruikbare betekenis hebben voor zoekopdrachten op trefwoorden.

Meestal heeft u een specifiek samengestelde-lijst met stopwoorden nodig aan de behoeften van uw toepassing. Als u nooit verwacht dat gebruikers de termen 'rood', 'wit' of 'blauw' in hun zoekopdrachten opnemen of als deze termen in elke voorkomen doorzoekbare record, wilt u ze toevoegen aan uw lijst met stopwoorden.

Zie de opmerking aan het einde van dit bericht voor instructies over het gebruik van uw eigen lijst met stopwoorden in MySQL.

Zie ook:

Stap 2. Bouw de omgekeerde index

Om een ​​geïnverteerde index van uw bestaande records te maken, moet u (pseudo-code):

    foreach( word(w) in record(r) ) {
      if(w is not in stopwords) {
        if( w does not exist in dictionary) {
          insert w to dictionary at w.id
        }
        insert (r.id, w.id) into inverted_index
      }
    }
Meer over stopwoorden:

In plaats van een specifieke stopwoordenlijst te gebruiken, kan de 'if(w is not in stopwords)'-test andere beslissingen nemen in plaats van of als aanvulling op uw lijst met onaanvaardbare woorden.

Uw toepassing wil mogelijk alle woorden uitfilteren die minder dan 4 tekens lang zijn of alleen opnemen woorden uit een vooraf gedefinieerde set.

Door uw eigen geïnverteerde index te maken, krijgt u veel meer en fijnmaziger controle over zoeken.

Stap 3. Vraag de geïnverteerde index op met SQL

Deze stap hangt echt af van hoe u verwacht dat zoekopdrachten naar uw index worden verzonden.

Als query's 'hard-coded' moeten zijn, kunt u eenvoudig zelf de select-instructie maken of als u door de gebruiker ingevoerde query's moet ondersteunen, moet u de door u gekozen querytaal omzetten in een SQL-instructie (meestal gedaan met behulp van een eenvoudige parser).

Ervan uitgaande dat u alle documenten wilt ophalen die voldoen aan de logische zoekopdracht '(word1 AND word2) OR word3', zou een mogelijke aanpak kunnen zijn:

CREATE TEMPORARY TABLE temp_results ( rec_id int, count int ) AS 
    ( SELECT rec_id, COUNT(rec_id) AS count 
      FROM invert AS I, dict AS D 
      WHERE I.word_id=D.id AND (D.word='word1' OR D.word='word2') 
      GROUP BY I.rec_id 
      HAVING count=2
    ) 
    UNION (
      SELECT rec_id, 1 AS count 
      FROM invert AS I, dict AS D
      WHERE I.word_id=D.id AND D.word='word3'
    );

SELECT DISTINCT rec_id FROM temp_results;

DROP TABLE temp_results;

OPMERKING:dit is slechts een eerste poging uit mijn hoofd. Ik ben ervan overtuigd dat er efficiëntere manieren zijn om een ​​booleaanse query-expressie om te zetten in een efficiënte SQL-instructie en verwelkom alle suggesties voor verbetering.

Als u naar woordgroepen wilt zoeken, moet u een veld aan de geïnverteerde index toevoegen om de positie van het woord in zijn record weer te geven, en dat in uw SELECT meenemen.

En tot slot moet u uw geïnverteerde index bijwerken als u nieuwe records toevoegt of oude verwijdert.

Laatste woord

"Full text search" valt onder een zeer groot onderzoeksgebied dat bekend staat als "Information Retrieval" of IR en er zijn veel boeken over dit onderwerp, waaronder

Kijk op Amazon voor meer.

Opmerkingen

Hoe u uw eigen lijst met stopwoorden in MySQL kunt gebruiken

Om uw eigen stopwoordenlijst in MySQL te gebruiken:

  1. Maak uw eigen lijst met stopwoorden, één woord per regel, en bewaar deze op een bekende locatie op uw server, bijvoorbeeld:/usr/local/lib/IR/stopwords.txt

  2. Bewerk my.cnf om de volgende regels toe te voegen of bij te werken:
        [mysqld]  
        ft_min_word_len=1    
        ft_max_word_len=40  
        ft_stopword_file=/usr/local/lib/IR/stopwords.txt
    

    die de minimum- en maximumlengte van juridische woorden instelt op respectievelijk 1 en 40, en mysqld vertelt waar u uw aangepaste lijst met stopwoorden kunt vinden.

    (Opmerking:de standaard ft_max_word_len is 84, wat naar mijn mening behoorlijk overdreven is en ertoe kan leiden dat reeksen reeksen worden geïndexeerd die geen echte woorden zijn.)

  3. Herstart mysqld

  4. Laat alle full-text gerelateerde indexen vallen en maak ze opnieuw



  1. Paginering in nodejs met mysql

  2. Wat is een goede manier om alle witruimtetekens van een string in T-SQL zonder UDF en zonder CLR te trimmen?

  3. SOUNDEX() Functie in Oracle

  4. Wat is de betekenis van SELECT... FOR XML PATH(' '),1,1)?