sql >> Databasteknik >  >> RDS >> Mysql

Hur optimerar man MySQL Boolean Full-Text Search? (Eller vad ska man ersätta det med?) - C#

Först bör du inse att RDBMS-stöd för fulltextindexering är ett hack för att tvinga fram en teknik som är utformad för att möjliggöra effektiv åtkomst till strukturerad data för att hantera ostrukturerad text. (Ja, det är bara mitt åsikt. Om det behövs kan jag försvara det eftersom jag förstår båda teknikerna mycket väl.;)

Så vad kan göras för att förbättra sökresultatet?

Alternativ ett - "Det bästa verktyget för uppgiften"

Det bästa sättet att hantera fulltextsökning inom en korpus av dokument är användningstekniken som är speciellt utformad för att göra det, såsom SOLR (Lucene) från Apache eller Sphinx från fel, Sphinx.

Av skäl som kommer att framgå nedan rekommenderar jag starkt detta tillvägagångssätt.

Alternativ två - Förladda dina resultat

När man konstruerar textbaserade söklösningar är det vanliga tillvägagångssättet att indexera alla dokument till ett enda sökbart index och även om detta kan vara det mest ändamålsenliga, är det inte det enda tillvägagångssättet.

Om du antar att det du söker efter lätt kan kvantifieras till en uppsättning kända regler, kan du erbjuda mer av en "guidad" sökstil än bara okvalificerad fulltext. Vad jag menar med detta är att om din applikation kan ha nytta av att guilda användare till resultat, kan du förinläsa olika uppsättningar resultat baserade på en känd uppsättning regler i deras egna tabeller och på så sätt minska mängden data som ska sökas.

Om du förväntar dig att en majoritet av dina användare kommer att dra nytta av en känd uppsättning söktermer i en känd ordning, kan du konstruera ditt sökgränssnitt så att det gynnar dessa termer.

Så förutsatt att en majoritet av användare letar efter en mängd olika bilar, kan du erbjuda fördefinierade sökningar baserat på modell, år, skick etc. Ditt användargränssnitt för sökning skulle utformas som en serie rullgardinsmenyer för att "vägleda" användare till specifika resultat.

Eller om en majoritet av sökningarna kommer att vara för ett specifikt huvudämne (säg "bilar") kan du fördefiniera en tabell över endast de poster som du tidigare har identifierat som relaterade till bilar.

Båda dessa tillvägagångssätt skulle minska antalet poster som ska genomsökas och därmed öka svarstiderna.

Alternativ tre - "Rulla din egen"

Om du inte kan integrera en extern sökteknik i ditt projekt och förladdning inte är ett alternativ, finns det fortfarande sätt att avsevärt förbättra svarstider för sökfrågor, men de skiljer sig beroende på vad du behöver åstadkomma och hur du förväntar dig att sökningar ska utföras .

Om du förväntar dig att användare ska söka med enstaka nyckelord eller fraser och booleska relationer mellan dem, kan du överväga att skapa din egen "inverterat index ' av din korpus. (Det här är vad MySQL:s booleska fulltextsökning redan gör, men att göra det själv ger större kontroll över både sökningens hastighet och noggrannhet.)

Så här bygger du ett inverterat index från dina befintliga data:

Steg 1. Skapa tre tabeller

    // 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  
    )

Obs:Det här är bara en skiss. Du kommer att vilja lägga till index och begränsningar, etc. när du faktiskt skapar dessa tabeller.

Stoppordstabellen används för att minska storleken på ditt index till endast de ord som har betydelse för användarnas förväntade frågor. Till exempel är det sällan användbart att indexera engelska artiklar, som 'a', 'an', 'the', eftersom de inte bidrar med användbar betydelse till nyckelordssökningar.

Vanligtvis behöver du en stoppordslista som är specifikt utformad till behoven i din ansökan. Om du aldrig förväntar dig att användare ska inkludera termerna "röd", "vit" eller "blå" i sina frågor eller om dessa termer förekommer i varje sökbar post, skulle du vilja lägga till dem i din stoppordslista.

Se noteringen i slutet av detta meddelande för instruktioner om hur du använder din egen stoppordslista i MySQL.

Se även:

Steg 2. Bygg det inverterade indexet

För att bygga ett inverterat index från dina befintliga poster måste du (pseudokod):

    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
      }
    }
Mer om stoppord:

Istället för att använda en specifik stoppordslista kan testet 'if(w är inte i stoppord)' fatta andra beslut antingen istället för eller som ett komplement till din lista över oacceptabla ord.

Din applikation kanske vill filtrera bort alla ord som är kortare än 4 tecken eller bara inkludera ord från en fördefinierad uppsättning.

Genom att skapa ditt eget inverterade index får du mycket större och finare kontroll över sökningen.

Steg 3. Fråga det inverterade indexet med SQL

Det här steget beror verkligen på hur du förväntar dig att frågor ska skickas till ditt index.

Om frågor ska vara "hårdkodade" kan du helt enkelt skapa select-satsen själv eller om du behöver stödja användarinmatade frågor, måste du konvertera vilket frågespråk du än väljer till en SQL-sats (som vanligtvis görs med en enkel parser).

Om du antar att du vill hämta alla dokument som matchar den logiska frågan "(ord1 OCH ord2) ELLER ord3", kan ett möjligt tillvägagångssätt vara:

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;

OBS:Detta är bara ett första pass från toppen av mitt huvud. Jag är övertygad om att det finns mer effektiva sätt att konvertera ett booleskt frågeuttryck till en effektiv SQL-sats och välkomnar alla förslag till förbättringar.

För att söka efter fraser måste du lägga till ett fält i det inverterade indexet för att representera positionen som ordet förekom i dess post och ta med det i ditt SELECT.

Och slutligen måste du uppdatera ditt inverterade index när du lägger till nya poster eller tar bort gamla.

Slutord

"Fulltextsökning" faller under ett mycket stort forskningsområde känt som "Informationshämtning" eller IR och det finns många böcker om ämnet, inklusive

Kolla Amazon för mer.

Anteckningar

Hur du använder din egen lista över stoppord i MySQL

För att använda din egen stoppordslista i MySQL:

  1. Skapa din egen lista med stoppord, ett ord per rad, och spara den på en känd plats på din server, säg:/usr/local/lib/IR/stopwords.txt

  2. Redigera my.cnf för att lägga till eller uppdatera följande rader:
        [mysqld]  
        ft_min_word_len=1    
        ft_max_word_len=40  
        ft_stopword_file=/usr/local/lib/IR/stopwords.txt
    

    som kommer att ställa in den minsta och maximala längden på juridiska ord till 1 respektive 40, och tala om för mysqld var du kan hitta din anpassade lista med stoppord.

    (Obs:standard ft_max_word_len är 84, vilket jag tror är ganska överdrivet och kan göra att körningar av strängar som inte är riktiga ord indexeras.)

  3. Starta om mysqld

  4. Släpp och återskapa alla fulltextrelaterade index



  1. Syftet med att använda olika typer av PL/SQL-samlingar i Oracle

  2. Åtgärder att ta om du har ett MySQL-avbrott

  3. Hur initierar man mysql-behållare när den skapas på Kubernetes?

  4. Fråga till ORDER BY antalet rader som returneras från en annan SELECT