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/