2013-04-15 17 views
5

Facciamo finta che ho questi i titoli delle pagine nel mio wiki (MediaWiki 1.19.4):Come eseguire una ricerca con accento e senza distinzione tra maiuscole e minuscole nel database MediaWiki?

SOMETHIng 
Sómethìng 
SomêthÏng 
SÒmetHínG 

Se un utente cerca something voglio che tutte le 4 pagine vengono restituiti come risultato.

Al momento l'unica cosa che potevo pensare è questa query (MySQL Percona 5.5.30-30.2):

SELECT page_title 
FROM page 
WHERE page_title LIKE '%something%' COLLATE utf8_general_ci 

che restituisce solo SOMETHIng.

devo essere sulla strada giusta, perché se cerco sóméthíng O SÓMÉTHÍNG, ottengo SOMETHIng come risultato. Come posso modificare la query in modo da ottenere gli altri risultati come previsto? La prestazione non è critica in quanto la tabella page contiene solo ~ righe da 2 K.

Questa è la definizione della tabella con i corrispondenti bit:

CREATE TABLE page (
    (...) 
    page_title VARCHAR(255) NOT NULL DEFAULT '' COLLATE latin1_bin, 
    (...) 
    UNIQUE INDEX name_title (page_namespace, page_title), 
) 

La tabella di definizione non deve essere modificato, in quanto si tratta di un'installazione magazzino di MediaWiki e AFAIK suo codice aspetta questo campo essendo definita in questo modo (cioè unicode memorizzato come dati binari).

risposta

3

Ho trovato la soluzione perfetta, senza modificare o creare tabelle. Lo potrebbe avere implicazioni sulle prestazioni (non ho provato), ma come ho affermato nella mia domanda, è una tabella di ~ 2K righe quindi non dovrebbe avere molta importanza.

La radice del problema è che MediaWiki memorizza il testo con codifica UTF8 in tabelle con codifica latin1. Non importa molto a MediaWiki poiché ne è a conoscenza e interrogherà sempre il database con il set di caratteri corretto e farà il suo dovere, essenzialmente usando MySQL come contenitore di bit stupido. Lo fa perché il supporto apparentemente UTF8 in MySQL non è adeguato alle sue esigenze (vedere i commenti in MediaWiki DefaultSettings.php, variabile $wgDBmysql5).

Il problema si presenta quando si desidera che il database stesso sia in grado di eseguire operazioni UTF8-aware (come volevo fare nella mia domanda). Non sarai in grado di farlo perché per quanto ne sa MySQL, non sta memorizzando il testo con codifica UTF8 (anche se lo è, come spiegato nel paragrafo precedente).

C'è una soluzione ovvia per questo: trasmettere a UTF8 la colonna che si desidera utilizzare, qualcosa come questo CONVERT(col_name USING utf8). Il problema è che MySQL sta cercando di essere pericolosamente utile: pensa che col_name stia memorizzando testo con latin1 e tradurrà (non codificherà) ogni byte nel suo equivalente UTF8, e terminerai con UTF8 con doppia codifica, che è ovviamente sbagliato.

Come evitare che MySQL sia così bello e utile? Basta inserire in BINARY prima del facendo la conversione in UTF8! In questo modo MySQL non assumerà nulla e farà esattamente come richiesto: codifica questo gruppo di bit in UTF8. La sintassi esatta è CONVERT(CAST(col_name AS BINARY) USING utf8).

Quindi questa è la mia domanda finale ora:

SELECT CONVERT(CAST(page_title AS BINARY) USING utf8) 
FROM page 
WHERE 
    CONVERT(CAST(page_title AS BINARY) USING utf8) 
     LIKE '%keyword_here%' 
      COLLATE utf8_spanish_ci 

Ora se cerco something o sôMëthîNG o qualsiasi variazione, ottengo tutti i risultati!

Si prega di notare che ho usato utf8_spanish_ci perché voglio la ricerca di differenziare ñ da n ma non á da a. Utilizzare un confronto diverso in base al proprio caso d'uso (here is a complete list).

Link correlati:

+1

ben spiegato, grazie per averlo condiviso. Lo so, le prestazioni nel tuo caso non sono cruciali, ma sarebbe interessante sapere quale impatto ha. – agim

+1

@agim: A occhio e croce, con una query come ''% keyword%'' non dovrebbe esserci molta differenza, dal momento che MySQL deve eseguire comunque una scansione della tabella (o almeno una scansione dell'indice). Tuttavia, una query come ''prefix%'' dovrebbe essere molto più veloce se la colonna è stata correttamente fascicolata per cominciare. –

1

caso insensitive: si può semplicemente lasciare che il database di fare il lavoro per voi (che già fa con _ci)

Accenti: Al fine di avere tutti gli accenti o accenti almeno tutte conosciute potresti usare due righe nel tuo database. La prima riga memorizza il risultato così com'è (significa che si memorizza SomêthÏng) e si crea inoltre un secondo search_row che in questo caso contiene la stringa qualcosa (senza accenti). Per la conversione puoi creare una funzione con le regole di sostituzione.

Ora è possibile convertire la stringa di ricerca utilizzando la funzione di conversione.

L'ultimo passo è, fate un trigger, che riempie/aggiorna il campo search_row ogni volta che si inserisce/aggiornare il documento nella tabella di pagina di .

Questa soluzione non avrebbe alcun impatto negativo sulle prestazioni!

+0

sembra un po 'contorto, ma perfettamente valido immagino.Ma idealmente, vorrei evitare di modificare il database se possibile, si tratta di un'installazione di serie e dovrebbe rimanere in questo modo per evitare problemi di manutenzione. –

+0

Ho proposto questa soluzione, poiché è indipendente dalla lingua. Se userai una sola lingua, ecco una soluzione per le dieresi tedesche: http://stackoverflow.com/questions/2722044/mysql-german-accents-not-sensitive-search-in-full-text-searches Per quanto ne so, l'utilizzo di accenti da lingue diverse causa sempre problemi – agim

3

MediaWiki TitleKey extension è fondamentalmente progettato per questo, ma funziona solo in caso di piegatura. Tuttavia, se non ti dispiace l'hacking un po ', e hanno il PHP iconv extension installato, è possibile modificare TitleKey_body.php e sostituire il metodo:

static function normalize($text) { 
    global $wgContLang; 
    return $wgContLang->caseFold($text); 
} 

con ad esempio:

static function normalize($text) { 
    return strtoupper(iconv('UTF-8', 'US-ASCII//TRANSLIT', $text)); 
} 

e (ri) eseguire rebuildTitleKeys.php.

L'estensione TitleKey memorizza i titoli normalizzati in un separate table, dal nome sorprendente titlekey. È previsto l'accesso tramite l'interfaccia di ricerca di MediaWiki, ma se lo desideri, puoi sicuramente effettuare una query direttamente, ad es. così:

SELECT page.* FROM page 
    JOIN titlekey ON tk_page = page_id 
WHERE tk_namespace = 0 AND tk_key = 'SOMETHING'; 
Problemi correlati