rabbit-slide-kou-mysql-and-postgresql-and-japanese-full-text-search-3 2016.9.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rabbit +1 -0
- data/README.rd +46 -0
- data/Rakefile +17 -0
- data/config.yaml +27 -0
- data/images/redmine-full-text-search-form.png +0 -0
- data/images/redmine-full-text-search-plugin-report.png +0 -0
- data/images/zulip-full-text-search-form.png +0 -0
- data/images/zulip-highlight.png +0 -0
- data/mroonga-and-pgroonga.rab +652 -0
- data/pdf/mysql-and-postgresql-and-japanese-full-text-search-3-mroonga-and-pgroonga.pdf +0 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3bc6d689060f78211a40d95c1205655a49bb908e
|
4
|
+
data.tar.gz: 6538eff06dc04f6ddb770b6d62dcf49e2886755a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8f940a73aa12fb0f0690bb01c9d87d2f5f2d7dce5f06d6d303083c231c4b6fd80452e56a3b5e750cf3f708f7b1255502d8ac5fde70c2df0b9b315dcd7cbd7049
|
7
|
+
data.tar.gz: 54ce10dcd062a0f535375f7ba9a00c3b67b92e81b7a8adb25d8d9d5a8a37fd04eed3aaccb379a5c5e1b1789efa6414fd8d85071f28e48c36f3a51de0b911063f
|
data/.rabbit
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
mroonga-and-pgroonga.rab
|
data/README.rd
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
= Mroonga・PGroonga利用方法
|
2
|
+
|
3
|
+
Mroonga・PGroongaをさわり始めた人向けにMroonga・PGroongaを利用する方法を具体的な事例を使いながら紹介します。
|
4
|
+
|
5
|
+
== ライセンス
|
6
|
+
|
7
|
+
=== スライド
|
8
|
+
|
9
|
+
CC BY-SA 4.0
|
10
|
+
|
11
|
+
原著作者名は以下の通りです。
|
12
|
+
|
13
|
+
* 須藤功平(またはKouhei Sutou)
|
14
|
+
|
15
|
+
=== 画像
|
16
|
+
|
17
|
+
==== Groonga・Mroonga・PGroongaのロゴ
|
18
|
+
|
19
|
+
CC BY 3.0
|
20
|
+
|
21
|
+
原著作者名は以下の通りです。
|
22
|
+
|
23
|
+
* Groongaプロジェクト
|
24
|
+
* Mroongaプロジェクト
|
25
|
+
* PGroongaプロジェクト
|
26
|
+
|
27
|
+
== 作者向け
|
28
|
+
|
29
|
+
=== 表示
|
30
|
+
|
31
|
+
rake
|
32
|
+
|
33
|
+
=== 公開
|
34
|
+
|
35
|
+
rake publish
|
36
|
+
|
37
|
+
== 閲覧者向け
|
38
|
+
|
39
|
+
=== インストール
|
40
|
+
|
41
|
+
gem install rabbit-slide-kou-mysql-and-postgresql-and-japanese-full-text-search-3
|
42
|
+
|
43
|
+
=== 表示
|
44
|
+
|
45
|
+
rabbit rabbit-slide-kou-mysql-and-postgresql-and-japanese-full-text-search-3.gem
|
46
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "rabbit/task/slide"
|
2
|
+
|
3
|
+
# Edit ./config.yaml to customize meta data
|
4
|
+
|
5
|
+
spec = nil
|
6
|
+
Rabbit::Task::Slide.new do |task|
|
7
|
+
spec = task.spec
|
8
|
+
# spec.files += Dir.glob("doc/**/*.*")
|
9
|
+
# spec.files -= Dir.glob("private/**/*.*")
|
10
|
+
# spec.add_runtime_dependency("YOUR THEME")
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Tag #{spec.version}"
|
14
|
+
task :tag do
|
15
|
+
sh("git", "tag", "-a", spec.version.to_s, "-m", "Publish #{spec.version}")
|
16
|
+
sh("git", "push", "--tags")
|
17
|
+
end
|
data/config.yaml
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
---
|
2
|
+
id: mysql-and-postgresql-and-japanese-full-text-search-3
|
3
|
+
base_name: mroonga-and-pgroonga
|
4
|
+
tags:
|
5
|
+
- rabbit
|
6
|
+
- mroonga
|
7
|
+
- mysql
|
8
|
+
- pgroonga
|
9
|
+
- postgresql
|
10
|
+
- full-text-search
|
11
|
+
presentation_date: 2016-09-29
|
12
|
+
version: 2016.9.29.0
|
13
|
+
licenses:
|
14
|
+
- CC BY 3.0
|
15
|
+
- CC BY-SA 4.0
|
16
|
+
slideshare_id: mysql-and-postgresql-and-japanese-full-text-search-3
|
17
|
+
speaker_deck_id:
|
18
|
+
ustream_id:
|
19
|
+
vimeo_id:
|
20
|
+
youtube_id:
|
21
|
+
author:
|
22
|
+
markup_language: :rd
|
23
|
+
name: 須藤功平
|
24
|
+
email: kou@clear-code.com
|
25
|
+
rubygems_user: kou
|
26
|
+
slideshare_user: kou
|
27
|
+
speaker_deck_user:
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,652 @@
|
|
1
|
+
= Mroonga\n(('note:と'))\nPGroonga
|
2
|
+
|
3
|
+
: subtitle
|
4
|
+
導入方法例
|
5
|
+
: author
|
6
|
+
須藤功平
|
7
|
+
: institution
|
8
|
+
クリアコード
|
9
|
+
: content-source
|
10
|
+
MySQLとPostgreSQLと日本語全文検索
|
11
|
+
: date
|
12
|
+
2016-09-29
|
13
|
+
: allotted-time
|
14
|
+
20m
|
15
|
+
: theme
|
16
|
+
groonga
|
17
|
+
|
18
|
+
= Mroonga・PGroonga
|
19
|
+
|
20
|
+
* Mroonga(むるんが)
|
21
|
+
* (('wait'))MySQLに\n
|
22
|
+
高速日本語全文検索機能を追加する\n
|
23
|
+
プロダクト
|
24
|
+
* PGroonga(ぴーじーるんが)
|
25
|
+
* (('wait'))PostgreSQLに\n
|
26
|
+
高速日本語全文検索機能を追加する\n
|
27
|
+
プロダクト
|
28
|
+
|
29
|
+
= 高速?
|
30
|
+
|
31
|
+
# image
|
32
|
+
# src = http://slide.rabbit-shocker.org/authors/kou/mysql-and-postgresql-and-japanese-full-text-search/mroonga-and-pgroonga.pdf
|
33
|
+
# page = 6
|
34
|
+
# relative_height = 80
|
35
|
+
|
36
|
+
(('tag:center'))
|
37
|
+
(('note:詳細は第1回の資料を参照'))\n
|
38
|
+
(('note:http://slide.rabbit-shocker.org/authors/kou/mysql-and-postgresql-and-japanese-full-text-search/'))
|
39
|
+
|
40
|
+
= 導入方法例
|
41
|
+
|
42
|
+
既存システムへの導入方法を紹介
|
43
|
+
|
44
|
+
* Redmine
|
45
|
+
* チケット管理システム
|
46
|
+
* Ruby on Redmineを使用
|
47
|
+
* Zulip
|
48
|
+
* チャットツール
|
49
|
+
* Djangoを使用
|
50
|
+
|
51
|
+
= Redmine
|
52
|
+
|
53
|
+
# image
|
54
|
+
# src = images/redmine-full-text-search-form.png
|
55
|
+
# relative_width = 100
|
56
|
+
|
57
|
+
= 全文検索プラグイン
|
58
|
+
|
59
|
+
GitHub: (('tag:x-small:okkez/redmine_full_text_search'))
|
60
|
+
|
61
|
+
* MySQL・PostgreSQL両方対応
|
62
|
+
* MySQLのときはMroongaを利用
|
63
|
+
* PostgreSQLのときはPGroongaを利用
|
64
|
+
|
65
|
+
= 速さ
|
66
|
+
|
67
|
+
(('tag:center'))
|
68
|
+
MySQL + Mroongaのケース
|
69
|
+
|
70
|
+
# RT
|
71
|
+
delimiter = [|]
|
72
|
+
|
73
|
+
プラグイン | チケット数 | 時間
|
74
|
+
|
75
|
+
なし | 約3000件 | 467ms
|
76
|
+
あり | 約3000件 | 93ms
|
77
|
+
あり | 約200万件 | 380ms
|
78
|
+
|
79
|
+
== スライドプロパティ
|
80
|
+
|
81
|
+
: groonga-product
|
82
|
+
|
83
|
+
mroonga
|
84
|
+
|
85
|
+
= 速さ:コメント
|
86
|
+
|
87
|
+
# image
|
88
|
+
# src = images/redmine-full-text-search-plugin-report.png
|
89
|
+
# relative_height = 85
|
90
|
+
|
91
|
+
(('note:https://twitter.com/akahane92/status/733832496945594368'))
|
92
|
+
|
93
|
+
== スライドプロパティ
|
94
|
+
|
95
|
+
: groonga-product
|
96
|
+
|
97
|
+
mroonga
|
98
|
+
|
99
|
+
= 使いどころ
|
100
|
+
|
101
|
+
* Mroonga
|
102
|
+
* (('wait'))速さが欲しい
|
103
|
+
* (('wait'))トランザクションはいらない
|
104
|
+
* PGroonga
|
105
|
+
* (('wait'))機能が欲しい
|
106
|
+
* (('wait'))トランザクションも欲しい
|
107
|
+
|
108
|
+
= Redmine
|
109
|
+
|
110
|
+
* トランザクション必須
|
111
|
+
* Mroongaを使うときは一工夫必要
|
112
|
+
* PGroongaはそのままで大丈夫
|
113
|
+
|
114
|
+
= Redmine+Mroonga:方針
|
115
|
+
|
116
|
+
* (('wait'))チケットテーブルは変えない
|
117
|
+
* (('wait'))全文検索用テーブルを別途作成
|
118
|
+
* (('wait'))全文検索用テーブルから\n
|
119
|
+
チケットテーブルを参照
|
120
|
+
|
121
|
+
== スライドプロパティ
|
122
|
+
|
123
|
+
: groonga-product
|
124
|
+
|
125
|
+
mroonga
|
126
|
+
|
127
|
+
= マイグレーション
|
128
|
+
|
129
|
+
# coderay ruby
|
130
|
+
|
131
|
+
def up
|
132
|
+
create_table(:fts_issues, # 全文検索用テーブル作成
|
133
|
+
id: false, # idは有効・無効どっちでも可
|
134
|
+
options: "ENGINE=Mroonga") do |t|
|
135
|
+
t.belongs_to :issue, index: true, null: false
|
136
|
+
t.string :subject, default: "", null: false
|
137
|
+
t.text :description, limit: 65535, null: false
|
138
|
+
end
|
139
|
+
execute("INSERT INTO " + # データをコピー
|
140
|
+
"fts_issues(issue_id, subject, description) " +
|
141
|
+
"SELECT id, subject, description FROM issues;")
|
142
|
+
add_index(:fts_issues, [:subject, :description],
|
143
|
+
type: "fulltext") # 静的インデックス構築(速い)
|
144
|
+
end
|
145
|
+
|
146
|
+
== スライドプロパティ
|
147
|
+
|
148
|
+
: groonga-product
|
149
|
+
|
150
|
+
mroonga
|
151
|
+
|
152
|
+
= モデル
|
153
|
+
|
154
|
+
# coderay ruby
|
155
|
+
|
156
|
+
class FtsIssue < ActiveRecord::Base
|
157
|
+
# 実際はissue_idカラムは主キーではない。
|
158
|
+
# 主キーなしのテーブルなので
|
159
|
+
# Active Recordをごまかしているだけ。
|
160
|
+
self.primary_key = :issue_id
|
161
|
+
belongs_to :issue
|
162
|
+
end
|
163
|
+
|
164
|
+
== スライドプロパティ
|
165
|
+
|
166
|
+
: groonga-product
|
167
|
+
|
168
|
+
mroonga
|
169
|
+
|
170
|
+
= 保存
|
171
|
+
|
172
|
+
# coderay ruby
|
173
|
+
|
174
|
+
class Issue
|
175
|
+
# この後にロールバックされることがあるのでカンペキではない
|
176
|
+
# 再度同じチケットを更新するかデータを入れ直せば直る
|
177
|
+
after_safe do |record|
|
178
|
+
fts_record =
|
179
|
+
FtsIssue.find_or_initialize_by(issue_id: record.id)
|
180
|
+
fts_record.subject = record.subject
|
181
|
+
fts_record.description = record.description
|
182
|
+
fts_record.save!
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
== スライドプロパティ
|
187
|
+
|
188
|
+
: groonga-product
|
189
|
+
|
190
|
+
mroonga
|
191
|
+
|
192
|
+
= 全文検索
|
193
|
+
|
194
|
+
# coderay ruby
|
195
|
+
|
196
|
+
issue.
|
197
|
+
joins(:fts_issue).
|
198
|
+
where(["MATCH(fts_issues.subject, " +
|
199
|
+
"fts_issues.description) " +
|
200
|
+
"AGAINST (? IN BOOLEAN MODE)",
|
201
|
+
# ↓デフォルトANDで全文検索
|
202
|
+
"*D+ #{keywords.join(', ')}"])
|
203
|
+
|
204
|
+
== スライドプロパティ
|
205
|
+
|
206
|
+
: groonga-product
|
207
|
+
|
208
|
+
mroonga
|
209
|
+
|
210
|
+
= Redmine+Mroonga:まとめ
|
211
|
+
|
212
|
+
* (('wait'))トランザクション必須
|
213
|
+
* 元テーブルを置き換えない
|
214
|
+
* 全文検索用テーブルを作成
|
215
|
+
* (('wait'))データ
|
216
|
+
* アプリが複数テーブルに保存
|
217
|
+
* (('wait'))全文検索
|
218
|
+
* ((*JOIN*))して((*MATCH AGAINST*))
|
219
|
+
|
220
|
+
== スライドプロパティ
|
221
|
+
|
222
|
+
: groonga-product
|
223
|
+
|
224
|
+
mroonga
|
225
|
+
|
226
|
+
= Redmine+PGroonga:方針
|
227
|
+
|
228
|
+
* (('wait'))全文検索用インデックス作成
|
229
|
+
* (('wait'))インデックスに主キーを含める
|
230
|
+
* 検索スコアーを取得するため
|
231
|
+
|
232
|
+
== スライドプロパティ
|
233
|
+
|
234
|
+
: groonga-product
|
235
|
+
|
236
|
+
pgroonga
|
237
|
+
|
238
|
+
= マイグレーション
|
239
|
+
|
240
|
+
# coderay ruby
|
241
|
+
|
242
|
+
def up
|
243
|
+
enable_extension("pgroonga")
|
244
|
+
add_index(:issues,
|
245
|
+
[:id, :subject, :description],
|
246
|
+
using: "pgroonga")
|
247
|
+
end
|
248
|
+
|
249
|
+
== スライドプロパティ
|
250
|
+
|
251
|
+
: groonga-product
|
252
|
+
|
253
|
+
pgroonga
|
254
|
+
|
255
|
+
= モデル
|
256
|
+
|
257
|
+
追加・変更なし
|
258
|
+
|
259
|
+
== スライドプロパティ
|
260
|
+
|
261
|
+
: groonga-product
|
262
|
+
|
263
|
+
pgroonga
|
264
|
+
|
265
|
+
= 保存
|
266
|
+
|
267
|
+
追加・変更なし
|
268
|
+
|
269
|
+
== スライドプロパティ
|
270
|
+
|
271
|
+
: groonga-product
|
272
|
+
|
273
|
+
pgroonga
|
274
|
+
|
275
|
+
= 全文検索
|
276
|
+
|
277
|
+
# coderay ruby
|
278
|
+
|
279
|
+
issue.
|
280
|
+
# 検索対象のカラムごとに
|
281
|
+
# クエリーを指定
|
282
|
+
where(["subject @@ ? OR " +
|
283
|
+
"description @@ ?",
|
284
|
+
keywords.join(", "),
|
285
|
+
keywords.join(", ")])
|
286
|
+
|
287
|
+
== スライドプロパティ
|
288
|
+
|
289
|
+
: groonga-product
|
290
|
+
|
291
|
+
pgroonga
|
292
|
+
|
293
|
+
= Redmine+PGroonga:まとめ
|
294
|
+
|
295
|
+
* (('wait'))インデックス追加のみでOK
|
296
|
+
* トランザクション対応
|
297
|
+
* データ保存も変更なし
|
298
|
+
* (('wait'))全文検索
|
299
|
+
|
300
|
+
# coderay sql
|
301
|
+
カラム1 @@ 'クエリー' OR
|
302
|
+
カラム2 @@ 'クエリー' OR ...
|
303
|
+
|
304
|
+
== スライドプロパティ
|
305
|
+
|
306
|
+
: groonga-product
|
307
|
+
|
308
|
+
pgroonga
|
309
|
+
|
310
|
+
= Redmine:まとめ
|
311
|
+
|
312
|
+
* (('wait'))速い!
|
313
|
+
* (('wait'))Mroonga
|
314
|
+
* 全文検索用テーブルで実現
|
315
|
+
* (('wait'))PGroonga
|
316
|
+
* 全文検索用インデックスで実現
|
317
|
+
|
318
|
+
= Zulip
|
319
|
+
|
320
|
+
# image
|
321
|
+
# src = images/zulip-full-text-search-form.png
|
322
|
+
# relative_height = 95
|
323
|
+
|
324
|
+
= Zulipの全文検索機能
|
325
|
+
|
326
|
+
* (('wait'))現在:textsearch
|
327
|
+
* PostgreSQL標準機能
|
328
|
+
* 英語のみ対応
|
329
|
+
* (('wait'))NEW!:PGroonga
|
330
|
+
* オプション(=PGroongaに切替可)
|
331
|
+
* 全言語対応(日本語を含む)
|
332
|
+
|
333
|
+
== スライドプロパティ
|
334
|
+
|
335
|
+
: groonga-product
|
336
|
+
|
337
|
+
pgroonga
|
338
|
+
|
339
|
+
= Zulip+PGroonga:方針
|
340
|
+
|
341
|
+
* 書き込み速度を落とさない
|
342
|
+
* チャットは書き込みが遅いと微妙
|
343
|
+
* インデックスは裏で更新\n
|
344
|
+
(('note:PGroongaならリアルタイム更新でも大丈夫かも'))
|
345
|
+
|
346
|
+
(('note:詳細:https://github.com/zulip/zulip/pull/700/files'))
|
347
|
+
|
348
|
+
== スライドプロパティ
|
349
|
+
|
350
|
+
: groonga-product
|
351
|
+
|
352
|
+
pgroonga
|
353
|
+
|
354
|
+
= マイグレーション
|
355
|
+
|
356
|
+
# coderay python
|
357
|
+
|
358
|
+
migrations.RunSQL("""
|
359
|
+
ALTER ROLE zulip SET search_path
|
360
|
+
TO zulip,public,pgroonga,pg_catalog;
|
361
|
+
ALTER TABLE zerver_message
|
362
|
+
ADD COLUMN search_pgroonga text;
|
363
|
+
UPDATE zerver_message SET search_pgroonga =
|
364
|
+
subject || ' ' || rendered_content;
|
365
|
+
CREATE INDEX pgrn_index ON zerver_message
|
366
|
+
USING pgroonga(search_pgroonga);
|
367
|
+
""", "...")
|
368
|
+
|
369
|
+
== スライドプロパティ
|
370
|
+
|
371
|
+
: groonga-product
|
372
|
+
|
373
|
+
pgroonga
|
374
|
+
|
375
|
+
= 遅延インデックス更新
|
376
|
+
|
377
|
+
* メッセージ追加・更新時にログ
|
378
|
+
* 別プロセスでログを監視
|
379
|
+
|
380
|
+
== スライドプロパティ
|
381
|
+
|
382
|
+
: groonga-product
|
383
|
+
|
384
|
+
pgroonga
|
385
|
+
|
386
|
+
= 追加・更新時にログ
|
387
|
+
|
388
|
+
# coderay sql
|
389
|
+
|
390
|
+
CREATE FUNCTION append_to_fts_update_log()
|
391
|
+
RETURNS trigger
|
392
|
+
LANGUAGE plpgsql AS $$
|
393
|
+
BEGIN
|
394
|
+
INSERT INTO fts_update_log (message_id)
|
395
|
+
VALUES (NEW.id);
|
396
|
+
RETURN NEW;
|
397
|
+
END
|
398
|
+
$$;
|
399
|
+
CREATE TRIGGER update_fts_index_async
|
400
|
+
BEFORE INSERT OR UPDATE OF
|
401
|
+
subject, rendered_content ON zerver_message
|
402
|
+
FOR EACH ROW
|
403
|
+
EXECUTE PROCEDURE append_to_fts_update_log();
|
404
|
+
|
405
|
+
== スライドプロパティ
|
406
|
+
|
407
|
+
: groonga-product
|
408
|
+
|
409
|
+
pgroonga
|
410
|
+
|
411
|
+
= 別プロセスに通知
|
412
|
+
|
413
|
+
# coderay sql
|
414
|
+
|
415
|
+
CREATE FUNCTION do_notify_fts_update_log()
|
416
|
+
RETURNS trigger
|
417
|
+
LANGUAGE plpgsql AS $$
|
418
|
+
BEGIN
|
419
|
+
NOTIFY fts_update_log;
|
420
|
+
RETURN NEW;
|
421
|
+
END
|
422
|
+
$$;
|
423
|
+
CREATE TRIGGER fts_update_log_notify
|
424
|
+
AFTER INSERT ON fts_update_log
|
425
|
+
FOR EACH STATEMENT
|
426
|
+
EXECUTE PROCEDURE do_notify_fts_update_log();
|
427
|
+
|
428
|
+
== スライドプロパティ
|
429
|
+
|
430
|
+
: groonga-product
|
431
|
+
|
432
|
+
pgroonga
|
433
|
+
|
434
|
+
= インデックス更新プロセス
|
435
|
+
|
436
|
+
# coderay python
|
437
|
+
|
438
|
+
import psycopg2
|
439
|
+
conn = psycopg2.connect("user=zulip")
|
440
|
+
cursor = conn.cursor
|
441
|
+
cursor.execute("LISTEN fts_update_log;")
|
442
|
+
while True:
|
443
|
+
if select.select([conn], [], [], 30) != ([], [], []):
|
444
|
+
conn.poll()
|
445
|
+
while conn.notifies:
|
446
|
+
conn.notifies.pop()
|
447
|
+
update_fts_columns(cursor)
|
448
|
+
|
449
|
+
== スライドプロパティ
|
450
|
+
|
451
|
+
: groonga-product
|
452
|
+
|
453
|
+
pgroonga
|
454
|
+
|
455
|
+
= インデックス更新
|
456
|
+
|
457
|
+
# coderay python
|
458
|
+
|
459
|
+
def update_fts_columns(cursor):
|
460
|
+
cursor.execute("SELECT id, message_id "
|
461
|
+
"FROM fts_update_log;")
|
462
|
+
ids = []
|
463
|
+
for (id, message_id) in cursor.fetchall():
|
464
|
+
cursor.execute("UPDATE zerver_message SET "
|
465
|
+
"search_pgroonga = "
|
466
|
+
"subject || ' ' || rendered_content "
|
467
|
+
"WHERE id = %s", (message_id,))
|
468
|
+
ids.append(id)
|
469
|
+
cursor.execute("DELETE FROM fts_update_log "
|
470
|
+
"WHERE id = ANY(%s)", (ids,))
|
471
|
+
|
472
|
+
== スライドプロパティ
|
473
|
+
|
474
|
+
: groonga-product
|
475
|
+
|
476
|
+
pgroonga
|
477
|
+
|
478
|
+
= 全文検索
|
479
|
+
|
480
|
+
# coderay python
|
481
|
+
|
482
|
+
from sqlalchemy.sql import column
|
483
|
+
def _by_search_pgroonga(self, query, operand):
|
484
|
+
# WHERE search_pgroonga @@ 'クエリー'
|
485
|
+
target = column("search_pgroonga")
|
486
|
+
condition = target.op("@@")(operand)
|
487
|
+
return query.where(condition)
|
488
|
+
|
489
|
+
== スライドプロパティ
|
490
|
+
|
491
|
+
: groonga-product
|
492
|
+
|
493
|
+
pgroonga
|
494
|
+
|
495
|
+
= ハイライト
|
496
|
+
|
497
|
+
# image
|
498
|
+
# src = images/zulip-highlight.png
|
499
|
+
# relative_height = 95
|
500
|
+
|
501
|
+
== スライドプロパティ
|
502
|
+
|
503
|
+
: groonga-product
|
504
|
+
|
505
|
+
pgroonga
|
506
|
+
|
507
|
+
= ハイライト:SQL
|
508
|
+
|
509
|
+
# coderay sql
|
510
|
+
|
511
|
+
SELECT
|
512
|
+
pgroonga.match_positions_byte(
|
513
|
+
rendered_content,
|
514
|
+
pgroonga.query_extract_keywords('クエリー'))
|
515
|
+
AS content_matches,
|
516
|
+
pgroonga.match_positions_byte(
|
517
|
+
subject,
|
518
|
+
pgroonga.query_extract_keywords('クエリー'))
|
519
|
+
AS subject_matches
|
520
|
+
|
521
|
+
== スライドプロパティ
|
522
|
+
|
523
|
+
: groonga-product
|
524
|
+
|
525
|
+
pgroonga
|
526
|
+
|
527
|
+
= ハイライト:SQLAlchemy
|
528
|
+
|
529
|
+
# coderay python
|
530
|
+
|
531
|
+
from sqlalchemy import func
|
532
|
+
def _by_search_pgroonga(self, query, operand):
|
533
|
+
match_positions_byte = func.pgroonga.match_positions_byte
|
534
|
+
query_extract_keywords = func.pgroonga.query_extract_keywords
|
535
|
+
keywords = query_extract_keywords(operand)
|
536
|
+
query = query.column(
|
537
|
+
match_positions_byte(column("rendered_content"),
|
538
|
+
keywords).label("content_matches"))
|
539
|
+
query = query.column(
|
540
|
+
match_positions_byte(column("subject"),
|
541
|
+
keywords).label("subject_matches"))
|
542
|
+
# ...
|
543
|
+
|
544
|
+
== スライドプロパティ
|
545
|
+
|
546
|
+
: groonga-product
|
547
|
+
|
548
|
+
pgroonga
|
549
|
+
|
550
|
+
= ハイライト:Python
|
551
|
+
|
552
|
+
# coderay python
|
553
|
+
|
554
|
+
def highlight_string_bytes_offsets(text, locs):
|
555
|
+
# type: (AnyStr, Iterable[Tuple[int, int]]) -> text_type
|
556
|
+
string = text.encode('utf-8')
|
557
|
+
highlight_start = b'<span class="highlight">'
|
558
|
+
highlight_stop = b'</span>'
|
559
|
+
pos = 0
|
560
|
+
result = b''
|
561
|
+
for loc in locs:
|
562
|
+
(offset, length) = loc
|
563
|
+
result += string[pos:offset]
|
564
|
+
result += highlight_start
|
565
|
+
result += string[offset:offset + length]
|
566
|
+
result += highlight_stop
|
567
|
+
pos = offset + length
|
568
|
+
result += string[pos:]
|
569
|
+
return result.decode('utf-8')
|
570
|
+
|
571
|
+
== スライドプロパティ
|
572
|
+
|
573
|
+
: groonga-product
|
574
|
+
|
575
|
+
pgroonga
|
576
|
+
|
577
|
+
= ハイライト:補足
|
578
|
+
|
579
|
+
* 通常はハイライト関数で十分
|
580
|
+
* (({pgroonga.highlight_html}))
|
581
|
+
* ただし(({ts_headline}))では不十分
|
582
|
+
* HTML出力に使えない
|
583
|
+
|
584
|
+
== スライドプロパティ
|
585
|
+
|
586
|
+
: groonga-product
|
587
|
+
|
588
|
+
pgroonga
|
589
|
+
|
590
|
+
= ハイライト:ts_headline
|
591
|
+
|
592
|
+
# coderay sql
|
593
|
+
|
594
|
+
SELECT
|
595
|
+
ts_headline('english',
|
596
|
+
'PostgreSQL <is> great!',
|
597
|
+
to_tsquery('PostgreSQL'),
|
598
|
+
'HighlightAll=TRUE');
|
599
|
+
-- ts_headline
|
600
|
+
-- -------------------------------
|
601
|
+
-- <b>PostgreSQL</b> <is> great!
|
602
|
+
-- (1 row) 不正なHTML↑
|
603
|
+
|
604
|
+
== スライドプロパティ
|
605
|
+
|
606
|
+
: groonga-product
|
607
|
+
|
608
|
+
pgroonga
|
609
|
+
|
610
|
+
= ハイライト:pgroonga.highlight_html
|
611
|
+
|
612
|
+
# coderay sql
|
613
|
+
|
614
|
+
SELECT
|
615
|
+
pgroonga.highlight_html(
|
616
|
+
'PostgreSQL <is> great!',
|
617
|
+
pgroonga.query_extract_keywords('PostgreSQL'));
|
618
|
+
-- highlight_html
|
619
|
+
-- ----------------------------------------
|
620
|
+
-- <span class="keyword">PostgreSQL</span>
|
621
|
+
-- <is> great!
|
622
|
+
-- ↑ ↑HTMLエスケープされている
|
623
|
+
|
624
|
+
== スライドプロパティ
|
625
|
+
|
626
|
+
: groonga-product
|
627
|
+
|
628
|
+
pgroonga
|
629
|
+
|
630
|
+
= Zulip:まとめ
|
631
|
+
|
632
|
+
* (('wait'))全言語対応全文検索
|
633
|
+
* textsearch(1言語のみ)→\n
|
634
|
+
PGroonga(全言語)
|
635
|
+
* (('wait'))書き込み性能は維持
|
636
|
+
* 遅延インデックス更新
|
637
|
+
|
638
|
+
== スライドプロパティ
|
639
|
+
|
640
|
+
: groonga-product
|
641
|
+
|
642
|
+
pgroonga
|
643
|
+
|
644
|
+
= まとめ
|
645
|
+
|
646
|
+
* (('wait'))Mroonga・PGroongaの\n
|
647
|
+
導入方法を実例ベースで紹介
|
648
|
+
* Redmine:チケット管理システム
|
649
|
+
* Zulip:チャットツール
|
650
|
+
* (('wait'))トランザクション必須の場合
|
651
|
+
* Mroonga:別テーブル作成
|
652
|
+
* PGroonga:インデックス追加
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rabbit-slide-kou-mysql-and-postgresql-and-japanese-full-text-search-3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2016.9.29.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- 須藤功平
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-09-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rabbit
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.0.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.0.2
|
27
|
+
description: Mroonga・PGroongaをさわり始めた人向けにMroonga・PGroongaを利用する方法を具体的な事例を使いながら紹介します。
|
28
|
+
email:
|
29
|
+
- kou@clear-code.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".rabbit"
|
35
|
+
- README.rd
|
36
|
+
- Rakefile
|
37
|
+
- config.yaml
|
38
|
+
- images/redmine-full-text-search-form.png
|
39
|
+
- images/redmine-full-text-search-plugin-report.png
|
40
|
+
- images/zulip-full-text-search-form.png
|
41
|
+
- images/zulip-highlight.png
|
42
|
+
- mroonga-and-pgroonga.rab
|
43
|
+
- pdf/mysql-and-postgresql-and-japanese-full-text-search-3-mroonga-and-pgroonga.pdf
|
44
|
+
homepage: http://slide.rabbit-shocker.org/authors/kou/mysql-and-postgresql-and-japanese-full-text-search-3/
|
45
|
+
licenses:
|
46
|
+
- CC BY 3.0
|
47
|
+
- CC BY-SA 4.0
|
48
|
+
metadata: {}
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 2.5.1
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: Mroonga・PGroonga利用方法
|
69
|
+
test_files: []
|