# groonga - オープンソースのカラムストア機能付き全文検索エンジン

- English

# 4.10. マイクロブログ検索システムの作成

これまで学んだGroongaの機能を用いて、マイクロブログの検索システムを作成してみましょう。マイクロブログとは、Twitterのような短いメッセージを投稿するブログです。

## 4.10.1. テーブルの作成

まずは、テーブルを作成します。

    table_create --name Users --flags TABLE_HASH_KEY --key_type ShortText
    table_create --name Comments --flags TABLE_HASH_KEY --key_type ShortText
    table_create --name HashTags --flags TABLE_HASH_KEY --key_type ShortText
    table_create --name Bigram --flags TABLE_PAT_KEY --key_type ShortText --default_tokenizer TokenBigram --normalizer NormalizerAuto
    table_create --name GeoIndex --flags TABLE_PAT_KEY --key_type WGS84GeoPoint

    column_create --table Users --name name --flags COLUMN_SCALAR --type ShortText
    column_create --table Users --name follower --flags COLUMN_VECTOR --type Users
    column_create --table Users --name favorites --flags COLUMN_VECTOR --type Comments
    column_create --table Users --name location --flags COLUMN_SCALAR --type WGS84GeoPoint
    column_create --table Users --name location_str --flags COLUMN_SCALAR --type ShortText
    column_create --table Users --name description --flags COLUMN_SCALAR --type ShortText
    column_create --table Users --name followee --flags COLUMN_INDEX --type Users --source follower

    column_create --table Comments --name comment --flags COLUMN_SCALAR --type ShortText
    column_create --table Comments --name last_modified --flags COLUMN_SCALAR --type Time
    column_create --table Comments --name replied_to --flags COLUMN_SCALAR --type Comments
    column_create --table Comments --name replied_users --flags COLUMN_VECTOR --type Users
    column_create --table Comments --name hash_tags --flags COLUMN_VECTOR --type HashTags
    column_create --table Comments --name location --flags COLUMN_SCALAR --type WGS84GeoPoint
    column_create --table Comments --name posted_by --flags COLUMN_SCALAR --type Users
    column_create --table Comments --name favorited_by --flags COLUMN_INDEX --type Users --source favorites

    column_create --table HashTags --name hash_index --flags COLUMN_INDEX --type Comments --source hash_tags

    column_create --table Bigram --name users_index --flags COLUMN_INDEX|WITH_POSITION|WITH_SECTION --type Users --source name,location_str,description
    column_create --table Bigram --name comment_index --flags COLUMN_INDEX|WITH_POSITION --type Comments --source comment

    column_create --table GeoIndex --name users_location --type Users --flags COLUMN_INDEX --source location
    column_create --table GeoIndex --name comments_location --type Comments --flags COLUMN_INDEX --source location

### 4.10.1.1. Usersテーブル

ユーザーの名前や自己紹介文、フォローしているユーザー一覧など、ユーザー情報を格納するためのテーブルです。

`_key`
  ユーザーID
`name`
  ユーザー名
`follower`
  フォローしているユーザーの一覧
`favorites`
  お気に入りのコメント一覧
`location`
  ユーザーの現在地（緯度経度座標）
`location_str`
  ユーザーの現在地（文字列）
`description`
  ユーザーの自己紹介
`followee`
  `Users` テーブルの `follower` カラムに対するインデックス。 このインデックスを作ることで、あるユーザーをフォローしているユーザーを検索できるようになります。

### 4.10.1.2. Commentsテーブル

コメント内容や投稿日時、返信先情報など、コメントに関する内容を格納するテーブルです。

`_key`
  コメントID
`comment`
  コメント内容
`last_modified`
  投稿日時
`replied_to`
  返信元のコメント内容
`replied_users`
  返信先のユーザーの一覧
`hash_tags`
  コメントのハッシュタグの一覧
`location`
  投稿場所(緯度経度座標のため)
`posted_by`
  コメントを書いたユーザー
`favorited_by`
  `Users` テーブルの `favorites` カラムに対するインデックス。 このインデックスを作ることで、指定したコメントを誰がお気に入りに入れているのかを検索できるようになります。

### 4.10.1.3. HashTagsテーブル

コメントのハッシュタグを一覧で保存するためのテーブルです。

