プロジェクト

全般

プロフィール

プラグインDMSFの検索エンジンをHyper Estraierへ替える » 履歴 » リビジョン 9

リビジョン 8 (Masanori Machii, 2011/10/25 17:57) → リビジョン 9/10 (Masanori Machii, 2011/10/27 18:18)

h1. プラグインDMSFの検索エンジンをHyper Estraierへ替える 

 h2. DMSFとRedmineの関係 

 DMSFはRedmineが標準で備える種々の文書管理を一つにまとめて置き換えようとしたものです.ユーザーインターフェイスは優れていますが,残念ながら検索エンジンにXapianを利用しており,そのままでは日本語に対応できません.そこで,Hyper Estraierへ置き換えるわけですが,改造はエンジンのみの置き換えで済むので,DMSFの基本的な構造は変わりません. 

 その要点は次のとおりです. 

 bq. 注)$REDMINEは自動的に設定される環境編ではありません.ここでは便宜的に使います. 

 # {{fn(DMSFが管理する文書,ファイル名が同じであっても,インデックスは異なるよう,接頭辞が付いています.)}}はすべて $REDMINE/files/dnsf へ置かれる. 
 # インデックスファイルを作成するプロセスが定期的に動作し,それは {{fn($REDMINE/files へ置かれる,これは管理者が設定できるのですが,わかりやすくするため,推奨としました.)}}. 

 【例】"whitemine" というディレクトリにRedmineがインストールされている場合 
 !ファイル保存フォルダ.png! 

 !インデックスデータベース.png! 

 これは,DMSFの設定画面にも現れています.またこの画面では,Xapianに由来する設定がありますが,それがHyper Estraierでは不要になります. 

 !Xapianの設定.png! 

 ここでは,オリジナルを(ちょっと手を抜いた)次の方針で改造することにします. 

 # Web画面に "Xapian" 由来の設定などがあれば,それはそのままにし,削除しない. 
 # Web画面に Estraier に対応するものがあれば,それは "Estraier" へと修正する.(現バージョンではない) 
 # エラーメッセージなどに "Xapian" という文字列があれば,それは    "Estraier" へと上書きする. 
 # ソースコード中 "Xapian" を呼び出す部分は削除する.(ロジックの変更) 
 # ソースコード中 "Xapian" という変数名がそのまま    "Estraier" へと置き換えられる部分は "Estraier" へと修正する. 
 # -ソースコード中 "Xapian" というラベル名称はそのまま利用する.- 
 # バージョンは,日本語版であることを示すため,1.yy.zz-JP とする.  

 

 さて,変更するファイルは次の3つです. 

 # * config/locales/ja.yml 
 # init.rb 
 # * app/views/settings/_dmsf_settings.erb 
 # * app/models/dmsf_file.rb 

 h2. config/locales/ja.ymlの修正 

 コード中のラベルも含めて,Xapian から Estraier へと修正しています. 

 <pre> 
 @@ -156,7 +156,7 @@ 
    :error_file_storage_directory_does_not_exist: "ファイル保存フォルダが存在せず作ることもできません"  
    :error_file_can_not_be_created: "ファイルを保存フォルダに作ることができません"  
    :error_wrong_zip_encoding: "Zip エンコーディングが正しくありません"  
 -    :warning_xapian_not_available: "Xapian が利用できる状態になっていません"  
 +    :warning_estraier_not_available: "Hyper Estraier が利用できる状態になっていません"  
    :menu_dmsf: "DMSF"  
    :label_physical_file_delete: "物理ファイルの削除"  
    :user_is_not_project_member: "あなたはプロジェクトのメンバーではありません" 
 </pre> 


 h2. init.rbの修正 

 先の方針に基づいて,バージョン番号をユニークにします. 

 <pre> 
 @@ -29,7 +29,7 @@ 
   name "DMSF" 
   author "Vít Jonáš" 
   description "Document Management System Features" 
 -    version "1.2.1" 
 +    version "1.2.1-JP" 
   url "http://code.google.com/p/redmine-dmsf/" 
   author_url "mailto:vit.jonas@gmail.com" 
 </pre> 



 h2. app/views/settings/_dmsf_settings.erb 

 ライブラリ名称を xapian から estraier に変更します.加えて,先の ya.yml に対応する警告のラベルと一致させます. 
 抽出方針(Stem)に相当する部分も,文字列を xapian から estraier へと修正していますが,Xapian は機能しませんので,これはおまけと考えて下さい.  


 <pre> 
 @@ -75,22 +75,22 @@ 

  <hr /> 
  <% begin 
 -        require 'xapian' 
 -        xapian_disabled = false 
 +        require 'estraier' 
 +        estraier_disabled = false 
     rescue LoadError      %> 
 -      <p class="warning"><%= l(:warning_xapian_not_available) %></p>  
 -<%       xapian_disabled = true 
 +      <p class="warning"><%= l(:warning_estraier_not_available) %></p>  
 +<%       estraier_disabled = true 
     end %> 

  <p> 
    <%=content_tag(:label, l(:label_index_database) + ":") %> 
 -    <%=text_field_tag 'settings[dmsf_index_database]', @settings['dmsf_index_database'], :disabled => xapian_disabled, :size=>50 %><br/> 
 +    <%=text_field_tag 'settings[dmsf_index_database]', @settings['dmsf_index_database'], :disabled => estraier_disabled, :size=>50 %><br/> 
    (<%=l(:label_default)%>: <%="#{RAILS_ROOT}/files/dmsf_index"%>) 
  </p> 

  <p> 
    <%=content_tag(:label, l(:label_stemming_language) + ":") %> 
 -    <%=text_field_tag 'settings[dmsf_stemming_lang]', @settings['dmsf_stemming_lang'], :disabled => xapian_disabled %><br/> 
 +    <%=text_field_tag 'settings[dmsf_stemming_lang]', @settings['dmsf_stemming_lang'], :disabled => estraier_disabled %><br/> 
    (<%=l(:label_default)%>: english )<br/> 
    <br/> 
    <%=l(:note_possible_values)%>: danish dutch english finnish french german german2 hungarian italian kraaij_pohlmann lovins norwegian porter portuguese romanian russian spanish swedish turkish (<%=l(:note_pass_none_to_disable_stemming)%>) 
 @@ -98,9 +98,9 @@ 

  <p> 
    <%=content_tag(:label, l(:label_stem_strategy) + ":")%> 
 -    <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_NONE', @settings['dmsf_stemming_strategy'] == 'STEM_NONE', :disabled => xapian_disabled, :checked=>true    %> <%=l(:option_stem_none)%><br> 
 -    <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_SOME', @settings['dmsf_stemming_strategy'] == 'STEM_SOME', :disabled => xapian_disabled    %> <%=l(:option_stem_some)%><br> 
 -    <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_ALL', @settings['dmsf_stemming_strategy'] == 'STEM_ALL', :disabled => xapian_disabled    %> <%=l(:option_stem_all)%><br> 
 +    <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_NONE', @settings['dmsf_stemming_strategy'] == 'STEM_NONE', :disabled => estraier_disabled, :checked=>true    %> <%=l(:option_stem_none)%><br> 
 +    <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_SOME', @settings['dmsf_stemming_strategy'] == 'STEM_SOME', :disabled => estraier_disabled    %> <%=l(:option_stem_some)%><br> 
 +    <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_ALL', @settings['dmsf_stemming_strategy'] == 'STEM_ALL', :disabled => estraier_disabled    %> <%=l(:option_stem_all)%><br> 
    <br/> 
    <%=l(:label_stemming_description)%>: 
    <br> 
 </pre> 


 h2. app/models/dmsf_file.rb 

 検索機能本体の修正をします.主な修正の要点は次のとおりです. 

 XapianではなくてEstraier を呼び出します.呼び出し時のエラー処理はXapianと同様です. 
 エラーや警告のメッセージも, Estraier のそれへと修正します. 
 インデックスデータベースへのアクセスメソッドは,Xapianとは大幅に異なりますので,ざっくりと削除し,上書きします. 

 まず,コードの冒頭には,ライブラリの存在の有無によるエラー処理があります. 

 <pre> 
 @@ -17,11 +17,11 @@ 
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA    02110-1301, USA. 

  begin 
 -    require 'xapian' 
 -    $xapian_bindings_available = true 
 +    require 'estraier' 
 +    $estraier_bindings_available = true 
  rescue LoadError 
 -    Rails.logger.info "REDMAIN_XAPIAN ERROR: No Ruby bindings for Xapian installed !!. PLEASE install Xapian search engine interface for Ruby."  
 -    $xapian_bindings_available = false 
 +    Rails.logger.info "REDMAIN_ESTRAIER ERROR: No Ruby bindings for Estraier installed !!. PLEASE install Hyper Estraier search engine interface for Ruby."  
 +    $estraier_bindings_available = false 
  end 

 </pre> 

 次に,インデックスデータベースへのアクセスについて,エラー処理があります. 

 <pre> 
 @@ -260,46 +260,37 @@ 
        end 
      end 

 -      if !options[:titles_only] && $xapian_bindings_available 
 +      if !options[:titles_only] && $estraier_bindings_available 
        database = nil 
        begin 
 -          database = Xapian::Database.new(Setting.plugin_redmine_dmsf["dmsf_index_database"].strip) 
 +          database = Estraier::Database::new 
 +          database.open(Setting.plugin_redmine_dmsf["dmsf_index_database"].strip, Estraier::Database::DBREADER)  
        rescue 
 -          Rails.logger.warn "REDMAIN_XAPIAN ERROR: Xapian database is not properly set or initiated or is corrupted."  
 +          Rails.logger.warn "REDMAIN_ESTRAIER ERROR: Estraier database is not properly set or initiated or is corrupted."  
        end 
 </pre> 


 database.nil 以下は大幅に構造が異なりますので,ざっくりと入れ替えましょう. 

 <pre> 
        unless database.nil? 
 -          enquire = Xapian::Enquire.new(database) 
 +          # create a search condition object 
 +          cond = Estraier::Condition::new 

 -          queryString = tokens.join(' ') 
 -          qp = Xapian::QueryParser.new() 
 -          stemmer = Xapian::Stem.new(Setting.plugin_redmine_dmsf['dmsf_stemming_lang'].strip) 
 -          qp.stemmer = stemmer 
 -          qp.database = database 
 -         
 -          case Setting.plugin_redmine_dmsf['dmsf_stemming_strategy'].strip 
 -            when "STEM_NONE" then qp.stemming_strategy = Xapian::QueryParser::STEM_NONE 
 -            when "STEM_SOME" then qp.stemming_strategy = Xapian::QueryParser::STEM_SOME 
 -            when "STEM_ALL" then qp.stemming_strategy = Xapian::QueryParser::STEM_ALL 
 -          end 
 -       
 -          if options[:all_words] 
 -            qp.default_op = Xapian::Query::OP_AND 
 -          else   
 -            qp.default_op = Xapian::Query::OP_OR 
 -          end 
 -         
 -          query = qp.parse_query(queryString) 
 -   
 -          enquire.query = query 
 -          matchset = enquire.mset(0, 1000) 
 -     
 -          unless matchset.nil? 
 -            matchset.matches.each {|m| 
 -              docdata = m.document.data{url} 
 -              dochash = Hash[*docdata.scan(/(url|sample|modtime|type|size)=\/?([^\n\]]+)/).flatten] 
 -              filename = dochash["url"] 
 -              if !filename.nil? 
 +          # set the search phrase to the search condition object 
 +          queryString = tokens.join(options[:all_words] ? ' AND ': ' OR ') 
 +          cond.set_phrase(queryString ) 
 + 
 +          # get the result of search 
 +          result = database.search(cond) 
 + 
 +          if result 
 +            # for each document in the result 
 +            dnum = result.doc_num 
 +            for i in 0...dnum 
 +              # retrieve the document object 
 +              doc = database.get_doc(result.get_doc_id(i), 0) 
 +              next unless doc 
 +              # display attributes 
 +              uri = doc.attr("@uri") 
 +              if uri 
 +                filename = uri.sub(/.*\//, '') 
                dmsf_attrs = filename.split("_") 
                next if dmsf_attrs[1].blank? 
                next unless results.select{|f| f.id.to_s == dmsf_attrs[1]}.empty? 
 </pre> 

 最後にインデックスデータベースを閉じて終了です. 

 <pre> 

 @@ -331,9 +322,14 @@ 
                  end 
                end 
              end 
 -            } 
          end 
        end     
 + 
 +          # close the database 
 +          unless database.close 
 +            Rails.logger.warn(database.err_msg(database.error)) 
 +          end 
 +        end # unless database.nil? 
      end 

      [results, results_count] 
 </pre> 

 修正は以上です.