Kouhei Sutou
kou****@clear*****
2014年 2月 5日 (水) 18:53:38 JST
須藤です。 In <CANgwiJyGX-cy-kLCR2enCFVu_Yi5yM2m+PqcJjaJy_nDxZh****@mail*****> "[groonga-dev,02105] groonga のテーブル設計がさっぱりわかりません" on Wed, 5 Feb 2014 14:51:01 +0900, Hiroyuki TAKEDA <leiqu****@gmail*****> wrote: > groonga を勉強しているのですが、テーブル設計の部分がさっぱりわかりません。 他のシステムとは違う考え方をしているので、たしかに最初はとっ つきにくいかもしれません。。。 レコードを管理するために使うテーブルを、全文検索(転置索引) 用の語彙表にも使っているのがピンとこない原因かもしれません。 他のシステムでは全文検索用の語彙表に関する情報はあまり表にで てきません。Solrでもトークナイザーなどは指定しますが、トーク ンをどう保存するかなどは指定しないですよね。 (「改定新版 Apache Solr入門」を読んだくらいの知識なので実は そうじゃないのかもしれませんが。。。) Groongaはトークナイザーだけではなく、トークンの保存の仕方も指 定する(語彙表を自分で定義する)のでわかりにくのかもしれませ ん。こうすることで既存のシステムと違うことになるので、わかり にくくなるという欠点はあるのですが、いくつか利点もあります。 たとえば、トークンにメタ情報をつけることができます。語彙表を 通常のテーブルと扱えるため、語彙表にカラムを追加して、そこに トークンに関する情報を保存できます。全文検索ではありませんが、 これを使えば、タグに重み情報やラベルをもたせる、といったこと ができます。(タグ検索も転置索引を使って実現しているので、全 文検索と仕組みは同じです。) こんなイメージです。 タグ用の語彙表: キー(トークン。タグ) | 重み | ラベル ------------------------------------------ groonga | 10 | Groonga solr | 5 | Apache Solr で、本題ですが。。。 > 例えば、Wikipedia のデータを検索できるようにするとして、solr のスキーマで書くと、 > > <fields> > <field name="id" type="string" indexed="true" stored="true" > required="true"/> > <field name="title" type="text_ja" indexed="true" stored="true" > required="true"/> > <field name="revision" type="int" indexed="false" stored="true"/> > <field name="user" type="string" indexed="true" stored="true"/> > <field name="userId" type="int" indexed="false" stored="true"/> > <field name="text_ja" type="text_ja" indexed="true" stored="true" > required="true"/> > <field name="timestamp" type="date" indexed="true" stored="true" > default="now" multiValued="false"/> > </fields> > > long 64b整数 / string 文字列 / text_ja 形態素解析による分かち / int 32b整数 / date 日付 > > を groonga でテーブル定義するとしたらどのようになるのでしょうか? こんな感じになります! * longはなかったので↓にはでてきません。Int32をInt64にすれ ば64ビット整数になります。 * 日付型はなく、時刻型になります。 詳細はコメントにしています。 -- # <field name="id" type="string" indexed="true" stored="true" required="true"/> # Groongaではレコードを一意に識別する値はキーとして扱う。 # 必須。required="true"相当? table_create Articles TABLE_PAT_KEY ShortText # <field name="title" type="text_ja" indexed="true" stored="true" required="true"/> column_create Articles title COLUMN_SCALAR Text # <field name="revision" type="int" indexed="false" stored="true"/> column_create Articles revision COLUMN_SCALAR Int32 # <field name="user" type="string" indexed="true" stored="true"/> column_create Articles user COLUMN_SCALAR ShortText # <field name="userId" type="int" indexed="false" stored="true"/> column_create Articles user_id COLUMN_SCALAR UInt64 # <field name="text_ja" type="text_ja" indexed="true" stored="true" required="true"/> column_create Articles text_ja COLUMN_SCALAR Text # <field name="timestamp" type="date" indexed="true" stored="true" default="now" multiValued="false"/> # default="now"相当の機能はありません。 column_create Articles timestamp COLUMN_SCALAR Time # ↓のtype="string"の転置索引用の語彙表: # <field name="id" type="string" indexed="true" stored="true" required="true"/> # <field name="user" type="string" indexed="true" stored="true"/> # Bigramでトークナイズする。 table_create Terms TABLE_PAT_KEY ShortText \ --default_tokenizer TokenBigram \ --normalizer NormalizerAuto # indexed="true"に相当。 column_create Terms articles_text COLUMN_INDEX|WITH_POSITION|WITH_SECTION \ Articles _key,user # ↓用の転置索引用の語彙表: # <field name="title" type="text_ja" indexed="true" stored="true" required="true"/> # <field name="text_ja" type="text_ja" indexed="true" stored="true" required="true"/> # text_jaに相当するのは--default_tokenizer TokenMecab。 table_create JapaneseTerms TABLE_PAT_KEY ShortText \ --default_tokenizer TokenMecab \ --normalizer NormalizerAuto # indexed="true"に相当。 column_create JapaneseTerms articles_text COLUMN_INDEX|WITH_POSITION|WITH_SECTION \ Articles title,text_ja # ↓用の転置索引用の語彙表: # <field name="timestamp" type="date" indexed="true" stored="true" default="now" multiValued="false"/> # カラムに入っている値(タイムスタンプ)そのものをトークンとして使うので # トークナイザーは指定しない。 # キーの型をTimeにしてArticles.timestamp(トークンの型)とあわせることがポイント。 table_create Times TABLE_PAT_KEY Time # Articles.timestamp用の転置索引の定義。indexed="true"に相当 column_create Times articles_timestamp COLUMN_INDEX Articles timestamp -- データはこんな感じで投入します。時刻はUNIX秒で指定することに 注意してください。("2013-10-13 23:40:46"という形式でも指定 できるのですが、タイムゾーンは指定できません。ローカルタイム として解釈されます。アプリケーション全体で扱いを統一する必要 があります。) -- load --table Articles [ {"_key": "5", "title": "アンパサンド", "revision": 49408724, "user": "Beatclick", "user_id": 17343, "text_ja": "記号文字", "timestamp": 1381707646} ] -- こんな感じで検索します。(JSONは整形済み。) -- select Articles --match_columns _key,title,user,text_ja --query 記号 [[0, 1391593982.92962, 0.00132989883422852], [[[1], [["_id", "UInt32"], ["_key", "ShortText"], ["revision", "Int32"], ["text_ja", "Text"], ["timestamp", "Time"], ["title", "Text"], ["user", "ShortText"], ["user_id", "UInt64"]], [1, "5", 49408724, "記号文字", 1381707646.0, "アンパサンド", "Beatclick", 17343]]]] -- > MySQLみたいなテーブル設計しか思い浮かばないので、 > 何故テーブルがインデックスの種類とデータ型を持つのかが不思議だったりします。 なるほど。 「インデックスの種類」というのはトークナイザーの指定というこ とですよね。 テーブルをデータ(レコード)を管理するためのテーブルではなく、 トークンを管理するための語彙表として使っているためです。 データを管理するためのテーブルのとき(↑の例でいえば、 Articlesテーブル)は「インデックスの種類」を指定する必要はあ りません。MySQLのようなテーブルと一緒です。 語彙表として使うときは、元のテキストをどうやってトークンに分 割して語彙表に入れるか、という処理のための情報としてトークナ イザーを指定します。 「データ型」は、キーの型のことですよね。 Groongaでは、テーブルの種類によって、MySQLでいうプライマリー キーが必須のテーブルとそうでないテーブルがあります。プライマ リーキーが必須のテーブルの場合は、キーが必須なので「データ型」 (キーの型)を指定します。 > groonga のテーブルはキーを管理する。とどこかで読んだかもしれないのですが、 > 検索したいフィールド分テーブルを作る?んでしょうか。 いえ、ここは(たぶん)Solrと同じで、検索の仕方の数だけ作れば 十分です。Solrではstring/text_jaと同じ文字列でも検索の仕方が 異なる場合は異なるtypeを定義します。Groongaもそれと同じで、 検索の仕方の数だけテーブルを作ればよいです。 ↑の例では、type="string"に相当するものとしてTermsテーブル、 type="text_ja"に相当するものとしてJapaneseTermsテーブルを定 義していますが、そのような感じです。 > 基本的な質問で恥ずかしいのですが教えてください。 いえいえ、Groongaに初めて触る人がどんなところでつまずきやすい のかがわかってこちらも助かるので、どんどん質問してください! -- 須藤 功平 <kou****@clear*****> 株式会社クリアコード <http://www.clear-code.com/> (03-6231-7270) Groongaサポート: http://groonga.org/ja/support/ パッチ採用はじめました: http://www.clear-code.com/recruitment/ コミットへのコメントサービスはじめました: http://www.clear-code.com/services/commit-comment.html