`_key`
  ハッシュタグ
`hash_index`
  「Comments.hash_tags」のインデックス。 このインデックスを作ることで、指定したハッシュタグのついているコメントの一覧を出すことが出来るようになります。

### 4.10.1.4. Bigramテーブル

ユーザー情報・コメントで全文検索が出来るようにするためのインデックスを格納するテーブルです。

`_key`
  単語
`users_index`
  ユーザー情報のインデックス。 このカラムは、ユーザー名「Users.name」、現在地「Users.location_str」、自己紹介文「Users.description」のインデックスになっています。
`comment_index`
  コメント内容「Comments.comment」のインデックス

### 4.10.1.5. GeoIndexテーブル

位置情報検索を効果的に行うための locationカラムのインデックスを保持するテーブルです。

`users_location`
  Usersテーブルのlocationカラムに対するインデックス
`comments_location`
  Commentsテーブルのlocationカラムに対するインデックス

## 4.10.2. データのロード

つづいて、テスト用データをロードします。

    load --table Users
    [
      {
        "_key": "alice",
        "name": "Alice",
        "follower": ["bob"],
        "favorites": [],
        "location": "152489000x-255829000",
        "location_str": "Boston, Massachusetts",
        "description": "Groonga developer"
      },
      {
        "_key": "bob",
        "name": "Bob",
        "follower": ["alice","charlie"],
        "favorites": ["alice:1","charlie:1"],
        "location": "146249000x-266228000",
        "location_str": "Brooklyn, New York City",
        "description": ""
      },
      {
        "_key": "charlie",
        "name": "Charlie",
        "follower": ["alice","bob"],
        "favorites": ["alice:1","bob:1"],
        "location": "146607190x-267021260",
        "location_str": "Newark, New Jersey",
        "description": "Hmm,Hmm"
      }
    ]

    load --table Comments
    [
      {
        "_key": "alice:1",
        "comment": "I've created micro-blog!",
        "last_modified": "2010/03/17 12:05:00",
        "posted_by": "alice",
      },
      {
        "_key": "bob:1",
        "comment": "First post. test,test...",
        "last_modified": "2010/03/17 12:00:00",
        "posted_by": "bob",
      },
      {
        "_key": "alice:2",
        "comment": "@bob Welcome!!!",
        "last_modified": "2010/03/17 12:05:00",
        "replied_to": "bob:1",
        "replied_users": ["bob"],
        "posted_by": "alice",
      },
      {
        "_key": "bob:2",
        "comment": "@alice Thanks!",
        "last_modified": "2010/03/17 13:00:00",
        "replied_to": "alice:2",
        "replied_users": ["alice"],
        "posted_by": "bob",
      },
      {
        "_key": "bob:3",
        "comment": "I've just used 'Try-Groonga' now! #groonga",
        "last_modified": "2010/03/17 14:00:00",
        "hash_tags": ["groonga"],
        "location": "146566000x-266422000",
        "posted_by": "bob",
      },
      {
        "_key": "bob:4",
        "comment": "I'm come at city of New York for development camp! #groonga #travel",
        "last_modified": "2010/03/17 14:05:00",
        "hash_tags": ["groonga", "travel"],
        "location": "146566000x-266422000",
        "posted_by": "bob",
      },
      {
        "_key": "charlie:1",
        "comment": "@alice @bob I've tried to register!",
        "last_modified": "2010/03/17 15:00:00",
        "replied_users": ["alice", "bob"],
        "location": "146607190x-267021260",
        "posted_by": "charlie",
      }
      {
        "_key": "charlie:2",
        "comment": "I'm at the Museum of Modern Art in NY now!",
        "last_modified": "2010/03/17 15:05:00",
        "location": "146741340x-266319590",
        "posted_by": "charlie",
      }
    ]

`Users` テーブルの `follower` カラムと `favorites` カラム、そして `Comments` テーブルの `replied_users` カラムは、ベクターカラムです。そのため、これらのカラムは配列で値を指定します。

`Users` テーブルの `location` カラムと、`Comments` テーブルの `location` カラムは、 `GeoPoint` 型です。この型での値の指定は、"[緯度]x[経度]"と記述して指定します。

`Comments` テーブルの `last_modified` カラムは、Time型です。

