36
09/06/20 textsearch_jaで全文検索 日本PostgreSQLユーザ会 北海道支部 株式会社サイクル・オブ・フィフス 石田朗雄

textsearch_jaで全文検索

Embed Size (px)

DESCRIPTION

PostgreSQLの全文検索機能に関するosc2009 hokkaido での発表資料です。

Citation preview

Page 1: textsearch_jaで全文検索

09/06/20

textsearch_jaで全文検索

日本PostgreSQLユーザ会北海道支部

株式会社サイクル・オブ・フィフス石田朗雄

Page 2: textsearch_jaで全文検索

09/06/20

自己紹介

● id:iakio● http://postgresql.g.hatena.ne.jp/iakio/● 今日の資料とデモのソースコードは公開します

Page 3: textsearch_jaで全文検索

09/06/20

自己紹介

● 代表作● SQLでボウリングのスコアを計算

with recursives(idx, pins1, pins2, pins3) as ( select s1.idx, s1.pins, s2.pins, s3.pins from score s1 left join score s2 on (s2.idx = s1.idx + 1) left join score s3 on (s3.idx = s1.idx + 2)),f(idx, pins1, pins2, pins3) as ( select idx, pins1, pins2, pins3 from s where idx = 1 union all select s.idx, s.pins1, s.pins2, s.pins3 from s join f on (s.idx = f.idx + case when f.pins1 = 10 then 1 else 2 end)),sof(idx, pins1, pins2, pins3, score_of_frame) as ( select idx , pins1, pins2, pins3 , case when pins1 = 10 then pins1 + pins2 + pins3 when pins1 + pins2 = 10 then pins1 + pins2 + pins3 else pins1 + pins2 end as score_of_frame from f)select row_number() over w as frame , pins1 , pins2 , case row_number() over w when 10 then pins3 else null end as pins3 , score_of_frame , sum(score_of_frame) over w from sofwindow w as (order by idx)

Page 4: textsearch_jaで全文検索

09/06/20

告知など

● PostgreSQL的な話● 8.4RC1が6/14に出ました

● JPUG北海道支部的な話● 5/13に勉強会を開催

● 次は7/8?● JPUG的な話

● JPUG 10th Anniversary Conference(11/20~21)

Page 5: textsearch_jaで全文検索

09/06/20

PostgreSQLと全文検索

Page 6: textsearch_jaで全文検索

09/06/20

PostgreSQLでの全文検索

● PostgreSQL8.2以前● contrib/tsearch2

● PostgreSQL8.3● 全文検索機能が本体に含まれるようになった。

● 日本語は未対応

● textsearch_ja● 日本語で全文検索を行なうための拡張モジュール

● http://textsearch-ja.projects.postgresql.org/● textsearch_sennaの話は今日はありません

Page 7: textsearch_jaで全文検索

09/06/20

資料に自信が無いのでとりあえずデモ

Page 8: textsearch_jaで全文検索

09/06/20

デモの構成

● PostgreSQL 8.4RC1(8.3でも同じです)● データベースエンコーディングはutf-8● PostgreSQLのドキュメントを格納したテーブル(html)● レコード数:900くらい

● 詳しくはブースで!!=> \d docs_en

カラム | 型 |--------+----------+------------ id | integer | path | text | title | text | body | text |

Page 9: textsearch_jaで全文検索

09/06/20

とりあえず検索する方法

Page 10: textsearch_jaで全文検索

09/06/20

方法1:関数インデックス

● インデックスを1つ作るだけで高速な全文検索ができる

インデックス作成=> CREATE INDEX docs_en_idx ON docs_en USING GIN(to_tsvector('english', body));CREATE INDEX

検索=> SELECT title FROM docs_en WHERE to_tsvector('english', body) @@ to_tsquery('english', 'search');

=> SELECT title FROM docs_en WHERE body ILIKE '%search%';

Page 11: textsearch_jaで全文検索

09/06/20

全文検索の基本

● tsvector、tsqueryはそれぞれデータ型

● to_tsvector、(plain)to_tsqueryはtext型をtsvector、tsqueryに変換する関数

tsvector @@ tsquery(検索対象) (検索式)

=> True / False

Page 12: textsearch_jaで全文検索

09/06/20

方法2:tsvector用の列を追加

● 高速。特にスコアによるソートをする場合

● 本文が更新された時にtsvectorは自動的に更新されない(トリガやバッチを使う必要がある)

● 容量は大きくなる

=> ALTER TABLE docs_en ADD vec tsvector;ALTER TABLE=> UPDATE docs_en SET vec = to_tsvector('english', body);UPDATE 936=> CREATE INDEX docs_en_idx2 ON docs_en USING GIN(vec);CREATE INDEX

