menu
  Home  ==>  articles  ==>  web  ==>  moteur_de_recherche_cgi   

Moteur de Recherche CGI - John COLIBRI.

  • résumé : un programme CGI retourne les pages correspondant à une recherche textuelle parmi les pages de votre site Web
  • mots clé : recherche textuelle - Boyer Moore Horspool - FindFirst, FindNext - évaluateur d'expression booléennes - CGI
  • logiciel utilisé : Windows XP, Delphi 6.0
  • matériel utilisé : Pentium 1.400Mhz, 256 M de mémoire, 140 G disque
  • champ d'application : Delphi 1 à 2005 sur Windows, Kylix
  • niveau : développeur Delphi
  • plan :


1 - Introduction

Le moteur de recherche utilisé jusqu'en décembre 2005 utilisait une base de mots clés:
  • pour chaque article, nous ajoutions une liste de mots considérés comme pertinents
  • l'utilisateur tapait une requête dans une Forme CGI, telle que "Delphi AND Interbase"
  • le programme analysait la table des mots clés, et un interprète trouvait les pages correspondant à la recherche
  • le programme retournait la page des liens vers les pages du site satisfaisant la requête
Il y a deux inconvénients à cette technique:
  • nous devions rédiger la liste des mots clés pour chaque nouvelle page
  • certaines requêtes correspondant à des phrases complètes, telles que "Delphi 2005" ne pouvaient être formulées (il fallait demander "Delphi AND 2005", ce qui n'est pas la même chose)
Nous avons publié sur notre site us The Coliget Search Engine le source d'un moteur de recherche textuelle sur les fichiers d'un répertoire. Nous allons ici adapter le programme pour qu'il réponde à des requêtes formulées dans une Forme CGI.




2 - Le fonctionnement du moteur



2.1 - Architecture

Le fonctionnement est très simple:
  • l'utilisateur tape sa requête dans un formulaire Web:

    lancer la recherche

  • le serveur de notre site charge en mémoire le moteur que nous allons présenter (le "script cgi")
  • ce programme:
    • récupère la requête et la débarrasse des artifices CGI
    • extrait les chaînes littérales de la requête
    • charge une à une les pages de notre site et recherche si le texte de la page satisfait la requête
    • construit une page réponse et la renvoie à l'utilisateur:

      résultats
de la recherche

Examinons ces étapes:



2.2 - Analyse de la requête

Il s'agit simplement de lire le fichier d'entrée par Readln, puis de supprimer les artifices liés à la syntaxe CGI ("+", "%", "&" etc)

Le résultat est une chaîne identique à celle qu'a tapé l'utilisateur

Dans l'exemple précédent:

  • l'utilisateur a tapé:

        GAMMA and "design patterns"

    en cochant "minuscules"

  • le Serveur Web envoie au CGI:

        q=GAMMA+and+%22design+patterns%22&minuscule=on&

  • l'analyseur retourne:

        GAMMA and "design patterns"
        minuscules True



2.3 - Evaluation de la requête

Nous devons
  • analyser la requête
  • pour chaque page de notre site
    • charger la page
    • tester si son contenu satisfait la requête


Pour accélérer l'évaluation de la requête, nous tokenisons cette requête:
  • une tStringList contient les chaînes littérales
  • un tableau d'entiers contient
    • les indices des chaînes littérales
    • des valeurs négatives pour les opérateurs et ponctuations (NOT AND OR ())
Voici un exemple:

tokenisation de l'expression de recherche



2.4 - Analyseur lexical

La tokenisation est effectuée par un analyseur lexical des plus simples: il sépare simplement
  • les mots clé NOT AND OR
  • les ponctuations ( )
  • les littéraux uml ou "design pattern"
L'analyseur lexical renvoie un Booléen qui permet d'interrompre le traitement si les symboles tapés sont illégaux.



2.5 - Analyse Syntaxique

Avant de lancer le moteur sur la lecture des pages, nous vérifions une fois que la syntaxe est correcte:
  • parenthèses incorrectes

        uml and ("design patterns"

  • littéraux sans opérateur

        uml "design patterns"

    ou, pour les usagers habitués au ET implicite

        design patterns

Cette vérification de syntaxe se fait par une simple descente récursive en signalant les problèmes.



2.6 - Chargement des pages

Les pages sont recherchées en utilisant simplement FindFirst et FindNext, et le texte charge dans une String



2.7 - Evaluation de la requête sur une page

L'évaluation se fait:
  • en effectuant une descente récursive sur la requête tokenisée
  • lorsqu'un token est un littéral, nous effectuons une recherche dans le texte de la page: le littéral est présent ou non dans le texte
  • l'évaluateur combine alors les booléen pour fournir en final le résultat


La recherche dans le texte pourrait être effectuée par Pos, mais pour accélérer une peu la recherche, nous avons utilisé un algorithme de Boyer Moore Horspool.



Le résultat de cette évaluation sur toutes les pages est une liste de pages comportant pour chacune:

  • le nom de la page
  • les paramètres: taille, date de dernière modification, le résumé.

    Le résumé est simplement récupéré dans le méta tag "content" que nous plaçons dans chacune de nos pages.



2.8 - Construction du résultat

Nous avons deux types de résultats possibles
  • les erreurs de syntaxe
  • la liste des pages correspondant à la recherche


Pour les erreurs, nous retournons la requête en coloriant en rouge la partie qui pose problème. Voici un exemple:
  • la requête incorrecte est:

    recherche contenant une erreur

  • et voici la réponse du berger à la bergère:

    résultat d'une recherche avec erreur



Voici le retour d'une question correcte:
  • pour la recherche "GAMMA and "design patterns"

  • la réponse affiche:

    résumé de chaque page

  • et le résumé a été récupéré dans le fichier .HTML qui contenait:

      <HTML>
        <HEAD>
          <TITLE>jColibri: Gang of Four Design Patterns</TITLE>
          <META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
          <META http-equiv="CONTENT-LANGUAGE" content="French">
          <META http-equiv="VW96.OBJECT TYPE" content="Archive">
          <META name="TITLE" content="John COLIBRI">
          <META name="DESCRIPTION" content="codage Delphi des 23 Design Patterns du livre de GAMMA et al (gof: Gang of Four)">
          <META name="KEYWORDS" content="Design Patterns,Patrons de Conception,Gof,Gang of Four,Erich GAMMA,Richard HELM,Ralph JOHNSON,John




3 - Le Programme Delphi

3.1 - Organisation des classes

Pour le programme CGI, nous avons créé
  • des unités qui dupliquent nos utilitaires classiques d'analyse de chaînes, de répertoire, de manipulation de chemins etc. Ces unités ont simplement été débarrassé de toutes référence à des composants visuels de la VCL (tMemo etc). Ces unités n'offrent aucune difficulté
  • des unités pour le traitement CGI pur (traitement des variables d'environnement etc). Ces unités ont déjà été présentées dans notre article sur les CGI
  • des unités propre à la recherche: analyse lexicale, syntaxique, évaluation, pages résultat


Pour la recherche:
  • c_text_searcher encapsule la recherche textuelle d'une chaîne dans une String (BMH dans notre cas)
  • c_site_searcher utilise FindFirst et FindNext pour trouver les pages .HTML de notre site
  • c_cgi_request_evaluator contient l'évaluateur lexical et l'évaluateur syntaxique
  • c_cgi_result_page_list gère la liste des pages correctes et leurs paramètres
  • c_cgi_html_page_builder calcule la page d'erreur ou la page de résultats en utilisant la liste de pages


L'unité principale, u_cgi_coliget_search contient une seule procédure qui gère l'ensemble:
  • lecture et analyse de la requête reçue de serveur
  • tokenisation et vérification de la requête
  • chargement et analyse de chaque page, avec construction de la liste des pages correctes
  • construction de la page résultat HTML
  • envoi de la page résultat vers le serveur


3.2 - c_text_searcher

La classe est définie par:

 c_text_searcherclass(c_basic_object)
                    m_text_to_searchString;
                    m_text_lengthInteger;

                    m_pattern_to_findString;
                    m_pattern_lengthInteger;

                    m_jump_tablearray[#0..#255] of Integer;
                    m_jump_valueinteger;

                    m_search_indexInteger;

                    Constructor create_text_searcher(p_nameString);

                    procedure initialize_text(p_text_to_searchString);
                    procedure initialize_pattern_to_search(p_pattern_to_searchString);
                    function f_index_ofInteger;

                    function f_found_string(p_pattern_to_searchString): Boolean;
                    function f_found_string_in_text(p_pattern_to_search,
                        p_text_to_searchString): Boolean;

                    Destructor DestroyOverride;
                  end// c_text_searcher

et:

  • m_text_to_search est le texte de la page .HTML, m_pattern_to_search la chaîne littérale recherchée dans ce texte
  • m_jump_table est une table de saut permettant de progresser lorsque la chaîne recherchée n'est pas trouvée à un endroit du texte
  • initialize_text initialise m_text_to_search
  • initialize_pattern_to_search construit la table de saut
  • f_index_of fournit la prochaine occurence de la chaîne à partir de m_search_index
Notez que:
  • toute cette mécanique Boyer Moore Horspool pourait être remplacée par Pos
  • l'initialisation de la table de saut n'a besoin d'être effectuée qu'une seule fois, alors que la recherche peut être itérée après chaque recherche ayant réussi
Vous pouvez vous reporter aux articles sur BMH si c'est la technique de recherche qui vous intéresse



3.3 - c_site_searcher

Cette unité est chargée de trouver les fichiers dans le répertoire indiqué, puis de charger le texte et lancer la recherche.

La classe est définie par:

 c_site_searcherclass(c_basic_object)
                    m_c_text_searcherc_text_searcher;
                    m_c_request_evaluatorc_request_evaluator;
                    m_case_sensitiveBoolean;
                    m_search_rootString;

                    // -- the results are saved here
                    m_c_result_page_list_refc_result_page_list;

                    Constructor create_site_searcher(p_nameString);

                      procedure _handle_page(p_pathp_file_nameString);
                    procedure find_pages(p_pathStringp_case_sensitiveBoolean;
                          p_c_result_page_listc_result_page_list);
                    procedure search_the_site(p_pathp_requestString;
                        p_case_sensitiveBoolean);
                  end// c_site_searcher

et:

  • m_c_text_searcher_ref est la référence vers la classe qui recherche chaque littéral
  • m_c_request_evaluator_ref est la mécanique qui évalue l'expression, qui sera présentée ci-dessous
  • m_c_result_page_list_ref est le conteneur de la liste de pages correspondant à la recherche

  • find_pages est la méthode qui utilise FindFirst et FindNext pour trouver les pages en appelant _handle_page pour chacune d'entre elles


Le traitement de chaque page est le suivant:

procedure c_site_searcher._handle_page(p_pathp_file_nameString);
  const k_content_meta'<META name="DESCRIPTION" content="';
  var l_text_to_searchString;
      l_text_lengthInteger;

  function f_extract_contentString;
    var l_start_contentl_indexInteger;
        l_content_metaString;
    begin
      // -- |<META name="DESCRIPTION" content="last technical papers, ">|
      Result:= '';
      l_content_meta:= k_content_meta;
      if not m_case_sensitive
        then l_content_meta:= LowerCase(l_content_meta);

      l_start_content:= Pos(l_content_metal_text_to_search);
      if l_start_content> 0
        then begin
            // -- skip the meta tag
            l_start_content:= l_start_contentLength(k_content_meta);
            l_index:= l_start_content;

            while (l_index<= l_text_lengthand (l_text_to_search[l_index]<> '"'do
            begin
              Result:= Resultl_text_to_search[l_index];
              Inc(l_index);
            end;
          end;
    end// f_extract_content

  var l_date_timetDateTime;
      l_c_result_pagec_result_page;

  begin // _handle_page
    with tStringList.Create do
    begin
      LoadFromFile(p_pathp_file_name);
      l_text_to_search:= Text;

      l_text_length:= Length(l_text_to_search);
      if not m_case_sensitive
        then l_text_to_search:= LowerCase(l_text_to_search);

      m_c_text_searcher.initialize_text(l_text_to_search);

      with m_c_request_evaluator do
      begin
        if f_evaluate_request(m_c_text_searcher)
          then begin
              // -- fetch the date
              if m_c_result_page_list_ref<> Nil
                then begin
                    l_date_time:= f_file_date_time(p_pathp_file_name);

                    l_c_result_page:= m_c_result_page_list_ref.f_c_add_result_page('page',
                        p_pathp_file_namel_text_lengthl_date_timef_extract_content);
                  end;
            end;
      end// with m_c_request_evaluator

      Free;
    end// with tStringList
  end// _handle_page



Notez que:

  • la routine de recherche de page a éliminé (dans notre cas) les pages de "frames html", dans notre cas les pages de gauche et du haut, et qui ne contiennent aucune information pertinente pour l'utilisateur
  • la méthode _handle_page aurait pu être nichée dans find_pages


3.4 - c_cgi_request_evaluator

Cette unité encapsule la classe qui tokenise l'expression, vérifie la syntaxe, et évalue l'expression sur une page donnée. Voici la définition de la classe:

 c_request_evaluatorclass(c_basic_object)
                        // -- the original request
                        m_requestString;
                        // -- the array of literals
                        m_c_request_identifierstStringList;
                        m_token_countInteger;

                        // -- the array of tokens: negative for NOT AND OR  ( )
                        // -- positive for literals
                        m_oa_tokensArray Of Integer;

                        Constructor create_request_evaluator(p_nameString);

                        function f_c_selfc_request_evaluator;
                        function f_tokenize_request(p_requestString): Boolean;

                        function f_check_syntaxBoolean;

                        function f_evaluate_request(p_c_text_searcherc_text_searcher): Boolean;

                        Destructor DestroyOverride;
                      end// c_request_evaluator

Nous avons déjà présenté plusieurs évaluateurs sur ce site, et nous ne nous attarderons donc pas à présenter la tokenisation ou la vérification syntaxique. Mais voici comment l'évaluateur lance la recherche de chaque littéral:

function c_request_evaluator.f_evaluate_request(p_c_text_searcherc_text_searcher): boolean;
  type t_symbol_typeInteger;
  var // -- the position in the tokenized request
      l_token_indexInteger;
      // -- the current symbol
      l_symbol_typet_symbol_type;

  procedure read_next_symbol;
    begin
      l_symbol_type:= m_oa_tokens[l_token_index];
      Inc(l_token_index);
    end// read_next_symbol

  function f_evaluate_expressionBoolean;

    function f_evaluate_termBoolean;

      function f_evaluate_factorBoolean;
        var l_identifierString;
        begin
          case l_symbol_type of
            k_token_opening_parenthesis :
              begin
                trace_evaluation('parent');
                // -- skip (
                read_next_symbol;
                Result:= f_evaluate_expression;
                if l_symbol_typek_token_closing_parenthesis
                  then read_next_symbol
                  else display_evaluation_error('in "(expr)" manque )');
              end;

            k_token_NOT :
              begin
                read_next_symbol;
                Result:= NOT f_evaluate_factor;
                // -- do NOT read next symbol: was read by compile_expression
              end;

            else
              if l_symbol_type>= 0
                then begin
                    l_identifier:= m_c_request_identifiers[l_symbol_type];
                    // -- look if the symbol is in the text
                    Result:= p_c_text_searcher.f_found_string(l_identifier);
                    read_next_symbol;
                  end
                else begin
                    // -- avoid warnings
                    Result:= False;
                    display_evaluation_error('attend fact: k, var, (: 'IntToStr(l_symbol_type));
                  end;
          end// case
        end// f_evaluate_factor

      begin // f_evaluate_term
        Result:= f_evaluate_factor;

        while l_symbol_typek_token_AND do
        begin
          read_next_symbol;
          Result:= Result AND f_evaluate_factor;
        end// while l_symbol_type
      end// f_evaluate_term

    begin // f_evaluate_expression
      Result:= f_evaluate_term;

      while l_symbol_typek_token_OR do
      begin
        read_next_symbol;
        Result:= Result OR f_evaluate_term;
      end// while
    end// f_evaluate_expression

  begin // f_evaluate_request
    l_token_index:= 0;
    read_next_symbol;
    Result:= f_evaluate_expression;
  end// f_evaluate_request

Notez que:

  • le texte de la page a été initialisé une seule fois
  • la recherche est lancée dans f_evaluate_factor chaque fois que le facteur est une chaîne littérale
  • par rapport au programme de recherche textuelle (non CGI) nous avons légèrement simplifié l'analyse lexicale


3.5 - c_cgi_result_page_list

Il s'agit là d'une simple encapsulation des pages correspondant à la recherce, en utilisant une tStringList. Voyez les exemples de squelettes de tStringList sur ce site.



3.6 - c_cgi_html_page_builder

Cette classe récupère la liste des pages correspondant à la recherche et construit une page à la syntaxe .HTML.

 c_html_result_page_builder=
     class(c_basic_object)
       // -- m_name: the request (for the error display)
       m_c_html_pagetStringList;
       m_site_urlString;

       Constructor create_html_result_page_builder(p_nameString;
           p_site_urlString);

       procedure build_error_page(p_error_indexp_error_lengthInteger;
           p_error_messageString);
       procedure build_the_result_page(p_c_result_page_listc_result_page_list);

       Destructor DestroyOverride;
     end// c_html_result_page_builder

et voici l'exemple (partiel) de construction de page:

procedure c_html_result_page_builder.build_the_result_page(p_c_result_page_listc_result_page_list);

  procedure add_file_name(p_page_indexIntegerp_c_result_pagec_result_page);
    begin
      // ...
    end// add_file_name

  procedure add_bottom_links;
    begin
      // ...
    end// add_bottom_links

  var l_element_indexInteger;
      l_number_of_filesString;

  begin // build_result_page
    m_c_html_page:= tStringList.Create;

    with p_c_result_page_list do
      l_number_of_files:= '(trouvé 'IntToStr(f_result_page_count)+ ' sur '
          + IntToStr(m_total_page_count)+ ' pages)';

    with m_c_html_page do
    begin
      Add('<HTML>');
      Add('  <HEAD>');
      Add('    <TITLE>Résultats</TITLE>');
      Add('  </HEAD>');
      Add('  <BODY>');
      Add('    <H2><CENTER><FONT COLOR="#FF0000">'m_name
          + '</FONT><BR>'
          + l_number_of_files'</CENTER></H2>');

      with p_c_result_page_list do
        for l_element_index:= 0 to f_result_page_count- 1 do
          add_file_name(l_element_indexf_c_result_page(l_element_index));

      add_bottom_links;

      Add('  </BODY>');
      Add('</HTML>');
    end// with m_c_html_page
  end// build_the_result_page



3.7 - u_cgi_coliget_search

L'ensemble des classes est utilisé par l'unité principale, qui contient une seule procédure qui gère l'ensemble:
  • lecture et analyse de la requête reçue de serveur
  • tokenisation et vérification de la requête
  • chargement et analyse de chaque page, avec construction de la liste des pages correctes
  • construction de la page résultat HTML
  • envoi de la page résultat vers le serveur
Voici la procédure unique:

procedure do_search(p_write_segment,
    p_log_namep_form_search_root_log_prefix_name,
    p_site_urlString);

  // ...

  var l_requestString;
      l_c_html_result_page_builderc_html_result_page_builder;

  begin // do_search
    // -- get the request without % etc
    analyze_form_values(l_requestg_case_sensitive);

    if not g_case_sensitive
      then l_request:= LowerCase(l_request);

    l_c_html_result_page_builder:=
        c_html_result_page_builder.create_html_result_page_builder(l_request,
        p_site_url);

    // -- build the target file list
    evaluate_request(l_request);

    send_answer(g_output_name);

    l_c_html_result_page_builder.Free;
  end// do_search

L'analyze de la chaîne à la syntaxe CGI est réalisée ainsi:

procedure analyze_form_values(var pv_requestStringvar pv_case_sensitiveBoolean);

  function f_convert_cgi_question_string(p_cgi_stringString): String;
     // -- convert "q=uml&minuscule=on&
    begin
      // ...
    end// f_convert_cgi_question_string

  var l_raw_request_stringString;
      l_minuscule_positionInteger;
      l_minuscule_stringString;

  begin // analyze_form_values
    with c_form.create_form('forms'f_cgi_exe_path
        + p_write_segmentp_form_search_root_log_prefix_namedo
    begin
      get_form_result;

      l_raw_request_string:= m_raw_answer_string;
      pv_request:= f_convert_cgi_question_string(l_raw_request_string);

      l_minuscule_position:= Pos('minuscule'pv_request);
      if l_minuscule_position> 0
        then begin
            // -- the end of the string
            l_minuscule_string:= Copy(pv_requestl_minuscule_position,
                Length(pv_request)+ 1- l_minuscule_position);
            pv_case_sensitive:= Pos('=on'l_minuscule_string)<= 0;
            pv_request:= Trim(Copy(pv_request, 1, l_minuscule_position- 1));
          end
        else pv_case_sensitive:= True;

      Free;
    end// with c_form.create_form
  end// analyze_form_values

get_form_result est la routine que nous avons déjà présentée dans le tutorial CGI



Et la procédure qui lance les test:

procedure evaluate_request(p_requestString);
  var l_c_result_page_listc_result_page_list;
      l_search_pathString;
  begin
    with c_site_searcher.create_site_searcher('search'do
    begin
      if m_c_request_evaluator.f_tokenize_request(p_request)
        then begin
            // -- check the syntax
            if m_c_request_evaluator.f_check_syntax
              then begin
                  l_c_result_page_list:= c_result_page_list.create_result_page_list('page_list');

                  if f_is_debug
                    then l_search_path:= g_debug_search_path
                    else begin
                        l_search_path:= f_cgi_exe_path;
                        // -- remove scripts
                        l_search_path:= f_remove_end_if_end_is_equal_to(Lowercase(l_search_path), 'scripts\');
                      end;

                  find_pages(l_search_pathg_case_sensitivel_c_result_page_list);

                  // -- now use the page list to build the answer
                  l_c_html_result_page_builder.build_the_result_page(l_c_result_page_list);

                  l_c_result_page_list.Free;
                end
              else begin
                  // -- build the syntax error answer
                  with m_c_request_evaluator do
                    l_c_html_result_page_builder.build_error_page(m_error_index,
                        m_error_lengthm_error_message);
                end;
          end
        else begin
            // -- build the lexical error answer
            with m_c_request_evaluator do
              l_c_html_result_page_builder.build_error_page(m_error_index,
                  m_error_lengthm_error_message);
          end;

      Free;
    end// with c_site_searcher
  end// evaluate_request



3.8 - Test du CGI

Le test d'un programme CGI est assez délicat:
  • il faut disposer d'un serveur Web
  • les messages de mise au point ne peuvent être affichés sur une Forme, car le programme travaille en mode console
Pour le serveur Web:
  • une première solution est de construire un petit serveur en Delphi. Un tel "serveur", qui ne fait qu'utiliser des tSocket pour implémenter le protocole HTTP, avait été présenté dans Pascalissime. Nous avions utilisé une version voisine pour nos essais SOAP.

    L'inconvénient est que nous pourrons toujours ajuster le serveur et le client pour qu'ils collaborent correctement, mais les véritables serveurs ont leur volonté propre, et de ce fait, nos test ne sont peut-être pas réalistes

  • Dans notre précédent article, nous avions utilisé le serveur personnel Windows (PWS) pour notre mise au point. C'est une possibilité

  • plusieurs serveurs ont été fournis par Borland ou d'autres développeurs Delphi. Le fait que Borland ne propose pas de serveur souligne le fait que l'émulation d'Internet Server ou d'Apache n'est pas trivial

  • nous pouvons utiliser le serveur de notre Provider, et tester sur son serveur. Avec les vitesses et les coûts actuels d'ADSL, c'est la solution que nous avons choisi: nous mettons au point le programme pour qu'il compile et fonctionne raisonablement en mode "non-cgi", et nous testons la partie CGI sur le véritable serveur


Quels sont alors les test à effectuer avant le placer le CGI sur le serveur ? Eh bien
  • nous vérifions que les unités sont cohérentes
  • nous effectuons des essais sans la partie entrée (lecture de la requête CGI) et sans la partie sortie (émission de la page Web)


Le premier test consiste simplement à tester la partie qui analyze la requête, charge les pages, et construit une liste de pages correspondant aux pages correctes.

Voici un exemple de test de la partie recherche pure:

procedure TForm1.text_search_Click(SenderTObject);
  begin
    with c_site_searcher.create_site_searcher('search'do
    begin
      search_the_site(k_pathEdit1.Text,
          not Form1.minuscules_.Checked);

      Form1.Memo1.Lines.Assign(m_c_ok_file_list);
      Free;
    end// with c_site_searcher
  end// text_search_Click

dont voici un exemple d'utilisation:

test de la recherche



Le second test est effectué

  • en n'appelant depuis le programme de test qu'une seule méthode qui effectue tout le traitement.
  • cette méthode est la même que celle qui sera invoquée par le véritable programme
  • la méthode est dans une unité qui ne contient aucun élément de visualisation sur la forme.

    En revanche cette unité utilise un journal ASCII dans lequel nous plaçons des chaînes de mise au point

Voici la méthode d'appel:

procedure TForm1.text_search_Click(SenderTObject);
  begin
    with c_site_searcher.create_site_searcher('search'do
    begin
      search_the_site(k_pathEdit1.Text,
          not Form1.minuscules_.Checked);

      Form1.Memo1.Lines.Assign(m_c_ok_file_list);
      Free;
    end// with c_site_searcher
  end// text_search_Click

Cette procédure n'affiche rien (par construction), mais crée un page dans le répertoire de l'EXE et remplit un fichier log, si nous en avons créé un. C'est bien le cas, et le bouton "load_log" permet de visualiser les étapes clé de la recherche. Voici un exemple:

test de la recherche

Notez que c'est le même journal qui sera déposé dans un fichier chez notre hébergeur et qui permettra la vérification du bon fonctionnement en mode distant.



Le véritable programme principal du CGI est le suivant:

procedure TForm1.text_search_Click(SenderTObject);
  begin
    with c_site_searcher.create_site_searcher('search'do
    begin
      search_the_site(k_pathEdit1.Text,
          not Form1.minuscules_.Checked);

      Form1.Memo1.Lines.Assign(m_c_ok_file_list);
      Free;
    end// with c_site_searcher
  end// text_search_Click

Et vous constaterez qu'il utilise bien la même méthode que le programme de test.




4 - Améliorations

Parmi les améliorations qui peuvent être apportées:
  • utiliser un algorithme de recherche de plusieurs chaînes à la fois (Teuhola Raita de mémoire)
  • au niveau de résultat présenter les mots clés et quelques mots les entourant (KWIC: keyword in context), plutôt que de présenter le résumé de nos pages
  • la recherche intégrale de toutes les pages de notre site peut sembler excessive. Notre précédente version utilisait une liste de mots "pertinents", mais obligeait à construire manuellement cette liste pour chaque page. Nos pages contiennent obligatoirement le résumé et les mots-clé, mais en plus il faillait construire cette liste supplémentaire. Une solution intermédiaire serait la construction d'un "thésaurus pertinent" par page: une liste de tous les mots en supprimant les mots triviaux (le la begin etc)

    Actuellement, la recherche notre site contient une centaine de pages, et le volume recherché est de l'ordre de l'ordre de 2 megas. La première recherche sur notre PC (1.400MHz et 256 Meg de mémoire) est de l'ordre de 5 à 10 secondes. Les recherches suivantes sont beaucoup plus rapides à cause des caches.




5 - Télécharger les sources

Pour télécharger, cliquez:

Les .ZIP comprennent:
  • le .DPR, la forme principale, les formes annexes eventuelles
  • les fichiers de paramètres (le schéma et le batch de création)
  • dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque .ZIP est autonaume)
Ces .ZIP contiennent des chemins RELATIFS. Par conséquent:
  • créez un répertoire n'importe où sur votre machine
  • placez le .ZIP dans ce répertoire
  • dézippez et les sous-répertoires nécessaires seront créés
  • compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre, de DLL ou autre). Pour supprimer le projet, effacez le répertoire.



Comme d'habitude:

  • nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
  • tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
  • plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
    Nom :
    E-mail :
    Commentaires * :
     

  • et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.



6 - Références




7 - L'auteur

John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le conseil (composants, architecture, test) et la formation. Son site contient des articles avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client.
Créé: jan-04. Maj: aou-15  148 articles, 471 sources .ZIP, 2.021 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2015
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
    + web_internet_sockets
      – http_client
      – moteur_de_recherche
      – javascript_debugger
      + asp_net
      – client_serveur_tcp
      – site_editor
      – pascal_to_html
      – cgi_form
      + les_logs_http_bruts
      + les_blogs
      – intraweb_tutorial
      + services_web
      – serveur_web_iis
      – client_serveur_pop3
    + prog_objet_composants
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog

Formation Delphi 7 complete L'outil de développpement, le langage de programmation, les composants, les bases de données et la programmation Internet - 5 jours
Formation Programmation Objet Delphi La programmation objet: les types, l'encapsulation, l'héritage, le polymorphisme - 3 jours