この型での値を指定する方法は2つあります。1つ目の方法は、1970年1月1日0時0分0秒からの経過秒数の値を直接指定する方法です。このとき、小数部分を指定することでマイクロ秒数での指定が可能です。指定した値は、データのロードの際にマイクロ秒を単位とする整数値に変換後、格納されます。 2つ目の方法は、文字列で日時と時刻を指定する方法です。"年/月/日 時:分:秒"というフォーマットで記述することで、データロードの際に文字列からキャストされ、マイクロ秒数の値が格納されます。

## 4.10.3. 検索

マイクロブログを検索してみましょう。

### 4.10.3.1. キーワードでユーザー検索

ここでは、 match_columnsパラメータ で扱った、複数カラムを対象とした検索を行います。

指定された文字列で、ユーザー名・現在地・自己紹介文を対象に検索をします。

実行例:

    select --table Users --match_columns name,location_str,description --query "New York" --output_columns _key,name
    # [
    #   [
    #     0,
    #     1337566253.89858,
    #     0.000355720520019531
    #   ],
    #   [
    #     [
    #       [
    #         1
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "name",
    #           "ShortText"
    #         ]
    #       ],
    #       [
    #         "bob",
    #         "Bob"
    #       ]
    #     ]
    #   ]
    # ]

「New York」をキーワードにユーザー検索した結果、New Yorkに住んでいる「Bob」がヒットしました。

### 4.10.3.2. 位置情報(GeoPoint)でユーザー検索

ここでは、GeoPoint型のカラムで検索をします。GeoPoint型については さまざまな検索条件 を参照してください。

次の例では、特定の場所から20km以内に住んでいる人を検索します。

実行例:

    select --table Users --filter 'geo_in_circle(location,"146710080x-266315480",20000)' --output_columns _key,name
    # [
    #   [
    #     0,
    #     1337566253.89858,
    #     0.000355720520019531
    #   ],
    #   [
    #     [
    #       [
    #         2
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "name",
    #           "ShortText"
    #         ]
    #       ],
    #       [
    #         "charlie",
    #         "Charlie"
    #       ],
    #       [
    #         "bob",
    #         "Bob"
    #       ]
    #     ]
    #   ]
    # ]

「Bob」と「Charlie」が「Grand Central Terminal」から20km以内に住んでいることがわかります。

### 4.10.3.3. あるユーザーをフォローしてるユーザーの検索

ここでは、 タグ検索・参照関係の逆引き の参照関係の逆引きをします。

次の例は、 `Users` テーブルの `follower` カラムにあるフォローリストを逆引きします。

実行例:

    select --table Users --query follower:@bob --output_columns _key,name
    # [
    #   [
    #     0,
    #     1337566253.89858,
    #     0.000355720520019531
    #   ],
    #   [
    #     [
    #       [
    #         2
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "name",
    #           "ShortText"
    #         ]
    #       ],
    #       [
    #         "alice",
    #         "Alice"
    #       ],
    #       [
    #         "charlie",
    #         "Charlie"
    #       ]
    #     ]
    #   ]
    # ]

「Alice」と「Charlie」が「Bob」をフォローしていることがわかります。

### 4.10.3.4. GeoPointでコメント検索

ある範囲内で書かれたコメントを検索します。

また、 ドリルダウン をおこないます。検索結果をハッシュタグとユーザーでドリルダウンし、ユーザー別・ハッシュタグ別のカウントを出します。

実行例:

    select --table Comments --filter 'geo_in_circle(location,"146867000x-266280000",20000)' --output_columns posted_by.name,comment --drilldown hash_tags,posted_by
    # [
    #   [
    #     0,
    #     1337566253.89858,
    #     0.000355720520019531
    #   ],
    #   [
    #     [
    #       [
    #         4
    #       ],
    #       [
    #         [
    #           "posted_by.name",
    #           "ShortText"
    #         ],
    #         [
    #           "comment",
    #           "ShortText"
    #         ]
    #       ],
    #       [
    #         "Charlie",
    #         "I'm at the Museum of Modern Art in NY now!"
    #       ],
    #       [
    #         "Bob",
    #         "I've just used 'Try-Groonga' now! #groonga"
    #       ],
    #       [
    #         "Bob",
    #         "I'm come at city of New York for development camp! #groonga #travel"
    #       ],
    #       [
    #         "Charlie",
    #         "@alice @bob I've tried to register!"
    #       ]
    #     ],
    #     [
    #       [
    #         2
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "_nsubrecs",
    #           "Int32"
    #         ]
    #       ],
    #       [
    #         "groonga",
    #         2
    #       ],
    #       [
    #         "travel",
    #         1
    #       ]
    #     ],
    #     [
    #       [
    #         2
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "_nsubrecs",
    #           "Int32"
    #         ]
    #       ],
    #       [
    #         "charlie",
    #         2
    #       ],
    #       [
    #         "bob",
    #         2
    #       ]
    #     ]
    #   ]
    # ]

