プロジェクト

全般

プロフィール

プラグインDMSFの検索エンジンをHyper Estraierへ替える » 履歴 » バージョン 7

Masanori Machii, 2011/10/21 17:29

1 1 Masanori Machii
h1. プラグインDMSFの検索エンジンをHyper Estraierへ替える
2
3
h2. DMSFとRedmineの関係
4
5 2 Masanori Machii
DMSFはRedmineが標準で備える種々の文書管理を一つにまとめて置き換えようとしたものです.ユーザーインターフェイスは優れていますが,残念ながら検索エンジンにXapianを利用しており,そのままでは日本語に対応できません.そこで,Hyper Estraierへ置き換えるわけですが,改造はエンジンのみの置き換えで済むので,DMSFの基本的な構造は変わりません.
6 1 Masanori Machii
7
その要点は次のとおりです.
8
9
bq. 注)$REDMINEは自動的に設定される環境編ではありません.ここでは便宜的に使います.
10
11
# {{fn(DMSFが管理する文書,ファイル名が同じであっても,インデックスは異なるよう,接頭辞が付いています.)}}はすべて $REDMINE/files/dnsf へ置かれる.
12
# インデックスファイルを作成するプロセスが定期的に動作し,それは {{fn($REDMINE/files へ置かれる,これは管理者が設定できるのですが,わかりやすくするため,推奨としました.)}}.
13 3 Masanori Machii
14 5 Masanori Machii
【例】"whitemine" というディレクトリにRedmineがインストールされている場合
15
!ファイル保存フォルダ.png!
16 4 Masanori Machii
17 5 Masanori Machii
!インデックスデータベース.png!
18 4 Masanori Machii
19
これは,DMSFの設定画面にも現れています.またこの画面では,Xapianに由来する設定がありますが,それがHyper Estraierでは不要になります.
20
21 5 Masanori Machii
!Xapianの設定.png!
22 1 Masanori Machii
23
ここでは,オリジナルを(ちょっと手を抜いた)次の方針で改造することにします.
24
25 5 Masanori Machii
# Web画面に "Xapian" 由来の設定などがあれば,それはそのままにし,削除しない.
26
# Web画面に Estraier に対応するものがあれば,それは "Estraier" へと修正する.(現バージョンではない)
27
# エラーメッセージなどに "Xapian" という文字列があれば,それは  "Estraier" へと上書きする.
28 1 Masanori Machii
# ソースコード中 "Xapian" を呼び出す部分は削除する.(ロジックの変更)
29 5 Masanori Machii
# ソースコード中 "Xapian" という変数名がそのまま  "Estraier" へと置き換えられる部分は "Estraier" へと修正する.
30
# ソースコード中 "Xapian" というラベル名称はそのまま利用する.
31
32
さて,変更するファイルは次の3つです.
33
34
* config/locales/ja.yml
35
* app/views/settings/_dmsf_settings.erb
36
* app/models/dmsf_file.rb
37
38
h2. config/locales/ja.ymlの修正
39
40 7 Masanori Machii
コード中のラベルも含めて,Xapian から Estraier へと修正しています.
41 5 Masanori Machii
42
<pre>
43
@@ -156,7 +156,7 @@
44
   :error_file_storage_directory_does_not_exist: "ファイル保存フォルダが存在せず作ることもできません" 
45
   :error_file_can_not_be_created: "ファイルを保存フォルダに作ることができません" 
46
   :error_wrong_zip_encoding: "Zip エンコーディングが正しくありません" 
47
-  :warning_xapian_not_available: "Xapian が利用できる状態になっていません" 
48 7 Masanori Machii
+  :warning_estraier_not_available: "Hyper Estraier が利用できる状態になっていません" 
49 5 Masanori Machii
   :menu_dmsf: "DMSF" 
50
   :label_physical_file_delete: "物理ファイルの削除" 
51 7 Masanori Machii
   :user_is_not_project_member: "あなたはプロジェクトのメンバーではありません"
52 5 Masanori Machii
</pre>
53 1 Masanori Machii
54 5 Masanori Machii
h2. app/views/settings/_dmsf_settings.erb
55 1 Masanori Machii
56 7 Masanori Machii
ライブラリ名称を xapian から estraier に変更します.加えて,先の ya.yml に対応する警告のラベルと一致させます.
57
抽出方針(Stem)に相当する部分も,文字列を xapian から estraier へと修正していますが,Xapian は機能しませんので,これはおまけと考えて下さい. 
58 5 Masanori Machii
59 7 Masanori Machii
60 5 Masanori Machii
<pre>
61 7 Masanori Machii
@@ -75,22 +75,22 @@
62 1 Masanori Machii
63
 <hr />
