PostgreSQL 9.6 全文検索 演算子/tsquery()を使わず全文検索


ぬこは激怒した。
かのPostgreSQL 9.6 beta1文書 全文検索演算子の <-> の使用例の記述を改善せねばと決意した。

<-> 演算子を使うとどうなるの?

tsquery_phrase()のsyntax suggerであるのは想像できるのだが、distanceをどう設定しているのか文書を読んでも今ひとつ良くわからぬ。
結局、動かすかソースを見るしかないのだろうか。

とりあえず、<-> 演算子を使ってみる。

tsearch=# SELECT                
    to_tsquery('english', 'cat') <-> to_tsquery('english', 'dog');
    ?column?     
-----------------
 'cat' <-> 'dog'
(1 row)

はあ。という結果なんだけど・・・

tsquery <-> tsquery の演算子が適用された結果は、やっぱりtsquery型になる。
上の例は、TEXT型としての 'cat' <-> 'dog' ではなく、
tsquery型の外部表現である 'cat' <-> 'dog' であることに注意。

次にtsquery_phrase()だけを動かしてみた。
まず、tsquery_phrase の第3引数に10を設定して動かしてみる。

tsearch=# SELECT tsquery_phrase(
    to_tsquery('english', 'cat'), to_tsquery('english', 'dog'), 10);
  tsquery_phrase  
------------------
 'cat' <10> 'dog'
(1 row)

ふむ。それにしても、<10> というのは演算子なのか?
ただ、CREATE OPERATORを見るとわかるように、PostgreSQL演算子として使用可能な文字は、
以下の文字に限定されているはず。

The operator name is a sequence of up to NAMEDATALEN-1 (63 by default) characters from the following list:

  1. - * / < > = ~ ! @ # % ^ & | ` ?

なので、<10> のように間に数字がはいるものは、「SQL演算子」としては認められない。<10> のような演算子はあくまでも、tsquery_phrase() が生成した、tsquery型の記法として使われているだけだ。

次はdistanceに2を設定。

tsearch=# SELECT tsquery_phrase(
    to_tsquery('english', 'cat'), to_tsquery('english', 'dog'), 2);
 tsquery_phrase  
-----------------
 'cat' <2> 'dog'
(1 row)

うむ。じゃあ、distanceに1を設定したら・・・?

tsearch=# SELECT tsquery_phrase(
    to_tsquery('english', 'cat'), to_tsquery('english', 'dog'), 1);
 tsquery_phrase  
-----------------
 'cat' <-> 'dog'
(1 row)

!!!
なんと、1を設定したときには、 <1> ではなく <-> と解釈された。

つまり、
tsquery <-> tsquery
というのは、
tsquery_phrase(tsquery, tsquery, 1);
と同じ意味になる。

tsquery_phraseのdistanceが1のときには、実質上引数1に指定したキーワードと引数2に指定したキーワードが連続したという意味になる。
なので、<-> 演算子を使うとこーなる。

tsearch=# SELECT
  to_tsvector('Lion is a big cat. Cat is a small lion.')
  @@
  (to_tsquery('cat') <-> to_tsquery('lion'));
 ?column? 
----------
 f
(1 row)

cat と lion の間に別の語(small)が入っているために、一致しない(f)とみなされる。

tsearch=# SELECT
  to_tsvector('Lion is a big cat. Cat is a small lion.')
  @@
  (to_tsquery('small') <-> to_tsquery('lion'));
 ?column? 
----------
 t
(1 row)

この場合、small と lion は間に別の語が入らず連結しているので一致する(t)とみなされる。

to_tsquery()を使わずに全文検索する。

で、この検証をやってるときに

tsearch=# SELECT tsquery_phrase(
    to_tsquery('english', 'cat'), to_tsquery('english', 'dog'), 2);
 tsquery_phrase  
-----------------
 'cat' <2> 'dog'
(1 row)

の結果を見て、ふと思った。

これ、tsqueryの外部表現を文字列として渡したら、to_tsquery()を使わなくても全文検索できるってことか?

やってみた。

tsearch=# SELECT                
  to_tsvector('I like cats and dogs.')
  @@
  '''cat'' <2> ''dog''';
 ?column? 
----------
 t
(1 row)

お、想定どおりの結果が返ってきたw
実際右辺の文字列は暗黙のキャストによって、tsquery型に変換されているはず。

まあ、面倒くさいだけなので、フツーに to_tsquery() や tsquery_phrase() を使ったほうがいいとは思うけど。