このクエリは、ニューヨークのセントラルパークから20km圏内で投稿されたコメントを検索します。

指定した範囲が20kmなので、位置情報を含むすべてのコメントが検索されました。#groongaというハッシュタグが2件、#travelというハッシュタグが1件で、BobとCharlieがコメントしているのは2件あります。

### 4.10.3.5. キーワードでコメント検索

あるキーワードを含むコメントを検索します。そして、 さまざまな検索条件 で言及している _score を出してみます。

実行例:

    select --table Comments --query comment:@Now --output_columns comment,_score
    # [
    #   [
    #     0,
    #     1337566253.89858,
    #     0.000355720520019531
    #   ],
    #   [
    #     [
    #       [
    #         2
    #       ],
    #       [
    #         [
    #           "comment",
    #           "ShortText"
    #         ],
    #         [
    #           "_score",
    #           "Int32"
    #         ]
    #       ],
    #       [
    #         "I've just used 'Try-Groonga' now! #groonga",
    #         1
    #       ],
    #       [
    #         "I'm at the Museum of Modern Art in NY now!",
    #         1
    #       ]
    #     ]
    #   ]
    # ]

'Now'をキーワードに使っているので、このクエリは2件のコメントを返します。 _score の値として 'Now'のカウントを含んでいます。

### 4.10.3.6. キーワードと位置情報で検索

あるキーワードと位置情報の両方でコメントを検索します。 --query と --filter オプションの両方を使用した場合、両方の条件に一致するレコードを返します。

実行例:

    select --table Comments --query comment:@New --filter 'geo_in_circle(location,"146867000x-266280000",20000)' --output_columns posted_by.name,comment --drilldown hash_tags,posted_by
    # [
    #   [
    #     0,
    #     1337566253.89858,
    #     0.000355720520019531
    #   ],
    #   [
    #     [
    #       [
    #         1
    #       ],
    #       [
    #         [
    #           "posted_by.name",
    #           "ShortText"
    #         ],
    #         [
    #           "comment",
    #           "ShortText"
    #         ]
    #       ],
    #       [
    #         "Bob",
    #         "I'm come at city of New York for development camp! #groonga #travel"
    #       ]
    #     ],
    #     [
    #       [
    #         2
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "_nsubrecs",
    #           "Int32"
    #         ]
    #       ],
    #       [
    #         "groonga",
    #         1
    #       ],
    #       [
    #         "travel",
    #         1
    #       ]
    #     ],
    #     [
    #       [
    #         1
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "_nsubrecs",
    #           "Int32"
    #         ]
    #       ],
    #       [
    #         "bob",
    #         1
    #       ]
    #     ]
    #   ]
    # ]

両方の条件をみたすコメントが1件あります。ドリルダウンの結果も含まれ、Bobによるコメントであることがわかります。

### 4.10.3.7. ハッシュタグでコメントを検索

あるハッシュタグのついているコメントを検索します。テーブルの参照関係を逆にたどってみましょう。

実行例:

    select --table Comments --query hash_tags:@groonga --output_columns posted_by.name,comment --drilldown posted_by
    # [
    #   [
    #     0,
    #     1337566253.89858,
    #     0.000355720520019531
    #   ],
    #   [
    #     [
    #       [
    #         2
    #       ],
    #       [
    #         [
    #           "posted_by.name",
    #           "ShortText"
    #         ],
    #         [
    #           "comment",
    #           "ShortText"
    #         ]
    #       ],
    #       [
    #         "Bob",
    #         "I've just used 'Try-Groonga' now! #groonga"
    #       ],
    #       [
    #         "Bob",
    #         "I'm come at city of New York for development camp! #groonga #travel"
    #       ]
    #     ],
    #     [
    #       [
    #         1
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "_nsubrecs",
    #           "Int32"
    #         ]
    #       ],
    #       [
    #         "bob",
    #         2
    #       ]
    #     ]
    #   ]
    # ]

