[groonga-dev,00842] Re: rroonga での scoreの設定方法

Back to archive index

Kouhei Sutou kou****@clear*****
2012年 5月 10日 (木) 16:00:52 JST


須藤です。

In <20120****@gmail*****>
  "[groonga-dev,00841] rroonga での scoreの設定方法" on Wed, 09 May 2012 21:45:57 +0900,
  Shinya Kawaji <kawaj****@gmail*****> wrote:

> rroongaで、任意の値を scoreに設定したいのですが、方法は有りますでしょう
> か。

はい、あります!

> 具体的にやりたいことは、一つは「ランダムに出力したい」ということで、
> 
> http://groonga.org/ja/docs/tutorial/search.html#scorer
> にある
> 
>   select --table Site --filter "1" --scorer "_score = rand()" \
>   --output_columns _id,_key,_score --sortby _score
> 
> と同様のことがしたいと思います。例えば
> 
>   Groonga["Site"].select{|t| t.score = rand() }.sort(["_score"])
> 
> のような記述になるかと思うのですが、
> 
> NoMethodError: undefined method `score=' for #<Groonga::RecordExpressionBuilder:0x00000012007410>
> 
> となります。

なるほど!
この場合はこうなります。
(もっとRubyっぽく書けるといいなぁとは思っているのですが、よ
い構文が思いつかなくてこのままになっています。。。)

  result_set = Groonga["Site"].select
  # ↑が--filter "1"相当
  result_set.select("_score = rand() && false", :syntax => :script)
  # ↑が--scorer "_score = rand()"相当
  result_set.sort(["_score"])
  # ↑が--sortby "_score"相当

最初に引数もブロックもなしでselectを呼んでいますが、これがポ
イントです。

groongaでは検索処理の結果を一時テーブル(*)に格納します。実
は、_scoreカラムがあるのはSiteなどの永続的なテーブルではなく
一時テーブルにあります。これは、永続的なテーブルに_scoreを追
加すると同時に複数の検索結果に_scoreをつけられないからです。
引数もブロックもないselectはこの一時テーブルを作っています。
何も条件を指定していないのですべてのレコードが格納されていま
す。

(*) 一時テーブルとはSiteなど名前がついていない、データベース
    を閉じるとなくなってしまうテーブルのことです。

次に、result_set.selectで--filterに指定できる書式で、各レコー
ドに適用する式を指定しています。result_set.selectは本来は検
索するための処理なのでresult_set.selectの結果も一時テーブル
になります。しかし、ここでは"_score = rand()"をすべてのレコー
ドで実行するためだけに呼んでいて結果は使いません(*)。そのた
め、" && false"を最後につけて1つもレコードがヒットしないよう
にしています。これで使わない検索結果にレコードが追加されなく
なります。

(*) result_set.selectの副作用だけが重要ということ。

あとは、いつも通りsortを呼んでいます。

> もう一つは「位置情報を元にしたソートを行いたい」ことで、
> 
> http://groonga.org/ja/docs/tutorial/search.html#id2
> にある
> 
>   select --table Site --query "_id:1 OR _id:2" \
>   --output_columns _key,location,_score \
>   --scorer '_score = geo_distance(location, "128515259x503187188")' \
>   --sortby -_score
> 
>> 
>   Groonga["Site"].select{|t|
>     t.score = "geo_distance(location, "128515259x503187188")"
>   }.sort(["_score"])
> 
> のような書き方かと思いましたが、これも上手くいきません。
> 
> 
> なにか書き方がありますでしょうか。
> もしくは、rroongaでは未実装なのでしょうか。

こちらも前述の通り実現できるのですが、まだ、Rubyっぽく書けま
せん。

  result_set = Groonga["Site"].select
  result_set.select("_score = geo_distance(location, '128515259x503187188') && false", :syntax => :script)
  result_set.sort(["_score"])


と、こんな感じで書けるとRubyっぽいかなぁというのを思いつきま
した。

  result_set.execute do |record|
    record.score = record.eval do
      # この中のselfは外のselfじゃなくして、
      # groonga内の関数(geo_distance)などがレシーバーなし
      # で使えるようにする。
      # ブロック内でselfをすり替えるのは直感的じゃないので好
      # きじゃないけどevalという名前にすれば、まぁ、いいかなぁと。
      geo_distance(location, "128515259x503187188")
    end
  end

が、なんか、書いてみるとそんなにパッとしないような気がしてき
ました。。。

-- 
須藤 功平 <kou****@clear*****>
株式会社クリアコード <http://www.clear-code.com/> (03-6231-7270)

groongaサポート:
  http://groonga.org/ja/support/
プログラミングが好きなソフトウェア開発者を募集中:
  http://www.clear-code.com/recruitment/




groonga-dev メーリングリストの案内
Back to archive index