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

Come ottimizzare la ricerca full-text booleana MySQL? (O con cosa sostituirlo?) - C#

Innanzitutto, dovresti renderti conto che il supporto RDBMS per l'indicizzazione del testo completo è un trucco per forzare una tecnologia progettata per consentire un accesso efficiente ai dati strutturati per gestire il testo non strutturato. (Sì, è solo mio opinione. Se necessario, posso difenderlo poiché capisco molto bene entrambe le tecnologie.;)

Quindi, cosa si può fare per migliorare le prestazioni di ricerca?

Opzione uno - "Lo strumento migliore per il compito"

Il modo migliore per gestire la ricerca full-text all'interno di un corpus di documenti è l'utilizzo di una tecnologia specificamente progettata per farlo, come SOLR (Luce) da Apache o Sphinx da ehm, Sfinge.

Per i motivi che risulteranno chiari di seguito, consiglio vivamente questo approccio.

Opzione due:precarica i risultati

Quando si costruiscono soluzioni di ricerca basate su testo, l'approccio usuale consiste nell'indicizzare tutti i documenti in un unico indice ricercabile e, sebbene questo possa essere il più conveniente, non è l'unico approccio.

Supponendo che ciò che stai cercando possa essere facilmente quantificato in un insieme di regole note, potresti offrire uno stile di ricerca più "guidato" rispetto al semplice testo completo non qualificato. Ciò che intendo dire con questo è che, se la tua applicazione potrebbe trarre vantaggio dall'associare gli utenti ai risultati, puoi precaricare vari insiemi di risultati in base a un insieme noto di regole nelle proprie tabelle, e quindi ridurre la maggior parte dei dati da cercare.

Se prevedi che la maggior parte dei tuoi utenti trarrà vantaggio da un insieme noto di termini di ricerca in un ordine noto, puoi creare la tua interfaccia utente di ricerca per favorire quei termini.

Quindi, supponendo che la maggior parte degli utenti stia cercando una varietà di automobili, potresti offrire ricerche predefinite basate su modello, anno, condizione, ecc. La tua interfaccia utente di ricerca sarebbe realizzata come una serie di menu a discesa per "guidare" gli utenti verso risultati specifici.

Oppure, se la maggior parte delle ricerche riguarderà un argomento principale specifico (ad esempio "automobili"), potresti predefinire una tabella contenente solo i record che hai precedentemente identificato come correlati alle automobili.

Entrambi questi approcci ridurrebbero il numero di record da cercare e quindi aumenterebbero i tempi di risposta.

Opzione tre - "Rolla da solo"

Se non puoi integrare una tecnologia di ricerca esterna nel tuo progetto e il precaricamento non è un'opzione, ci sono ancora modi per migliorare notevolmente i tempi di risposta alle query di ricerca, ma differiscono in base a ciò che devi realizzare e al modo in cui prevedi che le ricerche vengano eseguite .

Se prevedi che gli utenti effettuino ricerche utilizzando singole parole chiave o frasi e relazioni booleane tra di loro, potresti prendere in considerazione la creazione del tuo 'indice invertito ' del tuo corpus. (Questo è ciò che fa già la ricerca full-text booleana di MySQL, ma farlo da soli consente un maggiore controllo sia sulla velocità che sull'accuratezza della ricerca.)

Per creare un indice invertito dai tuoi dati esistenti:

Passaggio 1. Crea tre tabelle

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

Nota:questo è solo uno schizzo. Ti consigliamo di aggiungere indici e vincoli, ecc. quando crei effettivamente queste tabelle.

La tabella delle parole non significative viene utilizzata per ridurre la dimensione dell'indice alle sole parole che contano per le query previste degli utenti. Ad esempio, raramente è utile indicizzare articoli in inglese, come 'a', 'an', 'the', poiché non forniscono un significato utile alle ricerche di parole chiave.

In genere, avrai bisogno di un elenco di parole non significative creato appositamente alle esigenze della tua applicazione. Se non ti aspetti che gli utenti includano i termini "rosso", "bianco" o "blu" nelle loro query o se questi termini compaiono in ogni record ricercabile, vorresti aggiungerli al tuo elenco di stopword.

Vedere la nota alla fine di questo messaggio per istruzioni sull'utilizzo del proprio elenco di stopword in MySQL.

Vedi anche:

Passaggio 2. Crea l'indice invertito

Per creare un indice invertito dai record esistenti, dovrai (pseudo-codice):

    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
      }
    }
Altro sulle parole d'ordine:

nvece di utilizzare un elenco di parole non significative specifico, il test 'if(w non è nelle parole non significative)' potrebbe prendere altre decisioni al posto o in aggiunta al tuo elenco di parole non accettabili.

La tua applicazione potrebbe voler filtrare tutte le parole con meno di 4 caratteri o solo includere parole da un insieme predefinito.

Creando il tuo indice invertito, ottieni un controllo molto maggiore e dettagliato sulla ricerca.

Passaggio 3. Interroga l'indice invertito utilizzando SQL

Questo passaggio dipende davvero da come ti aspetti che le query vengano inviate al tuo indice.

Se le query devono essere "codificate", puoi semplicemente creare tu stesso l'istruzione select o se hai bisogno di supportare le query immesse dall'utente, dovrai convertire qualsiasi linguaggio di query tu scelga in un'istruzione SQL (in genere eseguita utilizzando un semplice analizzatore).

Supponendo che desideri recuperare tutti i documenti che corrispondono alla query logica '(parola1 AND parola2) OR parola3', un possibile approccio potrebbe essere:

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;

NOTA:questo è solo un primo passaggio dalla parte superiore della mia testa. Sono sicuro che esistono modi più efficienti per convertire un'espressione di query booleana in un'efficiente istruzione SQL e accolgo con favore tutti i suggerimenti per il miglioramento.

Per cercare le frasi, dovrai aggiungere un campo all'indice invertito per rappresentare la posizione in cui è apparsa la parola all'interno del suo record e tenerlo in considerazione nel tuo SELECT.

Infine, dovrai aggiornare il tuo indice invertito man mano che aggiungi nuovi record o elimini quelli vecchi.

Ultima parola

La "ricerca di testo completo" rientra in un'area di ricerca molto ampia nota come "Recupero delle informazioni" o IR e ci sono molti libri sull'argomento, tra cui

Controlla Amazon per ulteriori informazioni.

Note

Come utilizzare il tuo elenco di stopword in MySQL

Per utilizzare il tuo elenco di stopword in MySQL:

  1. Crea il tuo elenco di stopword, una parola per riga, e salvalo in una posizione nota sul tuo server, ad esempio:/usr/local/lib/IR/stopwords.txt

  2. Modifica my.cnf per aggiungere o aggiornare le seguenti righe:
        [mysqld]  
        ft_min_word_len=1    
        ft_max_word_len=40  
        ft_stopword_file=/usr/local/lib/IR/stopwords.txt
    

    che imposterà la lunghezza minima e massima delle parole legali su 1 e 40, rispettivamente, e dirà a mysqld dove trovare il tuo elenco personalizzato di stopword.

    (Nota:il valore predefinito ft_max_word_len è 84, che ritengo sia piuttosto eccessivo e può causare l'indicizzazione di sequenze di stringhe che non sono parole reali.)

  3. Riavvia mysqld

  4. Rilascia e ricrea tutti gli indici relativi al testo completo