このクエリは#groongaハッシュタグを含む2件のコメントを返します。投稿者のドリルダウン結果を2件含んでいて、Bobが投稿したことがわかります。

### 4.10.3.8. ユーザーIDでコメントを検索

あるユーザーが投稿したコメントを検索します。

実行例:

    select --table Comments --query posted_by:bob --output_columns comment --drilldown hash_tags
    # [
    #   [
    #     0,
    #     1337566253.89858,
    #     0.000355720520019531
    #   ],
    #   [
    #     [
    #       [
    #         4
    #       ],
    #       [
    #         [
    #           "comment",
    #           "ShortText"
    #         ]
    #       ],
    #       [
    #         "First post. test,test..."
    #       ],
    #       [
    #         "@alice Thanks!"
    #       ],
    #       [
    #         "I've just used 'Try-Groonga' now! #groonga"
    #       ],
    #       [
    #         "I'm come at city of New York for development camp! #groonga #travel"
    #       ]
    #     ],
    #     [
    #       [
    #         2
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "_nsubrecs",
    #           "Int32"
    #         ]
    #       ],
    #       [
    #         "groonga",
    #         2
    #       ],
    #       [
    #         "travel",
    #         1
    #       ]
    #     ]
    #   ]
    # ]

このクエリはBobによる4件のコメントを返します。ハッシュタグによるドリルダウン結果も含まれ、#groongaが2件、#travelが1件であることがわかります。

### 4.10.3.9. ユーザーのお気に入りのコメント一覧

あるユーザーのお気に入りコメントを検索します。

実行例:

    select --table Users --query _key:bob --output_columns favorites.posted_by,favorites.comment
    # [[0, 1337566253.89858, 0.000355720520019531], [[[0], []]]]

このクエリはBobのお気に入りのコメント一覧を返します。

### 4.10.3.10. 投稿時間でコメントを検索

コメントの投稿時間で検索をします。Time 型については いろいろなデータの保存 を参照してください。

ある時刻よりも古いコメントを検索します。

実行例:

    select Comments --filter 'last_modified<=1268802000' --output_columns posted_by.name,comment,last_modified --drilldown hash_tags,posted_by
    # [
    #   [
    #     0,
    #     1337566253.89858,
    #     0.000355720520019531
    #   ],
    #   [
    #     [
    #       [
    #         5
    #       ],
    #       [
    #         [
    #           "posted_by.name",
    #           "ShortText"
    #         ],
    #         [
    #           "comment",
    #           "ShortText"
    #         ],
    #         [
    #           "last_modified",
    #           "Time"
    #         ]
    #       ],
    #       [
    #         "Alice",
    #         "I've created micro-blog!",
    #         1268795100.0
    #       ],
    #       [
    #         "Bob",
    #         "First post. test,test...",
    #         1268794800.0
    #       ],
    #       [
    #         "Alice",
    #         "@bob Welcome!!!",
    #         1268795100.0
    #       ],
    #       [
    #         "Bob",
    #         "@alice Thanks!",
    #         1268798400.0
    #       ],
    #       [
    #         "Bob",
    #         "I've just used 'Try-Groonga' now! #groonga",
    #         1268802000.0
    #       ]
    #     ],
    #     [
    #       [
    #         1
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "_nsubrecs",
    #           "Int32"
    #         ]
    #       ],
    #       [
    #         "groonga",
    #         1
    #       ]
    #     ],
    #     [
    #       [
    #         2
    #       ],
    #       [
    #         [
    #           "_key",
    #           "ShortText"
    #         ],
    #         [
    #           "_nsubrecs",
    #           "Int32"
    #         ]
    #       ],
    #       [
    #         "alice",
    #         2
    #       ],
    #       [
    #         "bob",
    #         3
    #       ]
    #     ]
    #   ]
    # ]

このクエリは2010/03/17 14:00:00以前の5件のコメントを返します。投稿者によるドリルダウン結果も含まれ、Aliceが2件、Bobが3件であることがわかります。