Perché la ricerca full-text restituisce meno righe di LIKE


10

Non riesco a far funzionare la ricerca full-text come voglio, e non capisco le differenze nelle liste dei risultati.

Esempi di dichiarazioni:

SELECT `meldungstext`
FROM `artikel`
WHERE `meldungstext` LIKE '%punkt%'

restituisce 92 righe. Ricevo righe che hanno corrispondenze, ad esempio, come "Punkten", "Zwei-Punkte-Vorsprung" e "Treffpunkt" nella colonna meldungstext.

Ho impostato un indice full-text sulla colonna "meldungstext" e ho provato questo:

SELECT `meldungstext`
FROM `artikel`
WHERE MATCH (`meldungstext`)
AGAINST ('*punkt*')

questo restituisce solo 8 righe. Ricevo solo righe che corrispondono a "Punkt" stesso o parole che penso siano prese come "Punkt" come in "i-Punkt".

Ho quindi provato la modalità booleana:

SELECT `meldungstext`
FROM `artikel`
WHERE MATCH (`meldungstext`)
AGAINST ('*punkt*' IN BOOLEAN MODE)

restituisce 44 righe. Ricevo righe che contengono "Zwei-Punkte-Vorsprung" o "Treffpunkt" nella colonna meldungstext, ma non quelle con "Punkten".

Perché ciò accade e come posso impostare una ricerca full-text "completamente" funzionante per impedire l'uso di LIKE '%%' nella clausola where?


1
Questo merita un grande +1 perché questo problema non è realmente esaminato e l'indicizzazione FULLTEXT è spesso data per scontata.
RolandoMySQLDBA

Risposte:


13

Ho preso le tre stringhe nella tua domanda e l'ho aggiunta a una tabella più altre tre con panktinvece di punkt.

Quanto segue è stato eseguito utilizzando MySQL 5.5.12 per Windows

mysql> CREATE TABLE artikel
    -> (
    ->     id INT NOT NULL AUTO_INCREMENT,
    ->     meldungstext MEDIUMTEXT,
    ->     PRIMARY KEY (id),
    ->     FULLTEXT (meldungstext)
    -> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.03 sec)

mysql> INSERT INTO artikel (meldungstext) VALUES
    -> ('Punkten'),('Zwei-Punkte-Vorsprung'),('Treffpunkt'),
    -> ('Pankten'),('Zwei-Pankte-Vorsprung'),('Treffpankt');
Query OK, 6 rows affected (0.00 sec)
Records: 6  Duplicates: 0  Warnings: 0

mysql>

Ho eseguito queste query sul tavolo usando 3 approcci diversi

  • MATCH ... AGAINST
  • LOCATEcome nella funzione LOCATE
  • LIKE

Si prega di notare le differenze

mysql> SELECT id,meldungstext,
    -> COUNT(IF(MATCH (`meldungstext`) AGAINST ('*punkt*' IN BOOLEAN MODE),1,0)) PunktMatch,
    -> IF(LOCATE('punkt',meldungstext)>0,1,0) PunktLocate,
    -> meldungstext  LIKE '%punkt%' PunktLike
    -> FROM `artikel` GROUP BY id,meldungstext;
+----+-----------------------+------------+-------------+-----------+
| id | meldungstext          | PunktMatch | PunktLocate | PunktLike |
+----+-----------------------+------------+-------------+-----------+
|  1 | Punkten               |          1 |           1 |         1 |
|  2 | Zwei-Punkte-Vorsprung |          1 |           1 |         1 |
|  3 | Treffpunkt            |          1 |           1 |         1 |
|  4 | Pankten               |          1 |           0 |         0 |
|  5 | Zwei-Pankte-Vorsprung |          1 |           0 |         0 |
|  6 | Treffpankt            |          1 |           0 |         0 |
+----+-----------------------+------------+-------------+-----------+
6 rows in set (0.01 sec)

mysql>

Tutti i valori PunktMatch dovrebbero essere 3 1 e 3 0.

Ora guardami interrogarli normalmente

mysql> SELECT `meldungstext` FROM `artikel`
    -> WHERE MATCH (`meldungstext`) AGAINST ('*punkt*' IN BOOLEAN MODE);
+-----------------------+
| meldungstext          |
+-----------------------+
| Zwei-Punkte-Vorsprung |
| Punkten               |
+-----------------------+
2 rows in set (0.01 sec)

mysql> SELECT `meldungstext` FROM `artikel`
    -> WHERE LOCATE('punkt',meldungstext)>0;
+-----------------------+
| meldungstext          |
+-----------------------+
| Punkten               |
| Zwei-Punkte-Vorsprung |
| Treffpunkt            |
+-----------------------+
3 rows in set (0.00 sec)

mysql> SELECT `meldungstext` FROM `artikel`
    -> WHERE `meldungstext` LIKE '%punk%';
+-----------------------+
| meldungstext          |
+-----------------------+
| Punkten               |
| Zwei-Punkte-Vorsprung |
| Treffpunkt            |
+-----------------------+
3 rows in set (0.00 sec)

mysql>

OK usando MATCH .. CONTRO con punkt non funziona. Che dire di pankt ???

