全文検索そのものと、キーワードのハイライトについてはよかったのですが、pgroonga.score関数でとれるスコアを使ったソートで問題が・・・(つづく)
前回の続き、この問題について。端的に言うと、せっかく作ったインデックスを使ってくれずスコアが取れないため、結果として正しくソートが出来ていません。
pgroonga.score関数の仕様はこの様になっています。
pgroonga.score関数はインデックスを使わずに全文検索した場合は常に0.0を返します。言い換えると、pgroonga.score関数はシーケンシャルスキャンで全文検索を実行した場合は常に0.0を返します。
そして、チュートリアルでは作ったインデックスを使わせる対策として、enable_seqscanをoffにしていました。
確実にpgroongaインデックスを使うためにシーケンシャルスキャンを無効にします。
直にクエリを発行するなら、SELECTの前後でenable_seqscanをoff→DEFAULTと一時的に切り替えてあげればよいと思うのですが、ActiveRecordを使っているとクエリが発行されるのは実際に結果が必要になったタイミングなので、この様に書いても意味がありません。
> Article.connection.execute 'SET enable_seqscan = off'
> articles = Article.full_text_search('桃太郎')
> Article.connection.execute 'SET enable_seqscan = DEFAULT'
SELECTの前後にコールバックがあればいいのですが、探してみても用意されてはいないみたい。
(after_initialize and after_findは期待している動きとはちょっと違う。)
かと言って、postgresql.confに書いてしまうのはやり過ぎ、というか全体でシーケンシャルスキャンをoffにしてしまうのは問題だと思います。
このような場合、どう実装するのがいいのでしょうか。
[15:30 追記]
ActiveRecordが生成するSQLをpsqlで流すとインデックスが使われることは確認できているので、enable_seqscanをどこで切り替えるか、に悩んでいます。
> puts Article.full_text_search('桃太郎').to_sql
SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles" WHERE (title %% '桃太郎' OR body %% '桃太郎') ORDER BY pgroonga_score DESC
=> EXPLAIN SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles" WHERE (title %% '桃太郎' OR body %% '桃 太郎') ORDER BY pgroonga_score DESC;
QUERY PLAN
-------------------------------------------------------------------------
Sort (cost=1.14..1.15 rows=4 width=242)
Sort Key: (score(articles.*)) DESC
-> Seq Scan on articles (cost=0.00..1.10 rows=4 width=242)
Filter: ((title %% '桃太郎'::text) OR (body %% '桃太郎'::text))
(4 rows)
=> SET enable_seqscan = off;
SET
=> EXPLAIN SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles" WHERE (title %% '桃太郎' OR body %% '桃太郎') ORDER BY pgroonga_score DESC;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Sort (cost=4.08..4.09 rows=4 width=242)
Sort Key: (score(articles.*)) DESC
-> Bitmap Heap Scan on articles (cost=0.00..4.04 rows=4 width=242)
Recheck Cond: ((title %% '桃太郎'::text) OR (body %% '桃太郎'::text))
-> BitmapOr (cost=0.00..0.00 rows=2 width=0)
-> Bitmap Index Scan on index_articles_on_id_and_title_and_body (cost=0.00..0.00 rows=2 width=0)
Index Cond: (title %% '桃太郎'::text)
-> Bitmap Index Scan on index_articles_on_id_and_title_and_body (cost=0.00..0.00 rows=1 width=0)
Index Cond: (body %% '桃太郎'::text)
(9 rows)
=> SET enable_seqscan = DEFAULT;
SET
=> EXPLAIN SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles" WHERE (title %% '桃太郎' OR body %% '桃太郎') ORDER BY pgroonga_score DESC;
QUERY PLAN
-------------------------------------------------------------------------
Sort (cost=1.14..1.15 rows=4 width=242)
Sort Key: (score(articles.*)) DESC
-> Seq Scan on articles (cost=0.00..1.10 rows=4 width=242)
Filter: ((title %% '桃太郎'::text) OR (body %% '桃太郎'::text))
(4 rows)