Firebird: Volltextindex

3 - Indizierung

Die Indizierung erfolgt in den SP's "prc_ftx_index_char" und "prc_ftx_index_text" wobei "prc_ftx_index_text" Text-BLOB's in für "prc_ftx_index_char" verarbeitbare Zeichenketten zerlegt und damit "prc_ftx_index_char" aufruft.

SP "prc_ftx_index_char"

CREATE OR ALTER PROCEDURE "prc_ftx_index_char" (
    IN_TEXT TYPE OF "dom_TextCache" COLLATE UTF8,
    IN_WHOLE_WORD TYPE OF "dom_BOOL",
    IN_WORD_BEGINNINGS TYPE OF "dom_BOOL",
    IN_WORD_TRAILINGS TYPE OF "dom_BOOL",
    IN_WORD_PARTS TYPE OF "dom_BOOL",
    IN_MIN_WORD_PART_LENGTH TYPE OF "dom_WordLength" = 1,
    IN_POSITION_OFFSET TYPE OF "dom_Position" = 0,
    IN_WORD_INDEX_OFFSET TYPE OF "dom_Position" = 0)
RETURNS (
    OUT_WORD_INDEX INTEGER)
AS
DECLARE VARIABLE TEXT_LEN TYPE OF "dom_Position";     /* Textlänge                     */
DECLARE VARIABLE WORD_INDEX TYPE OF "dom_Position";   /* aktueller Wortindex           */
DECLARE VARIABLE WORD_START TYPE OF "dom_Position";   /* Wortstartposition im Text     */
DECLARE VARIABLE WORD_STOP TYPE OF "dom_Position";    /* Wortendeposition im Text      */
DECLARE VARIABLE CHAR_IS_ALPHANUM TYPE OF "dom_BOOL"; /* aktuell untersuchte Position  */
                                                      /* ist alphanumerisch oder nicht */
BEGIN

  /* Wortindex initialisieren */
  WORD_INDEX = IN_WORD_INDEX_OFFSET;

  /* Textlänge bestimmen */
  TEXT_LEN = COALESCE(CHAR_LENGTH(IN_TEXT), 0);
  IF (TEXT_LEN > 0)
  THEN
  BEGIN

    WORD_START = 1;
    WHILE (WORD_START <= TEXT_LEN)
    DO
    BEGIN

      /* Wortanfang suchen */
      EXECUTE PROCEDURE "prc_ftx_is_alpha_num"(SUBSTRING(IN_TEXT FROM WORD_START FOR 1))
        RETURNING_VALUES CHAR_IS_ALPHANUM;
      WHILE (
              (WORD_START <= TEXT_LEN)
              AND
              (CHAR_IS_ALPHANUM = 0)
            )
      DO
      BEGIN
        WORD_START = WORD_START + 1;
        IF (WORD_START <= TEXT_LEN)
        THEN
          EXECUTE PROCEDURE "prc_ftx_is_alpha_num"(SUBSTRING(IN_TEXT FROM WORD_START FOR 1))
            RETURNING_VALUES CHAR_IS_ALPHANUM;
      END
      /* Wortanfang gefunden? */
      IF (CHAR_IS_ALPHANUM = 0)
      THEN
        BREAK;

      /* Wortende suchen */
      WORD_STOP = WORD_START;
      WHILE (
              (WORD_STOP <= TEXT_LEN)
              AND
              (CHAR_IS_ALPHANUM = 1)
            )
      DO
      BEGIN
        WORD_STOP = WORD_STOP + 1;
        IF (WORD_STOP <= TEXT_LEN)
        THEN
          EXECUTE PROCEDURE "prc_ftx_is_alpha_num"(SUBSTRING(IN_TEXT FROM WORD_STOP FOR 1))
            RETURNING_VALUES CHAR_IS_ALPHANUM;
      END

      IF (WORD_STOP > WORD_START)
      THEN
        /* Wort extrahieren und indizieren */
        EXECUTE PROCEDURE "prc_ftx_word_index"(
                              SUBSTRING(IN_TEXT FROM WORD_START FOR WORD_STOP - WORD_START),
                              WORD_START + IN_POSITION_OFFSET,
                              WORD_INDEX, IN_WHOLE_WORD,
                              IN_WORD_BEGINNINGS,
                              IN_WORD_TRAILINGS,
                              IN_WORD_PARTS,
                              IN_MIN_WORD_PART_LENGTH)
          RETURNING_VALUES WORD_INDEX;

      /* nächstes Wort */
      WORD_START = WORD_STOP + 1;

    END /* WHILE (WORD_START <= TEXT_LEN) */

  END /* IF (TEXT_LEN > 0) */

  /* Wortindex zurückgeben */
  OUT_WORD_INDEX = WORD_INDEX;

