Produkty

Úvodní stránka / Produkty / 602SQL - Komerční rozšíření / Podrobný popis

602SQL - Komerční rozšíření

602SQL

Rozšíření o pokročilé fulltextové funkce a podporu formátu XML umožňující bezproblémový přenos strukturovaných dat v heterogenních prostředích.

 

Fulltext - Příklad 4

Mějme sadu dokumentů uložených na disku. Cílem je obsah dokumentů zaindexovat a umožnit v nich fulltextově vyhledávat.

Nástin řešení:
Je nutné si uvědomit, že dokumenty v tomto případě nejsou uloženy v databázových tabulkách a přístup k nim je složitější, nicméně z hlediska návrhu ftx systému se jedná pouze o jeden změněný přepínač oproti předchozímu příkladu.

Indexace souborů může probíhat buď zcela v režii SQL serveru nebo se může o část práce (a zatížení) podělit s klientem. Klient může sám rozebrat soubor na slova a serveru pouze zasílat jednotlivá slova k indexování. Tím se může zvýšit výkon serveru, neboť nemusí komunikovat s externími programy a dělá jen to, na co je optimalizovaný, tj. vkládání a indexování dat. Dále v textu si ukážeme oba způsoby indexování.

Aby fulltextové jádro vědělo o externích souborech, je nezbytné, aby plná jména souborů byla uložena v databázové tabulce. Pokud tedy je cílem indexovat existující soubory, musí nějaký program předem přečíst jména souborů a uložit je do databáze. POZOR! Cesta k souboru musí být vztažena k počítači, kde běží SQL server (pokud soubory čte server) nebo k počítači, kde je spuštěn klient (v případě načítání souborů klientskou aplikací).

V řešení si ukážeme manuální indexování (tj. zde indexování odložené na pozdější vhodnou dobu menší zátěže). Budeme proto muset více programovat, nelze již kompletně řešení „naklikat“ z Vývojového prostředí.

Pokud by indexování externích souborů na disku bylo jediným cílem autora, bez návaznosti na databázovou aplikaci, pak lze nalézt jiné specializované indexovací programy, které tuto činnost mohou provádět s výrazně menší režií a tedy rychleji a spolehlivěji.

Vytvoření fulltextu

Tabulka TAB_FT_4 má následující strukturu:

Definice tabulky pro příklad 4

Definice je výrazně jednodušší než v předchozích příkladech: obsahuje sloupec JMENO_SOUBORU, do něhož se bude zapisovat plná cesta k indexovanému souboru. Dále je zde boolovský sloupec INDEXOVANO, do něhož budeme při odložené indexaci zaznamenávat, jestli byl záznam již zaindexován.

POZOR! Cesta musí být uvedena z hlediska počítače, kde je spuštěn program, který čte soubory, což může být jinde než je spuštěno Vývojové klientské prostředí, v němž objekty definujeme!

V tomto popisu neřešíme, jakým způsobem bude aplikace nebo uživatelé vkládat cesty k souborům do tabulky, to záleží na konkrétní aplikaci. Pro práci se soubory je nutné nějaké klientské programátorské rozhraní, jazyk SQL serveru práci se soubory nezná.

Vytvoření fulltextového systému pro obě varianty (klientskou i serverovou) je z počátku stejné: vytvoří se pouze jakási kostra systému bez pokynů k indexaci – ty jsou přenechány funkci Fulltext_index_doc, která se volá buď z jazyka SQL (varianta server) nebo z klientského jazyka (varianta klient).

Návrh "prázdného" fulltextu

Návrh fulltextu v návrháři se ukončí před definováním fulltextových triggerů. Systém tedy ví, v jakém jazyce budou dokumenty, že se mají slova lemmatizovat, že se prohlíží ZIPy, ale neprovádí to vše automaticky, protože nezná soubory k indexaci.

Odbočka k automatickému indexování serverem

Aby byl popis kompletní, ukažme si i variantu (a v ní dva různé způsoby) automatického indexování serverem: 1) pomocí ftx triggerů v návrháři fulltextu nebo 2) pomocí normálních SQL triggerů.

Ad 1)

Pro toho, kdo četl předchozí příklady nebude problémem vytvořit příslušný ftx trigger:

Návrh fulltextu pro případ automatického indexování

Jedinou novinkou je zde zapnutí příznaku Nepřímý v definici ftx triggeru, který se říká, že text ve sloupci JMENO_SOUBORU není přímo text k indexování, ale jen odkaz na soubor na disku. Pokud by všechny soubory byly jednoho typu, je efektivnější v poli Formát zvolit příslušný typ souboru, indexování pak bude o něco rychlejší.

Ad 2)

Návrh fulltextu ponecháme bez ftx triggerů a vytvoříme triggery v jazyce SQL, nad nimiž máme lepší kontrolu, nicméně tím neřešíme odložení indexování na jinou dobu. Budeme potřebovat minimálně triggery dva – pro vkládání a smazání záznamu. Pokud může nastat změna (přepis) ve sloupci se jménem souboru, zachytí ji trigger třetí.

Trigger pro indexaci při vložení záznamu:

