Announcement

Collapse
No announcement yet.

Fehlertolerante Schlagwortsuche

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Fehlertolerante Schlagwortsuche

    Hallo,

    Umg.: Delphi 6 Ent, BDE, Paradox und Oracle

    Die Tabellen der Datenbank, die untersucht werden sollen, habe alle ein indiziertes VARCHAR-Feld namens Beschreibung.

    Wie kann ich in der Anwendung dem Benutzer eine fehlertolerante Schlagwortsuche ermöglichen, die ein vernünftiges Laufzeitverhalten zeigt
    (als Ergebnisse kommen alle Datensätze in Betracht, die eine fehlertolerante Übereinstimmung des Suchwortes mit dem Beschreibungstext der Tabellen aufweisen)?

    Stephan

  • #2
    Hallo Stephan,<br>
    das Zauberwort heisst SoundEx. Der SoundEx-Algorithmus reduziert einen<br>
    beliebigen String auf 4 Zeichen. Diese 4 Zeichen bestehen aus dem Anfangsbuchstaben<br>
    des Strings und einem 3 stelligen Zahlencode. <br>
    Dadurch ist es z.B. möglich durch die Eingabe von Meier alle Meyer, Maier, Mayer usw zu finden.<br>
    Die Beschreibung des SoundEx haben ich im Internet gefunden. Daraufhin habe ich mir folgende<br>
    Unit gebastelt. Ob ich den Algorithums absolut fehlerfrei hinbekommen habe weiss ich<br>
    nicht so genau. Jedoch sind die Resultate die ich in meinen Projekten damit erziele <br>
    zu meiner vollsten Zufriedenheit.<br>
    <pre>
    <font face="Verdana" size="2" color="#000000">unit soundex;
    interface
    function SoundEx_Simple(const S : String) : String;
    function SoundEx_Komplex(const S : String) : String;
    function FilterCapitalLetters(const S : String) : String;
    function ReplaceUmlaute(const S : String) : String;
    implementation
    uses Sysutils;
    Type
    TLArray = Array [65..90] of Byte;
    const
    // A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y, Z
    LArray : TLArray = (0,1,2,3,0,1,2,0,0,2,2,4,5,5,0,1,2,6,2,3,0,1,0,2,0 ,2);
    function FilterCapitalLetters(const S : String) : String;
    var
    iCnt : Integer;
    begin
    Result:=AnsiUpperCase(S);
    iCnt:=1;
    While iCnt&lt;=Length(Result) do
    begin
    If (Ord(Result[iCnt])&lt;65) OR (Ord(Result[iCnt])&gt;90) then
    Delete(Result,iCnt,1)
    else
    Inc(iCnt);
    end;
    end;
    function ReplaceUmlaute(const S : String) : String;
    begin
    Result:=S;
    // Teste ob Sonderzeihen enthalten sind und ersetze sie -&gt;
    Result:=StringReplace(Result,'Ä','AE',[rfReplaceAll]);
    Result:=StringReplace(Result,'Ö','OE',[rfReplaceAll]);
    Result:=StringReplace(Result,'Ü','UE',[rfReplaceAll]);
    Result:=StringReplace(Result,'ä','ae',[rfReplaceAll]);
    Result:=StringReplace(Result,'ö','oe',[rfReplaceAll]);
    Result:=StringReplace(Result,'ü','ue',[rfReplaceAll]);
    Result:=StringReplace(Result,'ß','SS',[rfReplaceAll]);
    // &lt;- Teste ob Sonderzeihen enthalten sind und ersetze sie
    end;
    function SoundEx_Simple(const S : String) : String;
    var
    sTmp : String;
    iCnt : Integer;
    jCnt : Integer;
    Entry : Byte;
    LetterArray : TLArray;
    begin
    Result:='';
    Entry:=0;
    If S='' then Exit;
    LetterArray:=LArray;
    sTmp:=AnsiUpperCase(S);
    Result:=sTmp[1];
    iCnt:=2;
    Entry:=LetterArray[Ord(sTmp[1])];
    While (iCnt)&lt;=Length(sTmp) do
    begin
    If LetterArray[Ord(sTmp[iCnt])]=Entry then // Ist der nächste Buchstabe in der gleichen Kategorie ?
    Inc(iCnt) // Wenn ja, dann überspringen
    else
    Break;
    end;
    While iCnt&lt;=Length(sTmp) do
    begin
    If LetterArray[Ord(sTmp[iCnt])]&lt;&gt;0 then
    begin
    Result:=Result+IntToStr(LetterArray[Ord(sTmp[iCnt])]);
    Entry:=LetterArray[Ord(sTmp[iCnt])];
    While (iCnt+1)&lt;=Length(sTmp) do
    begin
    If LetterArray[Ord(sTmp[iCnt+1])]=Entry then // Ist der nächste Buchstabe in der gleichen Kategorie ?
    Inc(iCnt) // Wenn ja, dann überspringen
    else
    Break;
    end;
    end;
    If Length(Result)=4 then // SoundExe sind nie länger als 4 Zeichen
    Break;
    Inc(iCnt);
    end;
    While Length(Result)&lt;4 do // Wenn SoundExe kürzer als 4 Zeichen sind, dann wird der Rest mit 0 aufgefüllt
    Result:=Result+'0';
    end;
    function SoundEx_Komplex(const S : String) : String;
    begin
    Result:=StringReplace(S,' ','',[rfReplaceAll]);
    Result:=ReplaceUmlaute(Result);
    Result:=FilterCapitalLetters(Result);
    Result:=SoundEx_Simple(Result);
    end;
    end.</font>
    </pre>
    <b>SoundEx_Simple</b> wandlet den String direkt um.<br>
    <b>SoundEx_Komplex</b> wandelt vor der Berechnung alle Zeichen des Strings in Großbuchstaben (filtert danach alles heraus was kein Großbuchstabe ist (z.B. Ziffern))<br>
    um. Zusätzlich werden noch alle Sonderzeichen ersetzt. <br>
    <

    Comment


    • #3
      Ich verwende den SoundEx um fehlertolerant nach Namen zu suchen. Dafür spendiere ich der Tabelle ein zusätzliches Feld<br>
      für den SoundEx des Namens. <br>
      Wenn Du fehlertolerant innerhalb einer Beschreibung suchen möchtest, müsstest Du für jedes Wort der Beschreibung einen SoundEx bilden.<br>
      Wie sich das auf die Performance auswirkt, wäre zu testen.<br&gt

      Comment


      • #4
        Hallo!<br>
        Soundex hat so seine kleinen Probleme mit nicht englischsprachigen Namen. Der deutsche Meier mit allen seinen Ausprägungen wird da gerne mal abweichend kodiert.<br>
        Ebenso werden die (nicht gänzlich unwichtigen) Endsilben praktisch vollkommen ignoriert.<br>
        Als Quelle sei hier auf http://www.las-inc.com/soundex/ verwiesen.<br>
        <br>
        Eine Alternative sind Double Metaphone, die etwas mehr auf Sprachspezifika eingehen. Eine Lösung für MS SQL ist zu finden unter:<br>
        http://cpan.cbn.net.id/modules/by-category/11_String_Lang_Text_Proc/Text/<br>
        <br>
        BYE BERN

        Comment


        • #5
          Hallo,

          die Anmerkung von Bernd ist zwar richtig, aber ich habe mal soundex bei einer Großen Kundendatenbank, mit Email-Bestellung eingesetzt,
          also d.h.: email importieren, Besteller-Angaben in der DB suchen, wenn gefunden, Kundennummer übernehmen, sonst neue Kundennummer.
          und ich mußte sagen, der Fehler quozient lag unter 0,01 % und das finde ich schon richtig gut.

          Also würd ich sagen : probieren geht über studieren. :-)

          Gruß Marti

          Comment


          • #6
            Hallo!<br>
            Also die Probleme waren nicht bei Meier (das hatte ich wohl falsch in Erinnerung) sondern z.B. bei Namen mit z oder tz :
            <PRE>
            Name Soundex Double
            Schulz S420 SLTS
            Schultz S432 SLTS
            Schultzenheimer S432 SLTSN
            Schulzenheimer S425 SLTSN
            </PRE>
            Die Performance ist bei Soundex besser, da "weniger" gemacht wird.<br>
            Aber die Treffer sind sprachlich bei DoublemetaPhone besser.<br>
            Gerade aus diesem Grund haben wir unsere Anwendung (200-300 tausend Adressen, verschlagwortete Manuskripte ca. 50 tausend) auf DoubleMetaPhone umgestellt und sind eigentlich sehr zufrieden.<br>
            BYE BERN

            Comment


            • #7
              Hallo,

              Danke für die rege Beteiligung.

              Ich habe z.B. 50 Tabellen, die alle ein Feld namens Beschreibung haben. Jede dieser Tabellen hat eine bestimmte Anzahl an Datensätzen (u.U. > 50000). Der Benutzer gibt einen Begriff ein und nun beginnt die große Suche: Wie würde aber die Suche mit Soundex/Metaphone über die Tabellen/Datensätze konkret im Code aussehen? Sind diese Iterationen nicht sehr laufzeitbeinträchtigend?

              Stepha

              Comment


              • #8
                Je nachdem, wie viel Wartezeit Du Deinen Anwendern zumuten willst kannst Du entweder:<br>
                (In der Beschreibung phonetisch durch soundex oder DoubleMetaPhone ersetzen)<br>
                1. Ein Feld Beschreibung_Soundex zufügen und nun alle Worte aus Beschreibung in phonetisch umwandeln und den "phonetischen"-Satz im Feld Beschreibung_Phonetisch ablegen. Zugriff via select * from table where beschreibung_phonetisch like '%'+MeinPhonetischesSuchwort+'%'. Das kann dann schon mal etwas dauern!!!<br>
                <br>
                2. Eine Wortdatenbank für die Beschreibung anlegen. Jedes Wort aus der Beschreibung in phonetisch umwandeln und in der Wortdatenbank mit Referenz auf die Herkunft (Tabelle und Datensatz) eintragen. Dauert etwas länger beim Aufbau ist dann aber im Zugriff sehr fix. (Plattenplatz sollte eine untergeordnete Rolle spielen)<br>
                <br>
                Wir verwenden Version 2, die unter MS SQL 2000 mit Hilfe von Triggern und StoredProcs trotz allem akzeptabel fix bei der Erfassung ist.<br>
                <br>
                BYE BERN

                Comment


                • #9
                  Hallo Bernd, <br>
                  ich habe ein ähnliches Problem wie Stephan, ich habe Datenbank für die Medizin und nun soll der Benutzer ein Schlagwort eingeben und als Ergebnis alle ähnlichen Einträge wie bei Google angezeigt bekommen.
                  Bin gerade dran den Soundex einzubinden und zu testen, kannst du mir mehr Infos zu dem Phonetischen ... geben ?
                  Es sollte halt schon eine exakte Abfrage werden.

                  <br>
                  Gruss Volke

                  Comment


                  • #10
                    Hallo!<br>
                    Ich weiß jetzt nicht so genau, was Du wissen möchtest?!?<br>
                    Source, Probleme, Quellen im Internet und Kurzanleitungen für die Programmierung sind hier im Thread eigentlich gesammelt verfügbar.<br>
                    Google liefert eniges an Quellen, wenn man nach Soundex oder DoubleMetaPhone (manchmal auch double metaphone) sucht.<br>
                    Was genau fehlt Dir denn noch?<br>
                    BYE BERN

                    Comment


                    • #11
                      Danke für die Diskussion!

                      @Bernd: Deine Beispiele zielen auf eine Änderung des Datenmodells ab, eigentlich etwas, was ich vermeiden möchte. Jedesmal wenn ein Beschreibungstext gepostet wird, muss das Soundex-Pendant in das entsprechende Feld geschrieben werden. Geht mal ein Index auf eine Beschreibung verloren, ist eine Reorganisation nötig. Aus meiner Sicht ein enormer Aufwand.

                      Grundsätzlich würde beim Suchen eine Iteration ablaufen (Pseudocode):<pre>
                      for I := 0 to Tabellen - 1 do begin
                      S := SoundEx(Tabelle[I].Beschreibung);
                      if Vergleichsalgorithmus(S, Suchstring) = Ergebnis then...
                      end;</pre>

                      Würde diese Iteration nicht die Performance drücken? Als Vergleichsalgorithmus würde ich die <i>Damerau-Levenshtein-Metrik</i> nehmen. Ich habe aber hier leider keine Erfahrungswerte.

                      Stepha

                      Comment


                      • #12
                        Hi,
                        hat jemand die fertige udf von Soundex da ?
                        Bei mir läuft die nicht.
                        <br>
                        Wenn ja bitte an <br>
                        [email protected] <br>
                        mailen.<br>
                        Danke Volke

                        Comment


                        • #13
                          Hi, <br>
                          habe die Soundex.dll nun zum laufen bekommen aber bin etwas enttäuscht.<br>
                          Als Test habe ich mal 4 Datensätze mit dem Name "Daniel" eingefügt.<br>
                          1. Daniel<br>
                          2. Daiel <br>
                          3. Danie <br>
                          4. aniel <br>
                          5. Danielr<br>
                          Result tat war nur 1 = Daniel <br>
                          Gibt es keine andere Möglichkeit noch Ähnlichkeiten zu suchen ?<br>
                          <br>
                          Volke

                          Comment


                          • #14
                            @Stephan<br>
                            Enthält das Feld Beschreibung nicht einen kompletten deutschen Satz?!?!<br>
                            Wie willst Du denn eine Überprüfung in einer (auch nur halbwegs) annehmbaren Zeit hinbekommen, ohne die DB zu erweitern?<br>
                            Damerau-Levenshtein-Metrik: das war mir zu heftig. Hast Du da jetzt Erfahrungen mit? Welche Ergebnisse bekommt man denn da so?<br>
                            Hattest Du nicht gesagt, daß Du Oracle verwendest? Da gibt doch mit absoluter Sicherheit Trigger und alles andere was man so braucht, oder?!? Einer Realisierung allein auf Basis der Datenbank sollte dann doch kein Problem sein. Deine Bedenken mit dem verlorenen Index sind dann doch nicht meht so relevant?!?<br>
                            Und immer allerschlimmsten Fall läßt sich bei Realisierung mit einem Update Trigger der Index sehr fix wieder aufbauen. update tabelle set beschreibung = beschreibung und los gehts!<br>
                            BYE BERN

                            Comment


                            • #15
                              @Volker<br>
                              Ich nehme mal an, daß Du InterBase verwendest oder?<br>
                              Klar gibt es noch andere Möglichkeiten phonetisch zu suchen :<br>
                              Soundex<br>
                              DoubleMetaPhone<br>
                              Damerau-Levenshtein-Metrik<br>
                              sind da sicherlich nur einige Möglichkeiten.<br>
                              (Sieh Dir mal diese komplette Diskussion an)<br>
                              Deine Probleme mit soundex kann Du auch noch mal in meinem geposteten Link weiter oben wiederfinden.<br>
                              Dort ist auch der Link zu DoubleMetaPhone. Die stored procedure läßt sich eigentlich recht einfach lesen und es sollte nicht zu schwer sein mal eine Delphi Funktion daraus zu zaubern.<br>
                              Ansonsten bleibt dann selbermachen für eine phonetische Suche sch = s; tz = z; ss=s usw..<br>
                              Es hilft übrigens immer sich mal die Ergebnisse von phonetischen Funktionen direkt anzusehen. Das wird Dir bei Deinem Daniel Beispiel sicherlich weiterhelfen!<br>
                              BYE BERN

                              Comment

                              Working...
                              X