END

Die SP "prc_ftx_index_char" zerlegt die Zeichenkette in einzelne Wörter, indem über die SP "prc_ftx_is_alpha_num" geprüft wird, ob das aktuelle Zeichen alphanumerisch ist oder nicht. Dabei werden die Position und der Index des Wortes im Text mitgezählt. In der SP "prc_ftx_word_index" wird dann jedes gefundene Wort mit der Worttabelle verknüpft.

Aufrufparameter

IN_TEXTDie zu indizierende Zeichenkette.
IN_WHOLE_WORD1: Es werden ganze Wörter in die Worttabelle eingetragen.
IN_WORD_BEGINNINGS1: Es werden Wortanfänge (ohne ganzes Wort) in die Worttabelle eingetragen.
IN_WORD_TRAILINGS1: Es werden Wortenden (ohne ganzes Wort) in die Worttabelle eingetragen.
IN_WORD_PARTS1: Es werden die restlichen Wortteile in die Worttabelle eingetragen.
IN_MIN_WORD_PART_LENGTHDie minimale Länge von Wortteilen, die in die Worttabelle eingetragen werden. Ganze Wörter werden in jedem Fall eingetragen.
IN_POSITION_OFFSETAktuelle Position im Text bei Aufruf durch "prc_ftx_index_text".
IN_WORD_INDEX_OFFSETAktueller Index des Wortes im Text bei Aufruf durch "prc_ftx_index_text".

SP "prc_ftx_word_index"

CREATE OR ALTER PROCEDURE "prc_ftx_word_index" (
    IN_WORD "dom_Word" COLLATE UTF8,
    IN_WORD_START "dom_Position",
    IN_WORD_INDEX "dom_Position",
    IN_WHOLE_WORD "dom_BOOL",
    IN_WORD_BEGINNINGS "dom_BOOL",
    IN_WORD_TRAILINGS "dom_BOOL",
    IN_WORD_PARTS "dom_BOOL",
    IN_MIN_WORD_PART_LENGTH "dom_WordLength" = 1)
RETURNS (
    OUT_WORD_INDEX "dom_Position")
AS
DECLARE VARIABLE WORD_LENGTH "dom_WordLength"; /* Länge des Wortes */
DECLARE VARIABLE IDX "dom_WordLength";         /* Laufvariable 1   */
DECLARE VARIABLE IDY "dom_WordLength";         /* Laufvariable 2   */
BEGIN

  OUT_WORD_INDEX = IN_WORD_INDEX;
  WORD_LENGTH = COALESCE(CHAR_LENGTH(IN_WORD), 0);
  IF (WORD_LENGTH > 0)
  THEN
  BEGIN

    /* Wortindex aktualisieren */
    OUT_WORD_INDEX = OUT_WORD_INDEX + 1;

    /* Wort speichern */
    IF (IN_WHOLE_WORD <> 0)
    THEN
      EXECUTE PROCEDURE "prc_ftx_word_link"(IN_WORD, OUT_WORD_INDEX, IN_WORD_START, 0,
                                                IN_MIN_WORD_PART_LENGTH);

    /* Wortanfänge speichern */
    IF ((IN_WORD_BEGINNINGS <> 0) AND (WORD_LENGTH > 1))
    THEN
    BEGIN
      IDX = 1;
      WHILE (IDX < WORD_LENGTH)
      DO
      BEGIN
        /* Wortteil speichern */
        EXECUTE PROCEDURE "prc_ftx_word_link"(LEFT(IN_WORD, IDX), OUT_WORD_INDEX,
                                                  IN_WORD_START, 1, IN_MIN_WORD_PART_LENGTH);
        /* nächster Teil */
        IDX = IDX + 1;
      END
    END

    /* Wortteile ohne Anfang und Ende speichern*/
    IF ((IN_WORD_PARTS <> 0) AND (WORD_LENGTH > 2))
    THEN
    BEGIN
      IDX = 2;
      WHILE (IDX <= WORD_LENGTH - 1)
      DO
      BEGIN
        IDY = IDX + 1;
        WHILE (IDY <= WORD_LENGTH)
        DO
        BEGIN
          /* Wortteil speichern */
          EXECUTE PROCEDURE "prc_ftx_word_link"(
                                SUBSTRING(IN_WORD FROM IDX FOR IDY - IDX),
                                OUT_WORD_INDEX,
                                IN_WORD_START + IDX - 1,
                                2,
                                IN_MIN_WORD_PART_LENGTH);
          /* nächster Teil */
          IDY = IDY + 1;
        END
        /* nächster Teil */
        IDX = IDX + 1;
      END
    END

    /* Wortenden speichern */
    IF ((IN_WORD_TRAILINGS <> 0) AND (WORD_LENGTH > 1))
    THEN
    BEGIN
      IDX = 2;
      WHILE (IDX <= WORD_LENGTH)
      DO
      BEGIN
        /* Wortteil speichern */
        EXECUTE PROCEDURE "prc_ftx_word_link"(
                              SUBSTRING(IN_WORD FROM IDX FOR WORD_LENGTH - IDX + 1),
                              OUT_WORD_INDEX, IN_WORD_START + IDX - 1,
                              3,
                              IN_MIN_WORD_PART_LENGTH);
        /* nächster Teil */
        IDX = IDX + 1;
      END
    END

  END /* IF (WORD_LENGTH > 0) */