TRIGGER `ft4_ins` AFTER INSERT ON `TAB_FT_4`
REFERENCING NEW AS `nove`
FOR EACH ROW
  WHEN (nove.jmeno_souboru IS NOT NULL)
BEGIN
  DECLARE CONTINUE HANDLER FOR SQLSTATE 'W0226', SQLSTATE 'W6005' BEGIN 
    CALL Log_write("Indexace souboru "+nove.jmeno_souboru+" se nezdařila.
    Číslo chyby: "+SQLSTATE);
  END;
  CALL Fulltext_index_doc('.ftx_priklad4', nove.id, nove.jmeno_souboru, '', 1); 
END

Trigger pro odstranění slov indexu při smazání záznamu:

TRIGGER `ft4_del` AFTER DELETE ON `TAB_FT_4`
REFERENCING OLD AS `stare`
FOR EACH ROW
BEGIN
  CALL Fulltext_remove_doc(".ftx_priklad4", stare.id);
END

Trigger pro přeindexaci při změně ve sloupci:

TRIGGER `ft4_upd` AFTER UPDATE OF `JMENO_SOUBORU` ON `TAB_FT_4`
REFERENCING NEW AS `nove`
FOR EACH ROW
BEGIN
  DECLARE CONTINUE HANDLER FOR SQLSTATE 'W0226', SQLSTATE 'W6005' BEGIN 
    CALL Log_write("Indexace souboru "+nove.jmeno_souboru+" se nezdařila.
    Číslo chyby: "+SQLSTATE);
  END;
  CALL Fulltext_index_doc('.ftx_priklad4', nove.id, nove.jmeno_souboru, '', 1); 
END

Dále se již automatickému indexování v tomto příkladu nebudeme věnovat.

a) Varianta manuálního indexování serverem

V tomto případě bude třeba „jednou a čas“, např. v noci, spustit SQL proceduru, která vybere nové, dosud nezaindexované záznamy a ty zaindexuje. Procedura může vypadat takto:

PROCEDURE Odlozene_indexovani(  );
BEGIN
  DECLARE i,j INTEGER DEFAULT 0;
  DECLARE CONTINUE HANDLER FOR SQLSTATE 'W0226', SQLSTATE 'W6005' BEGIN 
    CALL Log_write("Indexace souboru "+nove.jmeno_souboru+" se nezdařila.
    Číslo chyby: "+SQLSTATE);
  END;

  FOR radek AS cx CURSOR FOR
    SELECT id, jmeno_souboru, indexovano 
    FROM Tab_ft_4
    WHERE NOT indexovano AND jmeno_souboru IS NOT NULL
  DO
    IF Fulltext_index_doc('.ftx_priklad4', radek.id, radek.jmeno_souboru, '', 1)
    THEN
      SET i = i+1;
    ELSE
      SET j = j+1;
    END IF;
    UPDATE SET indexovano = TRUE WHERE CURRENT OF cx;
  END FOR;
  CALL log_write(Int2str(i)+' dokumentů indexováno, '+Int2str(j)+' s chybou');
END

V proceduře musí být deklarován CONTINUE handler pro chyby, které mohou nastat v souvislosti s indexací – v opačném případě by procedura skončila na prvním výskytu chyby (tj. na první indexaci souboru, který buď nenajde nebo nedokáže poznat formát).

b) Varianta indexování klientem

Indexování klientem je vždy manuální a spočívá v tom, že klientská aplikace sama najde soubory, rozebere je na slova a ta zasílá serveru, který si je ukládá do systémových fulltextových tabulek. Tím může poněkud ulehčit práci SQL serveru, který se nemusí starat o práci se soubory. Při návrhu je třeba uvažovat další faktory: pokud je klientský program na stejném počítači jako sever (často v PHP skriptech), tak je počítač zatížen podobně jako v případě, že indexaci provádí sám server, pokud je klientský program na jiném počítači v síti, pak se indexují soubory uložené na počítači klienta, což nemusí být praktické.

Ukázka použití v jazyce Pascal v Delphi:

procedure TForm1.ButtonIndexClick(Sender: TObject);
var
 i,max,err : integer;
 id : integer;
 filename : string;
begin
  Memo1.Lines.Clear;
  with SQL602DataSet1 do begin
    CommandType := dsctQuery;
    CommandText := 'SELECT id,jmeno_souboru,indexovano FROM Tab_ft_4
    WHERE NOT indexovano AND jmeno_souboru IS NOT NULL';
    Open;
    max := RecordCount;
    First;
    for i := 1 to max do
    begin
      Next;
      id := Fields.Fields[0].AsInteger;
      filename := Fields.Fields[1].AsString;
      if cd_Fulltext_index_doc(SQL602Conn.cdp,'Ft_dev.ftx_priklad4',id,
      Pchar(filename),'') then
       Memo1.Lines.Add('OK'+' '+inttostr(i)+' '+inttostr(id)+' '+filename)
      else begin
       err := SQL602Conn.sz_error;
       Memo1.Lines.Add('FAILED'+' '+inttostr(i)+' '+inttostr(err)+' '+filename);
      end;
    end;
    Close;
  end;
end;

Ochrana osobních údajů