[groonga-dev,00538] Re: 前方一致検索、後方一致検索の組み合わせについて

Back to archive index

Kouhei Sutou kou****@clear*****
2011年 6月 23日 (木) 19:13:18 JST


須藤です。

In <01c5619f-50d3-3b99-9764-09bb37d78d8a @ api104>
  "[groonga-dev,00537] 前方一致検索、後方一致検索の組み合わせについて" on Mon, 20 Jun 2011 17:58:42 +0900,
  hirano_verf****@mail***** wrote:

> ドキュメントを読んだのですが見つけられなかったので教えてください。
> groongaまたはrroongaで前方一致、部分一致、後方一致をAND条件で行うことは可能でしょうか。

できないと思ったのですが、試してみたらできました!

> 例えば、menu_name というカラムに
> 
>  スープカレーライス
>  コーンスープ
>  スープカレーライス大盛り
> 
> といったデータがあった場合、MySQLでいう 
> 
>  WHERE menu_name LIKE 'スープ%ライス'
> 
> のようなことを行いたいです("スープカレーライス"がヒット)。

これでどうでしょうか?

  table_create --name MenuNames --flags TABLE_PAT_KEY|KEY_WITH_SIS --key_type ShortText
  table_create --name Menus --flags TABLE_HASH_KEY --key_type ShortText
  column_create --table Menus --name name --type MenuNames
  column_create --table Menus --name original --type Bool
  load --table Menus
  [
  {"_key":"menu1",name:"スープカレーライス",original:true},
  {"_key":"menu2",name:"コーンスープ",original:true},
  {"_key":"menu3",name:"スープカレーライス大盛り",original:true}
  ]
  select --table Menus --query "name._key:^スープ name._key:$ライス original:true"
  select --table Menus --filter "name._key @^ \"スープ\" && name._key @$ \"ライス\" && original == true"
結果:
  [[0,1308823544.04353,0.033253277],true]
  [[0,1308823544.07687,0.036181076],true]
  [[0,1308823544.1131,0.04834719],true]
  [[0,1308823544.16151,0.048366575],true]
  [[0,1308823544.20993,0.001149381],3]
  [[0,1308823544.21112,0.000710036],[[[1],[["_id","UInt32"],["_key","ShortText"],["original","Bool"],["name","MenuNames"]],[1,"menu1",true,"スープカレーライス"]]]]
  [[0,1308823544.21188,0.000427402],[[[1],[["_id","UInt32"],["_key","ShortText"],["original","Bool"],["name","MenuNames"]],[1,"menu1",true,"スープカレーライス"]]]]

1つめのselectは--queryを使った条件指定の方法で、2つめの方は
同じものを--filterで実現する方法です。


後方一致検索をする方法は
  http://groonga.org/ja/docs/tutorial/tutorial08.html
では「執筆中です。」になっていますが、今、書きました!
  https://github.com/groonga/groonga/blob/master/doc/source/tutorial/tutorial08.txt

> table_createコマンドのflagsオプションにTABLE_PAT_KEYと
> KEY_WITH_SISを指定することで、主キーによる前方一致検索・後
> 方一致検索の両方が可能となります。
>
> KEY_WITH_SISフラグを付与すると、データを追加する際に後方一
> 致用のレコードも追加されてしまいます。そのため、単純に検索
> すると、元のレコードに加えて自動的に追加されたレコードまで
> ヒットしてしまいます。元のレコードのみ検索するために、一工
> 夫必要になります。
>
> 例えば、元のレコードと自動的に追加されたレコードとの区別を
> つけるために、元のレコードであることを示すoriginalカラムを
> 追加して、検索時にはoriginalカラムが ``true`` も検索条件に
> 加えます。

↑というわけなので、originalカラムも使っています。


> ドキュメントの4.8.では主キーを利用した前方一致検索が紹介されていますが、主キー以外のカラムでも
> 行いたいと思っています。

うーん、例えば、これだとどうでしょうか?

  table_create --name Names --flags TABLE_PAT_KEY --key_type ShortText
  table_create --name Users --flags TABLE_HASH_KEY --key_type ShortText
  column_create --table Users --name name --type Names
  load --table Users
  [
  {"_key":"user1",name:"ひろゆき"},
  {"_key":"user2",name:"まろゆき"},
  {"_key":"user3",name:"ひろあき"}
  ]
  select --table Users --query name._key:@ひろ

結果:
  [[0,1308820824.30658,0.033150967],true]
  [[0,1308820824.33979,0.03630953],true]
  [[0,1308820824.37613,0.048380372],true]
  [[0,1308820824.42455,0.000387107],3]
  [[0,1308820824.42495,0.000298136],[[[2],[["_id","UInt32"],["_key","ShortText"],["name","Names"]],[1,"user1","ひろゆき"],[3,"user3","ひろあき"]]]]


前方一致検索する場合は必ずパトリシア木のキーにしなければいけ
ないのですが、カラムの型にパトリシア木テーブルを指定するとカ
ラム経由で使えます。

> rroongaでは prefix_search で前方一致, suffix_search で後方一致がありますが
> これらを組み合わせることはできるのでしょうか。
> (Groonga::Expression クラスあたりを利用する・・・?)

こんな感じでどうでしょうか!?

  # -*- encoding: utf-8 *-

  require 'fileutils'
  require 'groonga'

  db_path = "/tmp/db/db"
  FileUtils.rm_rf(File.dirname(db_path))
  FileUtils.mkdir_p(File.dirname(db_path))
  Groonga::Database.create(:path => db_path)

  Groonga::Schema.define do |schema|
    schema.create_table("MenuNames",
                        :type => :patricia_trie,
                        :key_type => :short_text,
                        :key_with_sis => true) do |table|
    end

    schema.create_table("Menus") do |table|
      table.reference("menu_name", "MenuNames")
      table.boolean("original")
    end
  end

  menus = Groonga["Menus"]
  menus.add(:menu_name => "スープカレーライス",
            :original => true)
  menus.add(:menu_name => "コーンスープ",
            :original => true)
  menus.add(:menu_name => "スープカレーライス大盛り",
            :original => true)
  menus.add(:menu_name => "カレーライスとわかめスープのセット",
            :original => true)

  records = menus.select do |record|
    record["menu_name._key"].prefix_search("スープ") &
      record["menu_name._key"].suffix_search("ライス") &
      (record.original == true)
  end

  require 'pp'
  pp records.collect {|record| record.menu_name.key} # => ["スープカレーライス"]

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

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




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