END

Die SP "prc_ftx_word_index" zerlegt das übergebene Wort und übergibt die einzelnen Bestandteile zusammen mit den Positionsangaben im Text an die SP "prc_ftx_word_link".

Aufrufparameter

IN_WORDDas zu zerlegende Wort.
IN_WORD_STARTPosition des Wortes im Text.
IN_WORD_INDEXIndex des Wortes im Text.
IN_WHOLE_WORD1: Es werden ganze Wörter in die Worttabelle eingetragen.
IN_WORD_BEGINNINGS1: Es werden Wortanfänge (ohne ganzes Wort) in die Worttabelle eingetragen.
IN_WORD_TRAILINGS1: Es werden Wortenden (ohne ganzes Wort) in die Worttabelle eingetragen.
IN_WORD_PARTS1: Es werden die restlichen Wortteile in die Worttabelle eingetragen.
IN_MIN_WORD_PART_LENGTHDie minimale Länge von Wortteilen, die in die Worttabelle eingetragen werden. Ganze Wörter werden in jedem Fall eingetragen.

SP "prc_ftx_word_link"

CREATE OR ALTER PROCEDURE "prc_ftx_word_link" (
    IN_WORD "dom_Word" COLLATE UTF8,
    IN_WORD_INDEX "dom_Position",
    IN_WORD_POSITION "dom_Position",
    IN_WORD_TYPE "dom_WordType",
    IN_MIN_WORD_PART_LENGTH "dom_WordLength" = 1)
AS
DECLARE VARIABLE WORD_ID TYPE OF "dom_PK"; /* ID des Wortes in Tabelle words */
BEGIN

  /* Wort speichern */
  IF (
       (IN_WORD_TYPE = 0) /* ganzes Wort */
       OR
       (CHAR_LENGTH(IN_WORD) >= IN_MIN_WORD_PART_LENGTH)
     )
  THEN
  BEGIN

    IN_WORD = LOWER(IN_WORD);
    SELECT
      "PK_wrd"
    FROM
      "words"
    WHERE
      LOWER("CL_wrd_word") = :IN_WORD
    INTO
      :WORD_ID;

    IF (WORD_ID IS NULL)
    THEN
      INSERT INTO
        "words"
        ("CL_wrd_word")
      VALUES
        (:IN_WORD)
      RETURNING
        "PK_wrd"
      INTO
        :WORD_ID;

    /* Verknüpfung auf das Wort speichern */
    INSERT INTO "_temp_text_link_words"
      (
        "FK_ttw_wrd",
        "CL_ttw_index",
        "CL_ttw_position",
        "FK_ttw_wty"
      )
    VALUES
      (
        :WORD_ID,
        :IN_WORD_INDEX,
        :IN_WORD_POSITION,
        :IN_WORD_TYPE
      );

  END

END

In der SP "prc_ftx_word_link" wird schließlich das Wort oder der Wortteil in die Worttabelle eingetragen. Die Verlinkung wird vorerst in eine temporäre Tabelle eingetragen und erst bei vollständiger Indizierung des Textes im Trigger in die entsprechende Linktabelle übertragen. Damit kann man dieses System leicht auf andere Tabellen mit eigenen Linktabellen adaptieren.

Aufrufparameter

IN_WORDDas zu indizierende Wort.
IN_WORD_INDEXIndex des Wortes im Text.
IN_WORD_POSITIONPosition des Wortes im Text.
IN_WORD_TYPEDer Worttyp aus der Tabelle "wort_types".
IN_MIN_WORD_PART_LENGTHDie minimale Länge von Wortteilen, die in die Worttabelle eingetragen werden. Ganze Wörter werden in jedem Fall eingetragen.