Dlaczego wyszukiwanie pełnotekstowe zwraca mniej wierszy niż LIKE


10

Wyszukiwanie pełnotekstowe nie działa tak, jak chcę, i nie rozumiem różnic w listach wyników.

Przykładowe instrukcje:

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

zwraca 92 wiersze. Otrzymuję wiersze, które pasują do siebie, na przykład „Punkten”, „Zwei-Punkte-Vorsprung” i „Treffpunkt” w kolumnie meldungstext.

Ustawiłem indeks pełnotekstowy w kolumnie „meldungstext” i spróbowałem:

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

zwraca to tylko 8 wierszy. Otrzymuję tylko wiersze, które pasują do samego „Punktu” lub słowa, które moim zdaniem są traktowane jako „Punkt” jak w „i-Punk”.

Następnie wypróbowałem tryb logiczny:

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

zwraca 44 wiersze. Otrzymuję wiersze, które mają „Zwei-Punkte-Vorsprung” lub „Treffpunkt” w kolumnie meldungstext, ale nie te z „Punkten”.

Dlaczego tak się dzieje i jak ustawić „w pełni działające” wyszukiwanie pełnotekstowe, aby zapobiec użyciu LIKE „%%” w klauzuli where?


1
Zasługuje to na dużą +1, ponieważ ten problem nie jest tak naprawdę badany, a indeksowanie FULLTEXT jest często brane za pewnik.
— RolandoMySQLDBA

Odpowiedzi:


13

Wziąłem trzy ciągi z twojego pytania i dodałem je do tabeli plus trzy kolejne z panktzamiast punkt.

Poniższe czynności zostały wykonane przy użyciu MySQL 5.5.12 dla systemu 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>

Uruchomiłem te zapytania do tabeli przy użyciu 3 różnych podejść

  • MATCH ... AGAINST
  • LOCATEjak w funkcji LOCATE
  • LIKE

Zwróć uwagę na różnice

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>

Wszystkie wartości PunktMatch powinny być równe 3 1 i 3 0.

Teraz patrz, jak pytam o nie jak zwykle

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 przy użyciu funkcji PODAJ. PONOWNIE z punktem nie działa. A co z panktem ???

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

mysql>

GROUP BYUruchommy moje duże zapytanie przeciwko 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>

Jest to również złe, ponieważ powinienem zobaczyć 3 0 i 3 1 dla PanktMatch.

Próbowałem czegoś innego

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>

Dodałem znak plus do pankt i uzyskałem różne wyniki. Co 2, a nie 3 ???

Zgodnie z Dokumentacją MySQL zwróć uwagę na to, co mówi o znaku wieloznacznym:

*

Gwiazdka służy jako operator obcięcia (lub symbolu wieloznacznego). W przeciwieństwie do innych operatorów, należy dodać do słowa, którego dotyczy zmiana. Słowa pasują, jeśli zaczynają się od słowa poprzedzającego operator *.

Jeśli słowo zostanie określone za pomocą operatora obcięcia, nie jest usuwane z zapytania boolowskiego, nawet jeśli jest ono zbyt krótkie (jak określono w ustawieniu ft_min_word_len) lub słowo kluczowe. Dzieje się tak, ponieważ słowo nie jest postrzegane jako zbyt krótkie lub słowo kluczowe, ale jako przedrostek, który musi występować w dokumencie w formie słowa rozpoczynającego się od przedrostka. Załóżmy, że ft_min_word_len = 4. Wtedy wyszukiwanie „+ słowo + słowo *” prawdopodobnie zwróci mniej wierszy niż wyszukiwanie „+ słowo + słowo”:

Poprzednie zapytanie pozostaje takie, jakie jest i wymaga, aby zarówno słowo, jak i * (słowo zaczynające się od) były obecne w dokumencie.

To ostatnie zapytanie jest przekształcane na słowo + (wymagające obecności tylko słowa). jest zarówno zbyt krótki, jak i słowo-stop, a każdy z warunków wystarcza, aby go zignorować.

Na tej podstawie znak wieloznaczny ma zastosowanie do tyłu znaczników, a nie do przodu. W świetle tego wynik musi być poprawny, ponieważ 2 z 3 żetonów początkowych punktu. Ta sama historia z panktem. To przynajmniej wyjaśnia, dlaczego 2 z 3 i dlaczego mniej wierszy.


Wow, wielkie dzięki za twoją inwestycję. Oznacza to, że wyszukiwanie pełnotekstowe działa zgodnie z oczekiwaniami lub przynajmniej tak, jak powiedziano w docu. Ale to również stwierdza, że ​​cały problem z pełnym tekstem nie pomoże znaleźć 100% kolumn zawierających dane słowo, co czyni go bezużytecznym dla moich celów. Aby uzyskać dokładne wyniki, musiałbym szukać za pomocą LIKE lub LOCALE, które oprócz zaskakująco oba wydają się być szybsze.
— 32bitfloat

Dlaczego znalazłeś „Punkten”, a @ 32bitfloat nie ?! Zamiast tego znalazł „Treffpunkt”, ale ty nie. I tak naprawdę nie rozumiem, dlaczego „punkt” zwrócił w COUNT(IF(MATCHzapytaniu „Pankten” .
— mgutt

Zastanawiam się, co dzieje się w InnoDB.
— Rick James

Dlaczego masz COUNT(…)w kolumnach PunktMatch i PanktMatch? COUNT(IF(MATCH (meldungstext zawsze) AGAINST ('*pankt*' IN BOOLEAN MODE),1,0)) będzie skutkować , ponieważ się liczy lub wynik z . 110IF(…)
— Quinn Comendant
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.