2010-04-28 11 views
14

Ich habe eine ~ 300.000 Reihentabelle; Dazu gehören technische Begriffe; mit PHP und MySQL + FULLTEXT Indizes abgefragt. Aber wenn ich einen falsch eingegebenen Begriff suche; zum Beispiel "hyperpext"; natürlich keine Ergebnisse."Meinten Sie" Funktion in einer Wörterbuch-Datenbank

Ich muss kleine Schreibfehler "kompilieren" und den nächsten Datensatz aus der Datenbank holen. Wie kann ich ein solches Ziel erreichen? Ich weiß (tatsächlich, heute gelernt) über Levenshtein Entfernung, Soundex und Metaphone Algorithmen, aber derzeit keine solide Idee, dies zu implementieren Abfrage gegen Datenbank.

Mit freundlichen Grüßen. (Tut mir leid, mein schlechtes Englisch, ich versuche, mein Bestes zu tun)

Antwort

11

Siehe in diesem Artikel, wie Sie vielleicht implement Levenshtein distance in a MySQL stored function.

Für die Nachwelt den Vorschlag des Autors liegen, dies zu tun:

CREATE FUNCTION LEVENSHTEIN (s1 VARCHAR(255), s2 VARCHAR(255)) 
    RETURNS INT 
    DETERMINISTIC 
     BEGIN 
     DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT; 
     DECLARE s1_char CHAR; 
     DECLARE cv0, cv1 VARBINARY(256); 
     SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), cv1 = 0x00, j = 1, i = 1, c = 0; 
     IF s1 = s2 THEN 
      RETURN 0; 
     ELSEIF s1_len = 0 THEN 
      RETURN s2_len; 
     ELSEIF s2_len = 0 THEN 
      RETURN s1_len; 
     ELSE 
      WHILE j <= s2_len DO 
      SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1; 
      END WHILE; 
      WHILE i <= s1_len DO 
      SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1; 
      WHILE j <= s2_len DO 
       SET c = c + 1; 
       IF s1_char = SUBSTRING(s2, j, 1) THEN SET cost = 0; ELSE SET cost = 1; END IF; 
       SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost; 
       IF c > c_temp THEN SET c = c_temp; END IF; 
       SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1; 
       IF c > c_temp THEN SET c = c_temp; END IF; 
       SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1; 
      END WHILE; 
      SET cv1 = cv0, i = i + 1; 
      END WHILE; 
     END IF; 
     RETURN c; 
     END 

Er liefert auch eine LEVENSHTEIN_RATIO Hilfsmethode, die das Verhältnis der verschiedenen/Gesamt Zeichen wertet, anstatt eine gerade Editierdistanz. Zum Beispiel, wenn es 60% ist, dann sind drei Fünftel der Zeichen in dem Quellwort verschieden von dem Zielwort.

CREATE FUNCTION LEVENSHTEIN_RATIO (s1 VARCHAR(255), s2 VARCHAR(255)) 
    RETURNS INT 
    DETERMINISTIC 
     BEGIN 
     DECLARE s1_len, s2_len, max_len INT; 
     SET s1_len = LENGTH(s1), s2_len = LENGTH(s2); 
     IF s1_len > s2_len THEN SET max_len = s1_len; ELSE SET max_len = s2_len; END IF; 
     RETURN ROUND((1 - LEVENSHTEIN(s1, s2)/max_len) * 100); 
     END 
+0

Auch für die Nachwelt ist dies Code von Jason Rust, die auf Code von Arnold Fribble basiert, der wiederum teilweise auf Arbeiten von Joseph Gama basiert. – webbiedave

+0

D'oh. Irgendwie dachte ich, ich hätte den Autor erwähnt, aber offensichtlich nicht. Danke für das Ausfüllen der Lücken, @webbiedave. –

+1

Danke für die UDF, es ist sehr nützlich. Aber wenn ich eine Abfrage wie "SELECT * FROM Tabelle WHERE HAVING LEVENSHTEIN ('Schlüsselwort', Feld) <3 (oder so)" in einer ~ 300k Zeile Tabelle, dauert es (offensichtlich) Alter zu vervollständigen. Ich habe auch versucht, die Zeilensuche innerhalb zu reduzieren (mit WHERE CHAR_LENGTH ('field') BETWEEN CHAR_LENGTH ('keyword') - 1 UND CHAR_LENGTH ('keyword') + 1) aber es liefert Ergebnisse in satte 35 Sekunden :) Do you (or andere) haben eine Idee, diese Abfrage zu beschleunigen? – Hazar

1

Aus den Kommentaren der http://dev.mysql.com/doc/refman/5.0/en/udf-compiling.html

jetzt herunterladen i das Paket aus der mysql UDF-Repository http://empyrean.lib.ndsu.nodak.edu/~nem/mysql/

wget http://empyrean.lib.ndsu.nodak.edu/~nem/mysql/udf/dludf.cgi?ckey=28 

ll 

tar -xzvf dludf.cgi\?ckey\=28 

gcc -shared -o libmysqllevenshtein.so mysqllevenshtein.cc -I/usr/include/mysql/ 

mv libmysqllevenshtein.so /usr/lib 

mysql -uroot -pPASS 

mysql> use DATABASE 

mysql> CREATE FUNCTION levenshtein RETURNS INT SONAME 'libmysqllevenshtein.so'; 

mysql> select levenshtein(w1.word,w2.word) as dist from word w1, word w2 where ETC........... order by dist asc limit 0,10; 
-1

Warum nicht eine Spalte der Tabelle zum Speichern des dazutun in seine alternative (zB Soundex) Form? Wenn Ihre erste SELECT-Datei die exakte Übereinstimmung nicht findet, können Sie eine zweite Suche durchführen, um nach passenden alternativen Formen zu suchen.

Der Trick besteht darin, jedes Wort so zu codieren, dass falsch geschriebene Variationen in dieselbe alternative Form konvertiert werden.

0

Ich schlage vor, dass Sie generate typo Variationen über die Abfrage eingeben.

dh hyperpext> {hyperpeext, hipertext, ...} etc

Eines davon gebunden ist, die richtige Schreibweise zu sein (vor allem für häufige Rechtschreibfehler)

Die Art und Weise Sie die wahrscheinlichste Übereinstimmung zu identifizieren ist um nach jedem auf einem Index zu suchen, der Ihnen die Dokumenthäufigkeit des Ausdruckes sagt. (Sinn machen?)