rabbit-slide-kou-jpmug-db-study-1 2018.1.30.0

Sign up to get free protection for your applications and to get access to all the features.
Binary file
@@ -0,0 +1,952 @@
1
+ = MariaDBとMroongaで作る\n全言語対応\n超高速全文検索システム
2
+
3
+ : author
4
+ 須藤功平
5
+ : institution
6
+ クリアコード
7
+ : content-source
8
+ 第一回 JPMUG DB勉強会
9
+ : date
10
+ 2018-01-30
11
+ : start-time
12
+ 2018-01-30T19:25:00+09:00
13
+ : end-time
14
+ 2018-01-30T20:10:00+09:00
15
+ : theme
16
+ .
17
+
18
+ = 全文検索システム\n対象
19
+
20
+ (('tag:center'))
21
+ (('tag:large'))
22
+ (('tag:margin-bottom * 2'))
23
+ 大量のテキスト
24
+
25
+ * 例:Wikiのデータ
26
+ * 例:オフィス文書のテキスト
27
+ * 例:商品説明・口コミ
28
+
29
+ = 全文検索システム\n目的
30
+
31
+ * 必要な情報を
32
+ * 必要なときに
33
+ * 活用
34
+
35
+ = 必要な情報を活用
36
+
37
+ * ×
38
+ * 探している情報が見つからない
39
+ * ○
40
+ * 探している情報が見つかる
41
+ * ◎
42
+ * 意識していなかったけど\n
43
+ ((*実は欲しかった*))情報も見つかる!
44
+
45
+ = 必要なときに活用
46
+
47
+ * ×
48
+ * なかなか見つからない
49
+ * ○
50
+ * すぐに見つかる
51
+ * ◎
52
+ * すでに見つかっていた
53
+ * 例:レコメンデーション
54
+
55
+ = 実装方法\n選択肢
56
+
57
+ * 全文検索サーバーを使う
58
+ * MariaDBでLIKEを使う
59
+
60
+ = 全文検索サーバー案\nメリット
61
+
62
+ * 必要な機能が揃っている
63
+ * +αの機能もある
64
+ * 速い
65
+
66
+ = 全文検索サーバー案\nデメリット
67
+
68
+ * 実装コスト大
69
+ * それぞれ独自の使い方だから
70
+ * マスターデータの同期はどうする?
71
+ * メンテナンスコスト大
72
+ * それぞれ独自の仕組みだから
73
+
74
+ = MariaDBでLIKE案\nメリット
75
+
76
+ * 実装コスト小
77
+ * 新しく覚えることが少ない
78
+ * データの一元管理
79
+ * メンテナンスコスト小
80
+ * 既存の運用ノウハウを使える
81
+ * データ少なら実用的な速度
82
+
83
+ = MariaDBでLIKE案\nデメリット
84
+
85
+ * 機能不足
86
+ * それっぽい順のソート不可
87
+ * 全文検索ではソート順が重要
88
+ * ユーザーは先頭n件しか見ない
89
+ * SQLの表現力不足
90
+ * nクエリーで実現すると性能に影響
91
+
92
+ = 実現方法\n第3の選択肢
93
+
94
+ * MariaDB経由(SQL)で\n
95
+ 全文検索エンジンを使う
96
+
97
+ = メリット
98
+
99
+ * 高速で豊富な機能
100
+ * それっぽい順のソート可
101
+ * 実装コスト小
102
+ * メンテナンスコスト小
103
+
104
+ = デメリット
105
+
106
+ * MariaDBに拡張機能が必要
107
+ * RDS・Azure databaseで使えない\n
108
+ (('note:(Azure database for MariaDBはまだリリース前)'))
109
+
110
+ = オススメの選択肢\n全文検索の知識ナシ
111
+
112
+ * まだ単純な機能で十分
113
+ * データ少:MariaDB単独でLIKE\n
114
+ (('note:(数十万件とか)'))
115
+ * データ中以上:\n
116
+ MariaDB経由で全文検索エンジン
117
+ * いまどきの全文検索機能が必要
118
+ * MariaDB経由で全文検索エンジン
119
+
120
+ = オススメの選択肢\n全文検索の知識アリ
121
+
122
+ * カリカリにチューニングしたい
123
+ * MariaDBと全文検索サーバーを併用
124
+ * それ以外
125
+ * MariaDB経由で全文検索エンジン
126
+
127
+ = 説明する選択肢
128
+
129
+ MariaDB経由で\n
130
+ 全文検索\n
131
+ エンジン
132
+
133
+ = 全文検索エンジン\nGroonga(ぐるんが)
134
+
135
+ * 組込可能な全文検索エンジン
136
+ * MariaDB・MySQLに組込→Mroonga
137
+ * PostgreSQLに組込→PGroonga
138
+ * 全文検索サーバーとして\n
139
+ 単独でも使用可能
140
+ * MariaDBと全文検索サーバーを併用\n
141
+ もできる
142
+
143
+ = Groongaの得意なこと
144
+
145
+ * データの追加・更新
146
+ * 新鮮な情報をすぐに検索可能に!
147
+ * 更新中も検索性能を落とさない!
148
+ * 日本語
149
+ * 開発者が日本人
150
+ * 便利機能が組み込み
151
+
152
+ = GroongaとUnicode
153
+
154
+ * NFKCベースの正規化機能を組込
155
+ * Unicode 5.1ベースで古い
156
+ * 2008年の仕様
157
+ * Unicode 10.0(最新)対応中\n
158
+ (('note:正規化方式を変えるとインデックスの互換性がなくなる(=要インデックス再構築)のでデフォルトは変えない'))
159
+
160
+ = Mroonga(むるんが)
161
+
162
+ * MariaDBのストレージエンジン
163
+ * InnoDB・MyISAMなどと同じレイヤー
164
+ * MariaDB 10.0.15から標準バンドル
165
+ * 使用方法
166
+ * (({CREATE TABLE (...) ENGINE=Mroonga}))
167
+
168
+ = 照合順序:COLLATION
169
+
170
+ * 文字の並び順の規則
171
+ * 文字が同一かどうかの判定にも利用
172
+ * 適切な日本語規則なし
173
+ * いわゆる🍣=🍺問題
174
+
175
+ (('note:MySQL 8では適切な日本語規則が追加される'))\n
176
+ (('note:utf8mb4_ja_0900_as_csなど'))
177
+
178
+ = Mroongaの照合順序
179
+
180
+ * MariaDB互換のもの
181
+ * (({utf8mb4_ja_0900_*}))互換は対応予定
182
+ * MariaDB互換を微調整したもの
183
+ * 日本語でもいい感じ
184
+ * Groonga提供のもの
185
+ * NFKCベースのもの
186
+ * 日本語でもいい感じ
187
+
188
+ = Mroongaで照合順序
189
+
190
+ * MariaDB互換がよい!
191
+ * 互換正規化処理を使用:デフォルト
192
+ * MariaDB互換は気にしないから\n
193
+ いい感じに!
194
+ * Groonga提供のものを使用
195
+
196
+ = 全文検索性能\n計測データ
197
+
198
+ * 対象:Wikipedia日本語版
199
+ * レコード数:約185万件
200
+ * データサイズ:約7GB
201
+ * メモリー4GB・SSD250GB(('note:(ConoHa)'))
202
+
203
+ = 検索性能1
204
+
205
+ (('tag:center'))
206
+ キーワード:テレビアニメ\n
207
+ (('note:(ヒット数:約2万3千件)'))
208
+
209
+ # RT
210
+ delimiter = [|]
211
+
212
+ InnoDB ngram | 3m2s
213
+ InnoDB MeCab | 6m20s
214
+ Mroonga:((*1*)) | 0.11s
215
+
216
+ = 検索性能2
217
+
218
+ (('tag:center'))
219
+ キーワード:データベース\n
220
+ (('note:(ヒット数:約1万7千件)'))
221
+
222
+ # RT
223
+ delimiter = [|]
224
+
225
+ InnoDB ngram | 36s
226
+ InnoDB MeCab:((*1*)) | 0.03s
227
+ Mroonga:((*2*)) | 0.09s
228
+
229
+ = 検索性能3
230
+
231
+ (('tag:center'))
232
+ キーワード:PostgreSQL OR MySQL\n
233
+ (('note:(ヒット数:約400件)'))
234
+
235
+ # RT
236
+ delimiter = [|]
237
+
238
+ InnoDB ngram | N/A(Error)
239
+ InnoDB MeCab:((*1*)) | 0.005s
240
+ Mroonga:((*2*)) | 0.028s
241
+
242
+ = 検索性能4
243
+
244
+ (('tag:center'))
245
+ キーワード:日本\n
246
+ (('note:(ヒット数:約63万件)'))
247
+
248
+ # RT
249
+ delimiter = [|]
250
+
251
+ InnoDB ngram | 1.3s
252
+ InnoDB MeCab | 1.3s
253
+ Mroonga:((*1*)) | 0.21s
254
+
255
+ = 全文検索性能まとめ
256
+
257
+ * Mroonga:安定して速い
258
+ * ((*SQLで使えて機能豊富で速い!*))
259
+ * InnoDB FTS MeCab
260
+ * ハマれば速い
261
+ * InnoDB FTS ngram
262
+ * 安定して遅い
263
+
264
+ = 普通の検索も速い
265
+
266
+ * カラムストアを活かした最適化
267
+ * ポイント1:余計なI/Oを減らす
268
+ * ポイント2:I/Oを局所化
269
+
270
+ = カラムストア
271
+
272
+ # image
273
+ # src = images/column-store.svg
274
+ # relative_width = 100
275
+
276
+ = 必要なカラムのみアクセス
277
+
278
+ # coderay sql
279
+ -- aのみにアクセス
280
+ SELECT a
281
+ FROM table
282
+ -- cのみにアクセス
283
+ WHERE c = XXX;
284
+ -- bにはアクセスしない
285
+
286
+ = 減ったI/O
287
+
288
+ # image
289
+ # src = images/not-access-to-needless-columns.svg
290
+ # relative_width = 100
291
+
292
+ = 行カウント
293
+
294
+ # coderay sql
295
+ -- カラムの値は必要ない
296
+ SELECT COUNT(*)
297
+ FROM table
298
+ -- cの全文検索インデックスにだけアクセス
299
+ WHERE MATCH(c)
300
+ AGAINST('+keyword' IN BOOLEAN MODE);
301
+ -- a, b, cはアクセスしない
302
+
303
+ = 減ったI/O
304
+
305
+ # image
306
+ # src = images/count-star.svg
307
+ # relative_width = 100
308
+
309
+ = (({ORDER BY LIMIT}))
310
+
311
+ # coderay sql
312
+ SELECT a
313
+ FROM table
314
+ WHERE MATCH(c)
315
+ AGAINST('+keyword' IN BOOLEAN MODE)
316
+ -- MariaDBではなくMroongaがORDER BY LIMITを処理
317
+ -- →Mroongaは10レコードだけMariaDBに返す
318
+ -- マッチしたレコードすべては返さない
319
+ ORDER BY a LIMIT 10;
320
+
321
+ = (({ORDER BY LIMIT}))の最適化
322
+
323
+ * Mroongaが検索
324
+ * カラム毎の処理でI/Oを局所化\n
325
+ (('note:(索引非使用時)'))
326
+ * Mroongaがソート
327
+ * カラム毎の処理でI/Oを局所化
328
+ * Mroongaが(({OFFSET}))/(({LIMIT}))を処理
329
+
330
+ = カラム毎の処理は速い
331
+
332
+ # image
333
+ # src = images/per-column-processing.svg
334
+ # relative_width = 100
335
+
336
+ = condition push downの最適化
337
+
338
+ * 従来はMariaDBが処理していた検索条件をストレージエンジンが処理する仕組み
339
+ * ストレージエンジンでの処理の方が高速なら全体として高速になる
340
+ * Mroonga 7.10から実験的に対応
341
+ * デフォルトオフ
342
+
343
+ = condition push downの効果
344
+
345
+ * 全文検索インデックスのみ
346
+ * 等価条件:シーケンシャルスキャン
347
+ * 全文検索:インデックススキャン
348
+ * データ
349
+ * シカゴの犯罪データ(('note:(651万レコード)'))
350
+
351
+ (('tag:xx-small'))
352
+ 詳細:((<"https://github.com/kou/rabbit-slide-kou-jpmug-db-study-1/blob/master/memo.md"|URL:https://github.com/kou/rabbit-slide-kou-jpmug-db-study-1/blob/master/memo.md>))
353
+
354
+ = 等価条件:数値1つ
355
+
356
+ (('tag:center'))
357
+ 26万件ヒットするケース
358
+
359
+ # RT
360
+ delimiter = [|]
361
+
362
+ InnoDB | 1.3s
363
+ Mroonga\n(デフォルト) | 1.3s
364
+ Mroonga\n(最適化ON) | 0.4s
365
+
366
+ = 等価条件:数値1つ+真偽値2つ
367
+
368
+ (('tag:center'))
369
+ 7000件ヒットするケース
370
+
371
+ # RT
372
+ delimiter = [|]
373
+
374
+ InnoDB | 1.6s
375
+ Mroonga\n(デフォルト) | 2.3s
376
+ Mroonga\n(最適化ON) | 0.4s
377
+
378
+ = 全文検索+等価条件
379
+
380
+ (('tag:center'))
381
+ 4000件ヒットするケース
382
+
383
+ # RT
384
+ delimiter = [|]
385
+
386
+ InnoDB | 18s
387
+ Mroonga\n(デフォルト) | 0.4s
388
+ Mroonga\n(最適化ON) | 0.4s
389
+
390
+ (('note:このパターンはデフォルトで最適化が効く'))
391
+
392
+ = Mroongaの検索性能まとめ
393
+
394
+ * 最適化が効くと桁違いに速い
395
+ * 全文検索のときはデフォルトで効く
396
+ * 7.10からさらなる最適化が!\n
397
+ (('note:まだ実験的扱いなのでデフォルトオフ'))
398
+ * OLAP用途にも使える
399
+ * MariaDB ColumnStoreを補完する\n
400
+ 立ち位置も可
401
+
402
+ (('tag:xx-small'))
403
+ 参考:((<"http://mroonga.org/ja/docs/reference/server_variables.html#mroonga-condition-push-down-type"|URL:http://mroonga.org/ja/docs/reference/server_variables.html#mroonga-condition-push-down-type>))
404
+
405
+ = 全文検索システムの実装
406
+
407
+ * 全文検索
408
+ * キーワードハイライト
409
+ * 周辺テキスト表示
410
+ * オートコンプリート
411
+ * 同義語展開
412
+ * 関連文書の表示
413
+
414
+ = 全文検索
415
+
416
+ # image
417
+ # src = images/php-document-search-search.png
418
+ # relative_height = 100
419
+
420
+ = テーブル定義
421
+
422
+ # coderay sql
423
+
424
+ CREATE TABLE entries (
425
+ title text,
426
+ content text,
427
+ -- 全文検索用インデックス
428
+ -- よくわからないならデフォルトのまま使うこと!
429
+ FULLTEXT INDEX (title, content)
430
+ ) ENGINE=Mroonga
431
+ DEFAULT CHARSET=utf8mb4;
432
+
433
+ = データ挿入
434
+
435
+ # coderay sql
436
+
437
+ -- 普通に挿入するだけでよい
438
+ INSERT INTO entries
439
+ VALUES ('タイトル',
440
+ '高速に全文検索したいですね!');
441
+
442
+ = 全文検索
443
+
444
+ # coderay sql
445
+
446
+ SELECT title FROM entries
447
+ WHERE -- MATCH AGAINSTで全文検索
448
+ MATCH (title, content)
449
+ -- デフォルトORがMariaDBの仕様
450
+ -- 「検索」または「高速」を含むとマッチ
451
+ AGAINST ('検索 高速'
452
+ IN BOOLEAN MODE);
453
+
454
+ = AND全文検索
455
+
456
+ # coderay sql
457
+
458
+ MATCH (title, content)
459
+ -- 各キーワードの前に「+」をつけるとAND
460
+ -- 「検索」かつ「高速」を含むとマッチ
461
+ AGAINST ('+検索 +高速'
462
+ IN BOOLEAN MODE);
463
+
464
+ = 使いやすいAND全文検索
465
+
466
+ # coderay sql
467
+
468
+ MATCH (title, content)
469
+ -- 最初に「*D+」をつけるとデフォルトAND
470
+ -- Mroonga独自機能
471
+ -- 「検索」かつ「高速」を含むとマッチ
472
+ AGAINST ('*D+ 検索 高速'
473
+ IN BOOLEAN MODE);
474
+
475
+ = それっぽい順のソート
476
+
477
+ # coderay sql
478
+
479
+ SELECT
480
+ title,
481
+ -- ここのMATCH AGAINSTはスコアーを返す
482
+ MATCH (title, content)
483
+ AGAINST ('*D+ 検索 高速'
484
+ IN BOOLEAN MODE) AS score
485
+ FROM entries
486
+ WHERE -- ...
487
+ -- それっぽさでソート
488
+ ORDER BY score DESC LIMIT 10;
489
+
490
+ = ハイライト
491
+
492
+ # image
493
+ # src = images/php-document-search-search.png
494
+ # relative_height = 100
495
+
496
+ = ハイライト
497
+
498
+ # coderay sql
499
+
500
+ SELECT mroonga_highlight_html(
501
+ title, '*D+ 検索 高速' AS query)
502
+ -- クエリーからハイライト対象のキーワードを抽出
503
+ FROM entries
504
+ WHERE
505
+ MATCH (title, content)
506
+ AGAINST ('*D+ 検索 高速' IN BOOLEAN MODE);
507
+
508
+ = ハイライト結果例
509
+
510
+ # coderay html
511
+
512
+ <Groonga>で高速全文検索!
513
+
514
+ &lt;Groonga&gt;で ← タグをエスケープ
515
+ <span class="keyword">高速</span>
516
+ 全文 ↑↓キーワードはclass付け
517
+ <span class="keyword">検索</span>!
518
+
519
+ = 周辺テキスト
520
+
521
+ # image
522
+ # src = images/php-document-search-search.png
523
+ # relative_height = 100
524
+
525
+ = 周辺テキスト
526
+
527
+ # coderay sql
528
+
529
+ SELECT mroonga_snippet_html(
530
+ content, '*D+ 検索 高速' AS query)
531
+ -- クエリーから対象のキーワードを抽出
532
+ FROM entries
533
+ WHERE
534
+ MATCH (title, content)
535
+ AGAINST ('*D+ 検索 高速' IN BOOLEAN MODE);
536
+
537
+ = 周辺テキスト結果例
538
+
539
+ # coderay html
540
+
541
+ ...<Groonga>で高速全文検索!...
542
+
543
+ <div class="snippet"> ←1つ目
544
+ ga&gt;で ←タグをエスケープ
545
+ <span class="keyword">高速</span>
546
+ 全文 ↑↓キーワードはclass付け
547
+ <span class="keyword">検索/span>!
548
+ </div>
549
+ <div class="snippet">...</div> ←2つ目
550
+
551
+ = オートコンプリート
552
+
553
+ # image
554
+ # src = images/php-document-search.png
555
+ # relative_height = 100
556
+
557
+ = オートコンプリート:必要なもの
558
+
559
+ * マスターテーブル
560
+ * 候補(例:牛乳)
561
+ * 候補のヨミ(カタカナ・複数可)
562
+ * 例1:ギュウニュウ
563
+ * 例2:ミルク
564
+
565
+ = オートコンプリート:実装方法
566
+
567
+ * 以下の検索のOR
568
+ * ヨミでの前方一致検索
569
+ * 候補を緩い全文検索
570
+ * 候補でソートして提示
571
+
572
+ = オートコンプリート:テーブル定義
573
+
574
+ # coderay sql
575
+
576
+ CREATE TABLE terms (
577
+ term varchar(256), -- 補完候補
578
+ reading varchar(256), -- ヨミガナ
579
+ PRIMARY KEY (term, reading),
580
+ FULLTEXT INDEX (term) -- 候補全文検索用
581
+ -- 緩い全文検索用トークナイザー
582
+ COMMENT 'tokenizer "TokenBigramSplitSymbolAlpha"',
583
+ FULLTEXT INDEX (reading) -- ヨミガナ前方一致用
584
+ COMMENT 'normalizer "NormalizerAuto",
585
+ tokenizer "off"' -- トークナイザー不要
586
+ ) ENGINE=Mroonga DEFAULT CHARSET=utf8mb4;
587
+
588
+ = オートコンプリート:データ例
589
+
590
+ # coderay sql
591
+
592
+ INSERT INTO terms VALUES (
593
+ '牛乳', -- 補完候補
594
+ 'ギュウニュウ' --ヨミガナはカタカナで指定
595
+ );
596
+ INSERT INTO terms VALUES (
597
+ '牛乳',
598
+ 'ミルク' -- 「ミルク」でも補完できるように
599
+ );
600
+
601
+ = オートコンプリート\nデータ管理のポイント
602
+
603
+ * 普通のテーブルなので管理が楽
604
+ * 追加・削除・更新が楽
605
+ * ダンプ・リストアもいつも通り
606
+ * レプリケーションもいつも通り
607
+
608
+ = オートコンプリート\n検索方法
609
+
610
+ # coderay sql
611
+
612
+ SELECT DISTINCT(term) FROM terms
613
+ WHERE MATCH (reading) -- ヨミガナ前方一致検索
614
+ AGAINST (CONCAT('*SS prefix_rk_search(reading, ',
615
+ mroonga_escape(${入力} AS script),
616
+ ')') IN BOOLEAN MODE) OR
617
+ MATCH (term) -- 候補を緩く全文検索
618
+ AGAINST (CONCAT('*D+ ', mroonga_escape(${入力})))
619
+ IN BOOLEAN MODE)
620
+ ORDER BY term LIMIT 10; -- ソート
621
+
622
+ = オートコンプリート\n検索例:漢字1
623
+
624
+ # coderay sql
625
+
626
+ -- ユーザーが「牛」を入力した場合
627
+ SELECT DISTINCT(term) FROM terms
628
+ WHERE MATCH (reading) -- ヨミガナ前方一致検索
629
+ AGAINST (CONCAT('*SS prefix_rk_search(reading, ',
630
+ mroonga_escape('牛' AS script),
631
+ ')') IN BOOLEAN MODE) OR
632
+ MATCH (term) -- 候補を緩く全文検索(ヒット)
633
+ AGAINST (CONCAT('*D+ ', mroonga_escape('牛')))
634
+ IN BOOLEAN MODE)
635
+ ORDER BY term LIMIT 10; -- ソート
636
+
637
+ = オートコンプリート\n検索例:漢字2
638
+
639
+ # coderay sql
640
+
641
+ -- ユーザーが「乳」を入力した場合
642
+ SELECT DISTINCT(term) FROM terms
643
+ WHERE MATCH (reading) -- ヨミガナ前方一致検索
644
+ AGAINST (CONCAT('*SS prefix_rk_search(reading, ',
645
+ mroonga_escape('乳' AS script),
646
+ ')') IN BOOLEAN MODE) OR
647
+ MATCH (term) -- 候補を緩く全文検索(ヒット)
648
+ AGAINST (CONCAT('*D+ ', mroonga_escape('乳')))
649
+ IN BOOLEAN MODE)
650
+ ORDER BY term LIMIT 10; -- ソート
651
+
652
+ = オートコンプリート\n検索例:カタカナ
653
+
654
+ # coderay sql
655
+
656
+ -- ユーザーが「ギュウ」を入力した場合
657
+ SELECT DISTINCT(term) FROM terms
658
+ WHERE MATCH (reading) -- ヨミガナ前方一致検索(ヒット)
659
+ AGAINST (CONCAT('*SS prefix_rk_search(reading, ',
660
+ mroonga_escape('ギュウ' AS script),
661
+ ')') IN BOOLEAN MODE) OR
662
+ MATCH (term) -- 候補を緩く全文検索
663
+ AGAINST (CONCAT('*D+ ', mroonga_escape('ギュウ')))
664
+ IN BOOLEAN MODE)
665
+ ORDER BY term LIMIT 10; -- ソート
666
+
667
+ = オートコンプリート\n検索例:ひらがな
668
+
669
+ # coderay sql
670
+
671
+ -- ユーザーが「ぎゅう」を入力した場合
672
+ SELECT DISTINCT(term) FROM terms
673
+ WHERE MATCH (reading) -- ヨミガナ前方一致検索(ヒット)
674
+ AGAINST (CONCAT('*SS prefix_rk_search(reading, ',
675
+ mroonga_escape('ぎゅう' AS script),
676
+ ')') IN BOOLEAN MODE) OR
677
+ MATCH (term) -- 候補を緩く全文検索
678
+ AGAINST (CONCAT('*D+ ', mroonga_escape('ぎゅう')))
679
+ IN BOOLEAN MODE)
680
+ ORDER BY term LIMIT 10; -- ソート
681
+
682
+ = オートコンプリート\n検索例:ローマ字
683
+
684
+ # coderay sql
685
+
686
+ -- ユーザーが「gyu」を入力した場合
687
+ SELECT DISTINCT(term) FROM terms
688
+ WHERE MATCH (reading) -- ヨミガナ前方一致検索(ヒット)
689
+ AGAINST (CONCAT('*SS prefix_rk_search(reading, ',
690
+ mroonga_escape('gyu' AS script),
691
+ ')') IN BOOLEAN MODE) OR
692
+ MATCH (term) -- 候補を緩く全文検索
693
+ AGAINST (CONCAT('*D+ ', mroonga_escape('gyu')))
694
+ IN BOOLEAN MODE)
695
+ ORDER BY term LIMIT 10; -- ソート
696
+
697
+ = 同義語展開
698
+
699
+ * 同義語
700
+ * 同じ意味だが表記が異なる語
701
+ * 例:「刺身」と「お造り」
702
+ * どの表記でもヒットして欲しい
703
+ * 同義語展開→同義語すべてでOR検索
704
+
705
+ = 同義語展開\n実装方法
706
+
707
+ * 同義語管理テーブルを作成
708
+ * クエリー内の同義語を展開
709
+ * 展開後のクエリーで検索
710
+
711
+ = 同義語展開:Mroonga\nテーブル定義
712
+
713
+ # coderay sql
714
+ CREATE TABLE synonyms (
715
+ term varchar(255), -- 展開対象の語
716
+ synonym varchar(255), -- 同義語
717
+ INDEX (term) -- 高速化と精度向上
718
+ COMMENT 'normalizer "NormalizerAuto"'
719
+ ) ENGINE=Mroonga DEFAULT CHARSET=utf8mb4;
720
+
721
+ = 同義語展開\nデータ例
722
+
723
+ # coderay sql
724
+ INSERT INTO synonyms
725
+ -- 「刺身」を「刺身 OR お造り」に展開
726
+ VALUES ('刺身', '刺身'),
727
+ ('刺身', 'お造り'),
728
+ -- 「お造り」を「お造り OR 刺身」に展開
729
+ ('お造り', 'お造り'),
730
+ ('お造り', '刺身');
731
+
732
+ = 同義語展開\nデータ管理のポイント
733
+
734
+ * 普通のテーブルなので管理が楽
735
+ * 追加・削除・更新が楽
736
+ * ダンプ・リストアもいつも通り
737
+ * レプリケーションもいつも通り
738
+
739
+ = 同義語展開:Mroonga\n確認方法
740
+
741
+ # coderay sql
742
+
743
+ SELECT mroonga_query_expand(
744
+ 'synonyms', -- テーブル名
745
+ 'term', -- 展開対象のカラム名
746
+ 'synonym', -- 対応する同義語のカラム名
747
+ '居酒屋 刺身' -- クエリー
748
+ );
749
+ -- '居酒屋 ((刺身) OR (お造り))'
750
+
751
+ = 同義語展開:Mroonga\n検索方法
752
+
753
+ # coderay sql
754
+ SELECT title FROM entries
755
+ WHERE
756
+ MATCH (title)
757
+ -- '*D+ 居酒屋 OR ((刺身) OR (お造り))'になる
758
+ AGAINST (mroonga_query_expand('synonyms',
759
+ 'term',
760
+ 'synonym',
761
+ '*D+ 居酒屋 刺身')
762
+ IN BOOLEAN MODE);
763
+
764
+ = 類似文書検索
765
+
766
+ * 検索クエリーは文書そのもの
767
+ * キーワードではない
768
+ * 関連エントリーの提示に使える
769
+ * メタデータがあるなら組み合わせる\n
770
+ →精度向上
771
+ * メタデータ:タグ・行動履歴など
772
+
773
+ = 類似文書検索:Mroonga\nインデックス定義
774
+
775
+ # coderay sql
776
+
777
+ CREATE TABLE entries (
778
+ -- ...
779
+ FULLTEXT INDEX (content)
780
+ -- TokenMecabを使わないと精度がでない
781
+ -- 必要なときだけカスタマイズ!
782
+ COMMENT 'tokenizer "TokenMecab"'
783
+ ) -- ...
784
+
785
+ = 類似文書検索:Mroonga\n検索方法
786
+
787
+ # coderay sql
788
+
789
+ SELECT title
790
+ FROM entries
791
+ WHERE
792
+ MATCH (content)
793
+ -- ↓ 既存文書の内容をそのまま指定
794
+ AGAINST ('...Groongaで高速全文検索!...'
795
+ IN NATURAL LANGUAGE MODE);
796
+
797
+ = 類似文書検索:Mroonga\n結果例
798
+
799
+ クエリー:
800
+ ...Groongaで高速全文検索!...
801
+
802
+ ヒット例:
803
+ ...Mroongaで高速全文検索!...
804
+
805
+ = 全文検索システムの実装\nまとめ
806
+
807
+ * 全文検索
808
+ * キーワードハイライト
809
+ * 周辺テキスト表示
810
+ * オートコンプリート
811
+ * 同義語展開
812
+ * 関連文書の表示
813
+
814
+ = 全文検索システムの実装\n次の一歩
815
+
816
+ * 構造化データ対応
817
+ * オフィス文書・HTMLなど
818
+ * 対応に必要な処理
819
+ * テキスト抽出
820
+ * メタデータ抽出(('note:(例:タイトル・更新日時)'))
821
+ * スクリーンショット作成(('note:(なおよい)'))
822
+
823
+ = 抽出ツール
824
+
825
+ * Apache Tika
826
+ * Apache Luceneのサブプロジェクト
827
+ * 対応フォーマット数が多い
828
+ * ChupaText
829
+ * Groongaのサブプロジェクト
830
+ * スクリーンショット作成対応
831
+
832
+ = ChupaText
833
+
834
+ * 対応フォーマット
835
+ * Word/Excel/PowerPoint
836
+ * ODT/ODS/ODP(('note:(OpenDocument)'))
837
+ * PDF/HTML/XML/CSV/...
838
+ * インターフェイス
839
+ * HTTPとコマンドライン
840
+
841
+ = ChupaText:インストール
842
+
843
+ * DockerかVagrantを使うのが楽
844
+ * (('tag:xx-small'))
845
+ https://github.com/ranguba/chupa-text-docker
846
+ * (('tag:xx-small'))
847
+ https://github.com/ranguba/chupa-text-vagrant
848
+
849
+ = ChupaText:Docker
850
+
851
+ # coderay console
852
+ % GITHUB=https://github.com
853
+ % git clone \
854
+ ${GITHUB}/ranguba/chupa-text-docker.git
855
+ % cd chupa-text-docker
856
+ % docker-compose up --build
857
+
858
+ = ChupaText:使い方
859
+
860
+ # coderay console
861
+ % curl \
862
+ --form data=@XXX.pdf \
863
+ http://localhost:20080/extraction.json
864
+
865
+ = ChupaText:結果例
866
+
867
+ # coderay json
868
+
869
+ {
870
+ "mime-type": "application/pdf", # 元データのMIMEタイプ
871
+ "size": 147159, # メタデータ
872
+ ...,
873
+ "texts": [ # 抽出されたテキスト(N個)
874
+ {
875
+ "mime-type": "text/plain", # 抽出後のMIMEタイプ
876
+ ...,
877
+ "creator": "Adobe Illustrator CS3", # メタデータ
878
+ "body": "This is sample PDF. ...", # 抽出したテキスト
879
+ "screenshot": {
880
+ "mime-type": "image/png", # スクリーンショットのMIMEタイプ
881
+ "data": "iVBORw...", # Base64にした画像データ
882
+ "encoding": "base64" # Base64であることを明記
883
+ }
884
+ }
885
+ ]
886
+ }
887
+
888
+ = ChupaText:Web UI
889
+
890
+ # image
891
+ # src = images/chupa-text-web-ui-form.png
892
+ # relative_height = 100
893
+
894
+ = ChupaText:Web UI抽出例
895
+
896
+ # image
897
+ # src = images/chupa-text-web-ui-extract-metadata.png
898
+ # relative_height = 100
899
+
900
+ = ChupaText:Web UI抽出例
901
+
902
+ # image
903
+ # src = images/chupa-text-web-ui-extract-text-and-screenshot.png
904
+ # relative_height = 100
905
+
906
+ = ChupaText:Vagrant
907
+
908
+ # coderay console
909
+ % GITHUB=https://github.com
910
+ % git clone \
911
+ ${GITHUB}/ranguba/chupa-text-vagrant.git
912
+ % cd chupa-text-vagrant
913
+ % vagrant up
914
+
915
+ (('tag:center'))
916
+ 使い方はDocker版と同じ
917
+
918
+ = ChupaText:活用例
919
+
920
+ * 抽出したテキスト
921
+ * Mroongaへ挿入
922
+ * 抽出したメタデータ
923
+ * Mroongaへ挿入
924
+ * 絞り込みに活用
925
+ * 作成したスクリーンショット
926
+ * 検索結果表示時に掲載
927
+
928
+ = まとめ
929
+
930
+ * MariaDBの全文検索まわり
931
+ * 全文検索システム実装例を紹介
932
+ * 構造化データの対応方法を紹介
933
+ * ChupaText
934
+
935
+ = 扱わなかった話題
936
+
937
+ * 運用について
938
+ * 障害対策・レプリケーション
939
+ * チューニング
940
+ * Groongaの機能を直接使う方法
941
+
942
+ = サポートサービス紹介
943
+
944
+ * 導入支援(('note:(設計支援・性能検証・移行支援・…)'))
945
+ * 開発支援\n
946
+ (('note:(サンプルコード提供・問い合わせ対応・…)'))
947
+ * 運用支援(('note:(障害対応・チューニング支援・…)'))
948
+
949
+ 問い合わせ先:
950
+
951
+ (('tag:x-small'))
952
+ ((<"https://www.clear-code.com/contact/?type=groonga"|URL:https://www.clear-code.com/contact/?type=groonga>))