Kouhei Sutou
kou****@clear*****
2015年 12月 30日 (水) 12:38:23 JST
須藤です。 In <AD096****@mva*****> "[groonga-dev,03817] rroonga / 複数キーワードによる検索" on Tue, 29 Dec 2015 22:15:58 +0900, Masatoshi SEKI <m_seki****@mva*****> wrote: > rroongaで複数のキーワードからなる検索を書く方法を調べていて次のblogを読みました。 > > http://www.clear-code.com/blog/2009/7/31.html > > injectとexpressionを使って次のようなコード片が出ています。 > > records = documents.select do |record| > words.inject(nil) do |expression, word| > sub_expression = record["content"] =~ word > if expression.nil? > sub_expression > else > expression & sub_expression > end > end > end > > > > sub_expressionのクラスはExpressionBuilderでしたっけ? はい、そんな感じのやつです。 > これの & や | などがnilを引数に取るとき自分自身を返すようにしたら > 次のように書けないでしょうか? > > records = documents.select do |record| > words.inject(nil) do |expression, word| > (record["content"] =~ word) & expression > end > end > > &,|の対称性がなくなるのがイマイチならメソッド名を変えてもかまいませんし、 > nilが不自然ならExpressionBuilder::NopとかSentinelとかTailとかでも… > > > > 複数キーワードの検索はよくありそうな処理なので、もっと簡単に書く方法はありますか? これはですね、selectで条件が入った配列を返す書き方があるので、 それを使うのがよいのです。(たぶん、この記事を書いた後に入っ た機能です。) コードにするとこんな感じです。 records = documents.select do |record| words.collect do |word| record["content"] =~ word end end その後、カラムにはメソッドでもアクセスできるようになったので、 Hashっぽくじゃなく、オブジェクトっぽく、こうも書けます。 records = documents.select do |record| words.collect do |word| record.content =~ word end end また、通常は自分でクエリーをパースして単語に直したくはないと 思うので、Groongaにクエリーをパースしてもらうのがおすすめで す。 query = "dRuby Rinda" # 「dRuby」と「Rinda」を両方含んでいればマッチ records = documents.select do |record| record.content.match(query) end queryにユーザーからの入力をそのまま入れるならシンタックスエ ラーも考慮します。Groongaにパースしてもらうと「(A OR B) C」 のような書き方もできるようになりますが、「(A」とかはエラーに なってしまうのです。 records = documents.select do |record| conditions = [] begin conditions << record.content.match(query) rescue Groonga::SyntaxError p $! end conditions end デフォルトではGoogleで使えるような「カラム:クエリー」という シンタックスも使えて、「name:dRuby」のようにすると 「record.content.match」としていても「record.name =~ "dRuby"」 のようなクエリーを書けるのですが、必要のないカラムまで検索さ れると困る場合はそれを無効にできます。 records = documents.select do |record| record.content.match(query, :allow_column => false) end まとめると、こんな感じがいいんじゃないかと思います。 records = documents.select do |record| conditions = [] begin conditions << record.content.match(query, :allow_column => false) rescue Groonga::SyntaxError p $! end conditions end -- 須藤 功平 <kou****@clear*****> 株式会社クリアコード <http://www.clear-code.com/> Groongaベースの全文検索システムを総合サポート: http://groonga.org/ja/support/ パッチ採用 - プログラミングが楽しい人向けの採用プロセス: http://www.clear-code.com/recruitment/ リーダブルコードワークショップ: http://www.clear-code.com/services/code-reader/readable-code-workshop.html