2013-07-03 4 views
7

Ho una query di ricerca che esegue una ricerca di testo completo sul DB.Ricerca full text MySQL su più colonne: confusione dei risultati

$sql = "SELECT 
* 
FROM 
`tbl_auction_listing` AS `al` 
JOIN 
`tbl_user` AS `u` ON `al`.`user_id` = `u`.`user_id` 
LEFT JOIN 
`tbl_gallery_details` AS `gd` ON `al`.`user_id` = `gd`.`user_id` 
LEFT JOIN 
`tbl_self_represented_details` AS `sr` ON `u`.`user_id` = `sr`.`user_id` 
WHERE 
`al`.`status` = '" . ACTIVE . "' 
AND 
`al`.`start_date` < NOW() 
AND 
`al`.`end_date` > NOW() 
AND 
MATCH(`al`.`listing_title`, 
`al`.`description`, 
`al`.`provenance`, 
`al`.`title`, 
`al`.`artist_full_name`, 
`al`.`artist_first_name`, 
`al`.`artist_last_name`, 
`sr`.`artist_name`, 
`gd`.`gallery_name`, 
`u`.`username`) AGAINST('$search_query' IN BOOLEAN MODE)"; 

Quando cerco 'Cardozo, Horacio' o 'Cardozo' o 'Horacio' ottengo alcun risultato però so che c'è un artista con 2 record nel DB con artist_full_name = Cardozo, Horacio.

Se rimuovo tutti i campi MATCH e ho solo al. artist_full_name Ottengo 2 risultati. Se aggiungo al. description Ottengo 1 risultato perché 'Horacio Cardozo' esiste nella descrizione.

C'è un modo per fare in modo che la ricerca restituisca tutti i record se qualche condizione (qualsiasi parola di ricerca) è soddisfatta in uno dei campi MATCH? Ho provato a rimuovere IN BOOLEAN MODE ma questo ha prodotto gli stessi risultati.

+0

Invece di 'IN MODALITÀ BOOLEAN', provare' IN MODALITÀ LINGUA NATURALE' –

+0

'IN MODALITÀ LINGUA NATURALE' è la modalità predefinita (ad esempio, quando non viene specificata alcuna modalità) – RandomSeed

+0

produce ancora 0 risultati durante la ricerca.Ho controllato che tutti i campi in DB fossero fulltext ma ancora nulla. – puks1978

risposta

15

Sembra che le tabelle InnoDB non consentano ricerche su più indici di testo completo nella stessa condizione MATCH().

Qui i campi non appartengono tutti alla stessa tabella, pertanto sono coperti da diversi indici. Si noti la stessa limitazione si applica se si ha una tabella come questa:

CREATE TABLE t (
    f1 VARCHAR(20), 
    f2 VARCHAR(20), 
    FULLTEXT(f1), FULLTEXT(f2) 
) ENGINE=InnoDB; 

SELECT * FROM t 
WHERE MATCH(f1, f2) AGAINST ('something in f2'); -- likely to return no row 

E sembra una ricerca full-text può consultare soltanto il primo indice full-text che incontra Ma questa è solo una cosa che detrarre from this experience, si prega di non dare per scontato questo.

La linea di fondo è che si dovrebbe dividere la ricerca in modo da utilizzare un unico indice full-text per MATCH() clausola:

SELECT * FROM auction, user, gallery, ... 
WHERE 
    MATCH(auction.field1, auction.field2) AGAINST ('search query' IN BOOLEAN MODE) OR 
    MATCH(auction.field3) AGAINST ('search query' IN BOOLEAN MODE) OR 
    MATCH(user.field1, user.field2, user.field3) AGAINST... 

Questo è un esempio di una possibile query se tu avessi due indici distinti su auction e uno uno su user. Devi adattarlo alla tua struttura reale (per favore pubblica le descrizioni dei tuoi tavoli se hai bisogno di più indicazioni).

Avviso questo si applica solo alle tabelle InnoDB. È interessante notare che le tabelle MyISAM do not seem to show the same limitation.


Aggiornamento: si scopre questo era a bug in the InnoDB engine, fissati in 5.6.13/5.7.2. L'esempio sopra ora giustamente fallisce con "Impossibile trovare l'indice FULLTEXT che corrisponde all'elenco delle colonne". Infatti, non esiste un indice su (f1, f2), ma uno su (f1) e un altro su (f2). As the changelog advises:

differenza MyISAM, InnoDB non supporta ricerche full-text booleane su colonne non indicizzati, ma questa restrizione non è stato applicato, con conseguente nelle query che hanno restituito risultati non corretti.

È interessante notare che, mentre tali query restituiscono un risultato corretto set con MyISAM, corrono più lento rispetto ci si potrebbe aspettare, come they silently ignore existing fulltext indexes.

+0

Posso usare * per ottenere qualcosa dopo quella parola, ma c'è un modo per ottenere parole in cui la query di ricerca potrebbe iniziare nel mezzo della parola? Ad esempio: query = risultati di ritorno semplici 'apple' – puks1978

+0

È possibile utilizzare una condizione di ricerca come: '... WHERE campo LIKE '% pple'' ('% 'è un carattere jolly) ma tale query non può utilizzare un indice di testo completo (né un indice normale). "[MySQL non può usare un indice se le colonne non formano il prefisso più a sinistra dell'indice] (http://dev.mysql.com/doc/refman/5.6/en/mysql-indexes.html)" (questa affermazione è inizialmente inteso a descrivere indici multi-colonna, ma è davvero la stessa idea per un indice parziale). – RandomSeed

+0

Il tuo suggerimento di utilizzare più corrispondenze 'match ... or match ...' nella clausola 'where' sembra impedire a myisam di usare anche l'indice. Le query indipendenti con un 'union' funzionano meglio. E, g, 'select * da rsspodcastitems dove match title contro (" verdure ") o match sottotitoli contro (" verdure ") -> 16 righe in set (2.46 sec)' considerando 'select * da rsspodcastitems dove match title contro ("verdure") union select * da rsspodcastitems dove match sottotitolo contro ("verdure") -> 16 righe in set (0.02 sec) '. – Jules