検索=> SELECT title FROM docs_en WHERE vec @@ to_tsquery('english', 'search');

Page 13: textsearch_jaで全文検索

09/06/20

LIKEと全文検索の違い

=> select count(*) from docs_en where body ilike '%html%'; count------- 935(1 row)

=> select count(*) from docs_en where body ilike '%query%'; count------- 312(1 row)

=> select count(*) from docs_en where to_tsvector('english', body) @@ to_tsquery('html'); count------- 14(1 row)

=> select count(*) from docs_en where to_tsvector('english', body) @@ to_tsquery('query'); count------- 327(1 row)

Page 14: textsearch_jaで全文検索

09/06/20

tsvectorができるまで

Page 15: textsearch_jaで全文検索

09/06/20

転置インデックス

● 単語がどの文章に含まれているか(本の索引)● 転置インデックスを作成するためには文章を単語に分

ける必要がある(日本語の場合はわかち書き)● GIN(Generalized Inverted Index)● 全文検索の他にも、配列型などにも使われる

1 単語1 単語2

2 単語1 単語3 単語5

3 単語2 単語3 単語4

4 単語1 単語5

単語1

単語2

単語3

単語4

1,2,4

1,3

2,3

3

単語5 2,5

Page 16: textsearch_jaで全文検索

09/06/20

to_tsvector

● Parserが文章を23種類のtokenに分解

a fat cat sat on a mat - it <b>ate</b> a fat rats

a

fat

cat

sat

on

mat

it

ate

rats

Word, all ASCII XML tag

<b>

</b>

Space symbols

' '

-

Page 17: textsearch_jaで全文検索

09/06/20

to_tsvector

● token種別毎に正規化

a

fat

cat

sat

on

mat

it

ate

rats

Word, all ASCII

XML tag

<b>

</b>

Space symbols

' '

-

削除english_stem

fat

cat

sat

mat

ate

rat

Stopwordの削除語幹に縮小

Page 18: textsearch_jaで全文検索

09/06/20

token=> select * from ts_token_type('default'); tokid | alias | description-------+-----------------+------------------------------------------ 1 | asciiword | Word, all ASCII 2 | word | Word, all letters 3 | numword | Word, letters and digits 4 | email | Email address 5 | url | URL 6 | host | Host 7 | sfloat | Scientific notation 8 | version | Version number 9 | hword_numpart | Hyphenated word part, letters and digits 10 | hword_part | Hyphenated word part, all letters 11 | hword_asciipart | Hyphenated word part, all ASCII 12 | blank | Space symbols 13 | tag | XML tag 14 | protocol | Protocol head... 23 | entity | XML entity(23 rows)

Page 19: textsearch_jaで全文検索

09/06/20

Dictionaries

=> \dF+ english Text search configuration "pg_catalog.english"Parser: "pg_catalog.default" Token | Dictionaries-----------------+------------------------------- asciihword | english_stem asciiword | english_stem email | simple file | simple float | simple host | simple… numword | simple sfloat | simple uint | simple url | simple url_path | simple version | simple word | english_stem

brankやtagにはDictionaryが設定されていない

email、host等は語幹縮小しない

Page 20: textsearch_jaで全文検索

09/06/20

Dictionary

● ストップワード● インデックスに格納しないトークンの削除("a","the")

● Snowball● 語幹縮小"queries" => "queri"

=> select to_tsvector('query queries'); to_tsvector------------- 'queri':1,2(1 row)

=> select to_tsvector('http://www.postgresql.org/about/history'); to_tsvector-------------------------------------------------------------------------------- '/about/history':3 'www.postgresql.org':2 'www.postgresql.org/about/history':1(1 row)

Page 21: textsearch_jaで全文検索

09/06/20

Dictionary

● Simple、Sysnonym、Ispell、Thesaurus、Snowball● share/tsearch_data/● どのtoken typeにどの辞書を適用するかは変更可能

(ALTER TEXT SEARCH CONFIG...)(share/tsearch_data/english.stop)imemymyselfweouroursourselvesyouyour...

Page 22: textsearch_jaで全文検索

09/06/20

LIKEと全文検索の違い

=> select count(*) from docs_en where body ilike '%html%'; count------- 935(1 row)

=> select count(*) from docs_en where body ilike '%query%'; count------- 312(1 row)

=> select count(*) from docs_en where to_tsvector('english', body) @@ to_tsquery('html'); count------- 14(1 row)

=> select count(*) from docs_en where to_tsvector('english', body) @@ to_tsquery('query'); count------- 327(1 row)

Page 23: textsearch_jaで全文検索

09/06/20

ここまでのまとめ

● Token種別"tag"に対してDictionaryが設定されていないので、htmlタグはtsvectorでは無視される