mysql> SELECT `meldungstext` FROM `artikel` WHERE `meldungstext` LIKE '%pankt%';
+-----------------------+
| meldungstext          |
+-----------------------+
| Pankten               |
| Zwei-Pankte-Vorsprung |
| Treffpankt            |
+-----------------------+
3 rows in set (0.00 sec)

mysql>

GROUP BYEseguiamo la mia grande query contro pankt

mysql> SELECT id,meldungstext,
    -> COUNT(IF(MATCH (`meldungstext`) AGAINST ('*pankt*' IN BOOLEAN MODE),1,0)) PanktMatch,
    -> IF(LOCATE('pankt',meldungstext)>0,1,0) PanktLocate,
    -> meldungstext  LIKE '%pankt%' PanktLike
    -> FROM `artikel` GROUP BY id,meldungstext;
+----+-----------------------+------------+-------------+-----------+
| id | meldungstext          | PanktMatch | PanktLocate | PanktLike |
+----+-----------------------+------------+-------------+-----------+
|  1 | Punkten               |          1 |           0 |         0 |
|  2 | Zwei-Punkte-Vorsprung |          1 |           0 |         0 |
|  3 | Treffpunkt            |          1 |           0 |         0 |
|  4 | Pankten               |          1 |           1 |         1 |
|  5 | Zwei-Pankte-Vorsprung |          1 |           1 |         1 |
|  6 | Treffpankt            |          1 |           1 |         1 |
+----+-----------------------+------------+-------------+-----------+
6 rows in set (0.01 sec)

mysql>

Questo è sbagliato anche perché dovrei vedere 3 0 e 3 1 per PanktMatch.

Ho provato qualcos'altro

mysql> SELECT id,meldungstext, MATCH (`meldungstext`) AGAINST ('+*pankt*' IN BOOLEAN MODE) PanktMatch, IF(LOCATE('pankt',meldungstext)>0,1,0) PanktLocate, meldungstext  LIKE '%pankt%' PanktLike FROM `artikel` GROUP BY id,meldungstext;
+----+-----------------------+------------+-------------+-----------+
| id | meldungstext          | PanktMatch | PanktLocate | PanktLike |
+----+-----------------------+------------+-------------+-----------+
|  1 | Punkten               |          0 |           0 |         0 |
|  2 | Zwei-Punkte-Vorsprung |          0 |           0 |         0 |
|  3 | Treffpunkt            |          0 |           0 |         0 |
|  4 | Pankten               |          1 |           1 |         1 |
|  5 | Zwei-Pankte-Vorsprung |          1 |           1 |         1 |
|  6 | Treffpankt            |          0 |           1 |         1 |
+----+-----------------------+------------+-------------+-----------+
6 rows in set (0.00 sec)

mysql>

Ho aggiunto un segno più a pankt e ho ottenuto risultati diversi. Cosa 2 e non 3 ???

Secondo la documentazione MySQL , nota cosa dice sul carattere jolly:

*

L'asterisco funge da operatore di troncamento (o jolly). A differenza degli altri operatori, dovrebbe essere aggiunto alla parola interessata. Le parole corrispondono se iniziano con la parola che precede l'operatore *.

Se una parola viene specificata con l'operatore di troncamento, non viene rimossa da una query booleana, anche se è troppo breve (come determinato dall'impostazione ft_min_word_len) o da una parola d'ordine. Ciò si verifica perché la parola non è vista come troppo breve o una parola d'ordine, ma come un prefisso che deve essere presente nel documento sotto forma di una parola che inizia con il prefisso. Supponiamo che ft_min_word_len = 4. Quindi una ricerca di '+ word + the *' probabilmente restituirà meno righe di una ricerca di '+ word + the':

La query precedente rimane così com'è e richiede che sia la parola sia * (una parola che inizia con il) siano presenti nel documento.

Quest'ultima query viene trasformata in + parola (richiede solo la parola per essere presente). è sia troppo breve che una parola d'ordine, e una delle due condizioni è sufficiente per farla ignorare.

Sulla base di questo, il carattere jolly è applicabile per il retro dei token e non per il fronte. Alla luce di ciò, l'output deve essere corretto perché 2 dei token di avvio di 3 punti. Stessa storia con pankt. Questo almeno spiega perché 2 su 3 e perché meno righe.


Wow, molte grazie per il tuo investimento. Questo significa che la ricerca full-text funziona come previsto, o almeno come detto nel documento. Ma questo afferma anche che l'intera questione full-text non aiuterà a trovare il 100% delle colonne che includono una determinata parte di parole, il che la rende inutile per i miei scopi. Per ottenere risultati esatti, dovrei cercare con LIKE o LOCALE, che a parte sorprendentemente entrambi sembrano essere più veloci.
32bitfloat,

Perché hai trovato "Punkten" e @ 32bitfloat no ?! Invece ha trovato "Treffpunkt", ma tu no. E non capisco davvero perché "punkt" abbia restituito "Pankten" nella COUNT(IF(MATCHquery.
mgutt,

Mi chiedo cosa succede in InnoDB.
Rick James,

Perché hai COUNT(…)le colonne PunktMatch e PanktMatch? COUNT(IF(MATCH (meldungstext ) AGAINST ('*pankt*' IN BOOLEAN MODE),1,0))si tradurrà sempre in 1, perché sta contando 1o 0, il risultato dal IF(…).
Quinn Comendant,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.