読者です 読者をやめる 読者になる 読者になる

Tokyo Cabinetでキーが重複したレコードを格納する

Tokyo Cabinetの仕様書を見ると、

B+木のデータベースでは、キーが重複する複数のレコードを格納することができます。このデータベースに対しては、ハッシュ表のデータベースと同様に、キーを指定してレコードを格納したり取り出したり削除したりすることができます。レコードはユーザが指示した比較関数に基づいて整列されて格納されます。カーソルを用いて各レコードを昇順または降順で参照することができます。この機構によって、文字列の前方一致検索や数値の範囲検索が可能になります。

とあるのだが、↓みたいにB+木のデータベースにキーが重複したレコードを格納しても格納されるのは「database -> oracle」のみとなる。

/* レコードを格納する */
if(!tcbdbput2(bdb, "database", "mysql")     ||
   !tcbdbput2(bdb, "database", "postgresql") ||
   !tcbdbput2(bdb, "database", "oracle")){
  ecode = tcbdbecode(bdb);
  fprintf(stderr, "put error: %s\n", tcbdberrmsg(ecode));
}
narazuya@bokkko% tcbmgr list -pv casket.tcb
database        oracle
narazuya@bokkko%  

う〜ん、何故だ?と思ってAPIを調べてたら、

bool tcbdbputdup2(TCBDB *bdb, const char *kstr, const char *vstr);
`bdb' specifies the B+ tree database object connected as a writer.
`kstr' specifies the string of the key.
`vstr' specifies the string of the value.
If successful, the return value is true, else, it is false.
If a record with the same key exists in the database, the new record is placed after the existing one.
The function `tcbdbputdup3' is used in order to store records into a B+ tree database object with allowing duplication of keys.

重複(duplicate)したキーを格納するには、後ろにdupの付いた関数を使わないといけないようだ。

/* レコードを格納する */
if(!tcbdbputdup2(bdb, "database", "mysql")     ||
   !tcbdbputdup2(bdb, "database", "postgresql") ||
   !tcbdbputdup2(bdb, "database", "oracle")){
  ecode = tcbdbecode(bdb);
  fprintf(stderr, "put error: %s\n", tcbdberrmsg(ecode));
}
narazuya@bokkko% tcbmgr list -pv casket.tcb
database        mysql
database        postgresql
database        oracle
narazuya@bokkko% 


ちなみに重複したキーのレコードを削除する場合、tcbdboutもしくはtcbdbout2だと最初の1つだけを削除し、
tcbdbout3だと該当するすべてのレコードを削除する。