● Token種別"asciiword"に対して設定された辞書"english_stem"により、文字列"query"も"queries"も"queri"と認識される

Page 24: textsearch_jaで全文検索

09/06/20

全文検索の機能

Page 25: textsearch_jaで全文検索

09/06/20

問い合わせ式 - tsquery

● to_tsquery()● '&'(AND)、'|'(OR)、'!'(NOT)、括弧● 詳細な検索条件が指定できるが、構文エラーに注意

● plainto_tsquery()● Tokenを'&'で結合する

● tsquery同士を演算子(&&、||)で結合することができる

=> select to_tsquery('fat & cat') && !! plainto_tsquery('sat mat'); ?column?------------------------------------ 'fat' & 'cat' & !( 'sat' & 'mat' )(1 row)

Page 26: textsearch_jaで全文検索

09/06/20

ts_rank

● ts_rank([weights float4[], ] vector tsvector, query tsquery[, normalization]) returns float4● 色々オプションがあるらしい

● ts_rankの引数にはtsvectorが必要

SELECT … ts_rank(to_tsvector(body), …)=> 1000件ヒットすると、1000回to_tsvector()が呼ばれる

SELECT … ts_rank(vec, …)=> あらかじめtsvectorの列を用意しておく

Page 27: textsearch_jaで全文検索

09/06/20

ts_headline

● ts_headline([regconfig, ]text, tsquery[, text])● 結果の強調表示

● StartSel、StopSel等設定可能

=> select ts_headline('fat cat sat mat', to_tsquery('cat')); ts_headline------------------------ fat <b>cat</b> sat mat(1 row)

Page 28: textsearch_jaで全文検索

09/06/20

configuration

● 全文検索で使う関数のふるまいをまとめたもの

● 省略された場合、default_text_search_configが使われる(postgresql.confやsetコマンドで指定できる)

to_tsvector('english', body)to_tsvector('simple', body)to_tsvector(body)

=> \dF

List of text search configurations Schema | Name | Description------------+------------+--------------------------------------- pg_catalog | danish | configuration for danish language pg_catalog | dutch | configuration for dutch language pg_catalog | english | configuration for english language

Page 29: textsearch_jaで全文検索

09/06/20

textsearch_ja

Page 30: textsearch_jaで全文検索

09/06/20

textsearch_ja

● default parserではわかち書きできない

● http://textsearch-ja.projects.postgresql.org/● Mecabを使って形態素解析

● 8.3、8.4に対応。Windows用バイナリ有り=> select to_tsvector('日本語を使ったfull text searchの例'); to_tsvector------------------------------------------------ 'searchの例':3 'text':2 '日本語を使ったfull':1(1 row)

=> select to_tsvector('japanese', '日本語を使ったfull text searchの例'); to_tsvector--------------------------------------------------------- 'full':3 'search':5 'text':4 '使う':2 '例':6 '日本語':1(1 row)

Page 31: textsearch_jaで全文検索

09/06/20

textsearch_jaのparser

● 非自立語を削除

● 動詞、助動詞を正規化● 「使った」->「使う」

● 英数はdefault parserでparseする(htmlは削除される)● tsqueryは、単語のANDになる

=> select to_tsquery('japanese', '全文検索'); to_tsquery----------------- '全文' & '検索'(1 row)

Page 32: textsearch_jaで全文検索

09/06/20

textsearch_jaと他の全文検索

● textsearch_jaはPostgreSQL本体の全文検索機能の拡張として実装されている● クラッシュリカバリやPITRを使うことができる

● tsearch2に対応したアプリをそのまま使える(MeidaWikiとか)

● 「PostgreSQL上にMeidaWiki環境を構築」http://lets.postgresql.jp/documents/tutorial/mediawiki/

Page 33: textsearch_jaで全文検索

09/06/20

その他のtextsearch_jaの機能

● というかmecabの機能● テキストの正規化(ja_normalize)● ふりがな(furigana)● ひらがな<->カタカナ(hiragana,katakana)● 等々

Page 34: textsearch_jaで全文検索

09/06/20

おまけ

Page 35: textsearch_jaで全文検索

09/06/20

参考

● Building search.postgresql.org(PGCon 2008)● http://www.pgcon.org/2008/schedule/events/75.en.html● www.postgresql.orgで使われているコードは

pgweb.postgresql.orgで公開されている

● https://pgweb.postgresql.org/browser/trunk/portal/tools/search

Page 36: textsearch_jaで全文検索

09/06/20

● Some recent advences in full-text search(PGcon 2009)● http://www.pgcon.org/2009/schedule/events/119.en.html● 8.4での新機能

– 前方一致検索等

● 8.5以降で実装予定の機能等

– フレーズ検索等