64
 <% begin
65
-      require 'xapian'
66 7 Masanori Machii
-      xapian_disabled = false
67 1 Masanori Machii
+      require 'estraier'
68 7 Masanori Machii
+      estraier_disabled = false
69 1 Masanori Machii
    rescue LoadError    %>
70 7 Masanori Machii
-    <p class="warning"><%= l(:warning_xapian_not_available) %></p> 
71
-<%     xapian_disabled = true
72
+    <p class="warning"><%= l(:warning_estraier_not_available) %></p> 
73
+<%     estraier_disabled = true
74
    end %>
75
76
 <p>
77
   <%=content_tag(:label, l(:label_index_database) + ":") %>
78
-  <%=text_field_tag 'settings[dmsf_index_database]', @settings['dmsf_index_database'], :disabled => xapian_disabled, :size=>50 %><br/>
79
+  <%=text_field_tag 'settings[dmsf_index_database]', @settings['dmsf_index_database'], :disabled => estraier_disabled, :size=>50 %><br/>
80
   (<%=l(:label_default)%>: <%="#{RAILS_ROOT}/files/dmsf_index"%>)
81
 </p>
82
83
 <p>
84
   <%=content_tag(:label, l(:label_stemming_language) + ":") %>
85
-  <%=text_field_tag 'settings[dmsf_stemming_lang]', @settings['dmsf_stemming_lang'], :disabled => xapian_disabled %><br/>
86
+  <%=text_field_tag 'settings[dmsf_stemming_lang]', @settings['dmsf_stemming_lang'], :disabled => estraier_disabled %><br/>
87
   (<%=l(:label_default)%>: english )<br/>
88
   <br/>
89
   <%=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)%>)
90
@@ -98,9 +98,9 @@
91
92
 <p>
93
   <%=content_tag(:label, l(:label_stem_strategy) + ":")%>
94
-  <%=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>
95
-  <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_SOME', @settings['dmsf_stemming_strategy'] == 'STEM_SOME', :disabled => xapian_disabled  %> <%=l(:option_stem_some)%><br>
96
-  <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_ALL', @settings['dmsf_stemming_strategy'] == 'STEM_ALL', :disabled => xapian_disabled  %> <%=l(:option_stem_all)%><br>
97
+  <%=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>
98
+  <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_SOME', @settings['dmsf_stemming_strategy'] == 'STEM_SOME', :disabled => estraier_disabled  %> <%=l(:option_stem_some)%><br>
99
+  <%=radio_button_tag 'settings[dmsf_stemming_strategy]', 'STEM_ALL', @settings['dmsf_stemming_strategy'] == 'STEM_ALL', :disabled => estraier_disabled  %> <%=l(:option_stem_all)%><br>
100
   <br/>
101
   <%=l(:label_stemming_description)%>:
102
   <br>
103 1 Masanori Machii
</pre>
104 5 Masanori Machii
105 1 Masanori Machii
106
h2. app/models/dmsf_file.rb
107
108
検索機能本体の修正をします.主な修正の要点は次のとおりです.
109
110
XapianではなくてEstraier を呼び出します.呼び出し時のエラー処理はXapianと同様です.
111 7 Masanori Machii
エラーや警告のメッセージも, Estraier のそれへと修正します.
112 1 Masanori Machii
インデックスデータベースへのアクセスメソッドは,Xapianとは大幅に異なりますので,ざっくりと削除し,上書きします.
113 7 Masanori Machii
114
まず,コードの冒頭には,ライブラリの存在の有無によるエラー処理があります.
115
116
<pre>
117
@@ -17,11 +17,11 @@
118
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
119
120
 begin
121
-  require 'xapian'
122
-  $xapian_bindings_available = true
123
+  require 'estraier'
124
+  $estraier_bindings_available = true
125
 rescue LoadError
