groongaで参照関係のあるレコードを簡単に削除するには

> もっとgroongaを知ってもらおう!ということで週刊groongaをはじめました。毎週木曜にgroongaやmroonga、rroongaのトピックを投稿予定です。

[gihyo.jp](http://gihyo.jp/)さんでgroongaの隔週連載が最終回を迎えました。groongaの最新情報と今後に興味があれば一読をおすすめします。

* [「最終回　groongaの今と未来」](http://gihyo.jp/dev/clip/01/groonga/0011)

第1回から第10回までの過去記事については、[隔週連載groonga](http://gihyo.jp/dev/clip/01/groonga)のページを参照してください。

今年も11/29に[全文検索エンジンgroongaを囲む夕べ 4](http://atnd.org/events/43461)を開催することになりました。groongaのいろんな話を聞きたい人にはおすすめです。参加登録はお早めに! 発表者も[募集](http://atnd.org/events/43461#application)しています。

# はじめに

オープンソースの[カラムストア機能付き全文検索エンジンgroonga](http://groonga.org/ja/)を公開しています。最新のバージョンは2013年8月29日にリリースした[3.0.7](http://groonga.org/ja/docs/news.html#release-3-0-7)です。

今回は、参照先レコードを削除したら参照元の値も連動して削除する方法について紹介します。

# 参照関係のあるレコードの例

例えば、とあるユーザーに関連付けられたURLがいくつかあるとします。ユーザー情報とURLのテーブルにそれぞれデータを格納して参照するようにするとなると、次のようなスキーマとなるでしょう。

```
table_create Users TABLE_HASH_KEY ShortText
table_create URLs TABLE_HASH_KEY ShortText

column_create Users bookmarks COLUMN_VECTOR URLs
column_create URLs author COLUMN_SCALAR Users

column_create URLs bookmarks_index COLUMN_INDEX Users bookmarks
```

これに、サンプルデータをロードします。

```
load --table Users
[
{"_key": "mori", "bookmarks": ["http://mroonga.org/", "http://groonga.org/", "http://ranguba.org/"]}
]

load --table URLs
[
{"_key": "http://groonga.org/", "author": "mori"}
{"_key": "http://mroonga.org/", "author": "hayashi"}
]
```

load完了時のURLsの内容は3件です。

|_id|_key|author|
|---|----|------|
|1|"http://mroonga.org"|"hayashi"|
|2|"http://groonga.org"|"mori"|
|3|"http://ranguba.org"||

このとき、UsersテーブルにURLsテーブルにあるキーを参照するデータが格納されています。そのため、単純に次のようにしてURLsテーブルの "http://groonga.org" というキーのレコードを削除することはできませんでした。

```
> delete URLs --key "http://groonga.org/"
```

実際に実行してみると次のようなエラーが発生していました。

```
[
  [
    -2,1380092940.21387,
    0.000238895416259766,
    "undeletable record (URLs:2) has value (bookmarks_index:1)",
    [
      ["is_deletable","db.c",1564]
    ]
  ],
  false
]
```

そこで、3.0.7以前と3.0.8での削除するときの違いを次に紹介します。

## 参照関係のあるレコードを削除する(3.0.7以前の場合)

delete URLs --key "http://groonga.org/" でレコードを削除する前に、Usersで参照しているデータを削除してからURLsのレコードを削除するというステップを踏む必要があります。

3.0.7以前の場合には、次のようにloadコマンドを使ってあらかじめベクターカラムに格納された値を削除します。

```
load --table Users
[
{"_key": "mori", "bookmarks": ["http://mroonga.org/", "http://ranguba.org/"]}
]
```

すると次のようにdeleteコマンドを使って削除することができます。

```
> delete URLs --key "http://groonga.org/"
[[0,1380768162.32286,0.000125885009765625],true]
```

URLsをselectしてみると、_keyが "http://groonga.org/" であるデータは削除されたので登録されているデータは3件から2件になりました。

```
> select URLs
[[0,1380768162.3231,5.76972961425781e-05],[[[2],[["_id","UInt32"],["_key","ShortText"],["author","Users"],["bookmarks_index","Users"]],[1,"http://mroonga.org/","hayashi",1],[3,"http://ranguba.org/","",1]]]]
```

上記を表にするとこんな感じです。

|_id|_key|author|
|---|----|------|
|1|"http://mroonga.org"|"hayashi"|
|3|"http://ranguba.org"||

## 参照関係のあるレコードを削除する(3.0.8の場合)

3.0.8では参照関係のあるレコードの削除がサポートされているので、delete URLs --key "http://groonga.org/" を実行するとURLsの該当するレコードを削除できます。

あらかじめ参照関係を確認して削除してからdeleteしなくても済むようになりました。

```
> delete URLs --key "http://groonga.org/"
[[0,1380768819.21763,0.000120878219604492],true]
```

これを実行しても、bookmarksカラムの関係ないデータは削除されません。
URLsテーブルをselectした結果からもわかるように、 "http://mroonga.org/" と "http://ranguba.org/" はそのままです。

```
[
  [
    [2],
    [["_id","UInt32"],["_key","ShortText"],["author","Users"],["bookmarks_index","Users"]],
    [1,"http://mroonga.org/","hayashi",1],
    [3,"http://ranguba.org/","",1]
  ]
]
```

## 参照関係のあるレコードを削除する(3.0.8の場合) その2

もうひとつの例として author を複数指定できるようにベクターカラムを使うようにしたものを試してみましょう。

```
table_create Users TABLE_HASH_KEY ShortText
table_create URLs TABLE_HASH_KEY ShortText

column_create Users bookmarks COLUMN_VECTOR URLs
column_create URLs author COLUMN_VECTOR Users

column_create URLs bookmarks_index COLUMN_INDEX Users bookmarks
```

ロードするデータも型を変更したので変えてみます。

```
load --table Users
[
{"_key": "mori", "bookmarks": ["http://mroonga.org/", "http://groonga.org/", "http://ranguba.org/"]}
{"_key": "hayashi", "bookmarks": ["http://groonga.org/", "http://droonga.org/"]}
{"_key": "nakano", "bookmarks": ["http://groonga.org/"]}
]

load --table URLs
[
{"_key": "http://groonga.org/", "author": ["mori", "hayashi"]}
{"_key": "http://mroonga.org/", "author": ["nakano"]}
]
```

同じように "http://groonga.org" をキーに持つデータを削除してみましょう。期待するのは、URLsで _keyを参照している箇所はまるっと削除してくれることです。
"mori" さん、"hayashi"さん、"nakano"さんはそれぞれ"bookmarks"カラムに "http://groonga.org" をデータにもっているのでこれが削除されていれば期待通りです。

この時点のUsersの登録内容は次の通りです。

|_id|_key|bookmarks|
|---|----|------|
|1|"mori"|"http://mroonga.org","http://groonga.org","http://ranguba.org"|
|2|"hayashi"|"http://groonga.org","http://droonga.org"|
|3|"nakano"|"http://groonga.org"|

この時点のURLsの登録内容は次の通りです。

|_id|_key|author|
|---|----|------|
|1|"http://mroonga.org"|"nakano"|
|2|"http://groonga.org"|"mori","hayashi"|
|3|"http://ranguba.org"||
|4|"http://droonga.org"||

```
> delete URLs --key "http://groonga.org/"
[[0,1380768819.21763,0.000120878219604492],true]
```

削除後にUsersテーブルをselectしてみると期待通りに "http://groonga.org" が削除されています。

```
[[[3],[["_id","UInt32"],["_key","ShortText"],["bookmarks","URLs"]],[1,"mori",["http://mroonga.org/","http://ranguba.org/"]],[2,"hayashi",["http://droonga.org/"]],[3,"nakano",[]]]]]
```

|_id|_key|bookmarks|
|---|----|------|
|1|"mori"|"http://mroonga.org","http://ranguba.org"|
|2|"hayashi"|"http://droonga.org"|
|3|"nakano"||

URLsテーブルを確認してみると、4件あったのが3件になっているので期待通りです。

```
[
  [
    [3],
    [
      ["_id","UInt32"],["_key","ShortText"],["author","Users"],["bookmarks_index","Users"]],
      [1,"http://mroonga.org/",["nakano"],1],
      [3,"http://ranguba.org/",[],1],
      [4,"http://droonga.org/",[],1]
    ]
  ]
]
```

|_id|_key|author|
|---|----|------|
|1|"http://mroonga.org"|"nakano"|
|3|"http://ranguba.org"||
|4|"http://droonga.org"||

# まとめ

今回は、参照先レコードを削除したら参照元の値も連動して削除する方法を紹介しました。
データ件数が少ないぶんには手作業で対応できることでも、件数が増えてくると面倒な削除操作がカスケード削除できることで便利になりました。

groongaに興味を持ったなら、まずは[インストール](http://groonga.org/ja/docs/install.html)して試してみてください。

groongaの基本的な動作を知るための[チュートリアル](http://groonga.org/ja/docs/tutorial.html)もあります。インストールしたら試してみてください。