126
-  Rails.logger.info "REDMAIN_XAPIAN ERROR: No Ruby bindings for Xapian installed !!. PLEASE install Xapian search engine interface for Ruby." 
127
-  $xapian_bindings_available = false
128
+  Rails.logger.info "REDMAIN_ESTRAIER ERROR: No Ruby bindings for Estraier installed !!. PLEASE install Hyper Estraier search engine interface for Ruby." 
129
+  $estraier_bindings_available = false
130
 end
131
132
</pre>
133
134
次に,インデックスデータベースへのアクセスについて,エラー処理があります.
135
136
<pre>
137
@@ -260,46 +260,37 @@
138
       end
139
     end
140
141
-    if !options[:titles_only] && $xapian_bindings_available
142
+    if !options[:titles_only] && $estraier_bindings_available
143
       database = nil
144
       begin
145
-        database = Xapian::Database.new(Setting.plugin_redmine_dmsf["dmsf_index_database"].strip)
146
+        database = Estraier::Database::new
147
+        database.open(Setting.plugin_redmine_dmsf["dmsf_index_database"].strip, Estraier::Database::DBREADER) 
148
       rescue
149
-        Rails.logger.warn "REDMAIN_XAPIAN ERROR: Xapian database is not properly set or initiated or is corrupted." 
150
+        Rails.logger.warn "REDMAIN_ESTRAIER ERROR: Estraier database is not properly set or initiated or is corrupted." 
151
       end
152
</pre>
153
154
155
database.nil 以下は大幅に構造が異なりますので,ざっくりと入れ替えましょう.
156
157
<pre>
158
       unless database.nil?
159
-        enquire = Xapian::Enquire.new(database)
160
+        # create a search condition object
161
+        cond = Estraier::Condition::new
162
163
-        queryString = tokens.join(' ')
164
-        qp = Xapian::QueryParser.new()
165
-        stemmer = Xapian::Stem.new(Setting.plugin_redmine_dmsf['dmsf_stemming_lang'].strip)
166
-        qp.stemmer = stemmer
167
-        qp.database = database
168
-        
169
-        case Setting.plugin_redmine_dmsf['dmsf_stemming_strategy'].strip
170
-          when "STEM_NONE" then qp.stemming_strategy = Xapian::QueryParser::STEM_NONE
171
-          when "STEM_SOME" then qp.stemming_strategy = Xapian::QueryParser::STEM_SOME
172
-          when "STEM_ALL" then qp.stemming_strategy = Xapian::QueryParser::STEM_ALL
173
-        end
174
-      
175
-        if options[:all_words]
176
-          qp.default_op = Xapian::Query::OP_AND
177
-        else  
178
-          qp.default_op = Xapian::Query::OP_OR
179
-        end
180
-        
181
-        query = qp.parse_query(queryString)
182
-  
183
-        enquire.query = query
184
-        matchset = enquire.mset(0, 1000)
185
-    
186
-        unless matchset.nil?
187
-          matchset.matches.each {|m|
188
-            docdata = m.document.data{url}
189
-            dochash = Hash[*docdata.scan(/(url|sample|modtime|type|size)=\/?([^\n\]]+)/).flatten]
190
-            filename = dochash["url"]
191
-            if !filename.nil?
192
+        # set the search phrase to the search condition object
193
+        queryString = tokens.join(options[:all_words] ? ' AND ': ' OR ')
194
+        cond.set_phrase(queryString )
195
+
196
+        # get the result of search
197
+        result = database.search(cond)
198
+
199
+        if result
200
+          # for each document in the result
201
+          dnum = result.doc_num
202
+          for i in 0...dnum
203
+            # retrieve the document object
204
+            doc = database.get_doc(result.get_doc_id(i), 0)
205
+            next unless doc
206
+            # display attributes
207
+            uri = doc.attr("@uri")
208
+            if uri
209
+              filename = uri.sub(/.*\//, '')
210
               dmsf_attrs = filename.split("_")
211
               next if dmsf_attrs[1].blank?
212
               next unless results.select{|f| f.id.to_s == dmsf_attrs[1]}.empty?
213
</pre>
214
215
最後にインデックスデータベースを閉じて終了です.
216
217
<pre>
218
219
@@ -331,9 +322,14 @@
220
                 end
221
               end
222
             end
223
-          }
224
         end
225
       end    
226
+
227
+        # close the database
228
+        unless database.close
229
+          Rails.logger.warn(database.err_msg(database.error))
230
+        end
231
+      end # unless database.nil?
232
     end
233
234
     [results, results_count]
235
</pre>
236
237
修正は以上です.