マルチインデックステーブルを使う時、基本的にはプライマリーキーでレコードを特定して操作していますが、それ以外の条件で検索する場面もやはりあるので、セカンダリーインデックスの使い方を調査してみました。
定義
ポイントは2つあります。
- 1つ目は、マルチインデックステーブルの構造体を定義する時、セカンダリーインデックス用の関数を定義する必要があります
- 2つ目は、マルチインデックステーブルの type を定義する時、セカンダリーインデックスの定義情報を追加する必要があります
struct [[eosio::table]] person {
uint64_t id;
string name;
uint64_t age;
string address;
string tel;
auto primary_key() const { return id; }
uint64_t get_age() const { return age; } // ① get_age 関数を追加
};
// ② index_by 部分を追加
typedef eosio::multi_index<"people"_n, person, indexed_by<"age"_n, const_mem_fun<person, uint64_t, &person::get_age>>> address_index;
cleos get table
で使う
# デフォルトで条件なしで、プライマリーキーでソートされる
$ cleos get table addressbook addressbook people
{
"rows": [{
"id": 0,
"name": "山田",
"age": 38,
"address": "横浜",
"tel": "0451113333"
},{
"id": 1,
"name": "田中",
"age": 29,
"address": "渋谷",
"tel": "031112222"
},{
"id": 2,
"name": "山本",
"age": 45,
"address": "品川",
"tel": "0333334444"
}
],
"more": false
}
# --index で使うインデックス番号を指定すると、そのインデックスでソートされる
$ cleos get table addressbook addressbook people --key-type i64 --index 2
{
"rows": [{
"id": 1,
"name": "田中",
"age": 29,
"address": "渋谷",
"tel": "031112222"
},{
"id": 0,
"name": "山田",
"age": 38,
"address": "横浜",
"tel": "0451113333"
},{
"id": 2,
"name": "山本",
"age": 45,
"address": "品川",
"tel": "0333334444"
}
],
"more": false
}
# --lower でインデックス範囲の `from` を指定できる
$ cleos get table addressbook addressbook people --key-type i64 --index 2 --lower 30
{
"rows": [{
"id": 0,
"name": "山田",
"age": 38,
"address": "横浜",
"tel": "0451113333"
},{
"id": 2,
"name": "山本",
"age": 45,
"address": "品川",
"tel": "0333334444"
}
],
"more": false
}
# --upper でインデックス範囲の `to` を指定できる
$ cleos get table addressbook addressbook people --key-type i64 --index 2 --lower 30 --upper 40
{
"rows": [{
"id": 0,
"name": "山田",
"age": 38,
"address": "横浜",
"tel": "0451113333"
}
],
"more": false
}
アクションで検索
[[eosio::action]]
void search(uint64_t age) {
address_index addresses(_code, _code.value);
auto age_index = addresses.get_index<"age"_n>();
auto itr = age_index.lower_bound(age); // age 以上のデータを検索
for (; itr != age_index.end(); itr++)
{
print("id: ", itr->id, " name: ", itr->name, " age: ", itr->age, "\n");
}
}
アクションを呼び出すと下記になります。
トランザクション呼び出しのコンソールには1つ目の print
結果だけ表示されていますが、nodeos
側のログには想定どおりに該当データ分全部表示されています。
$ cleos push action addressbook search '[30]' -p addressbook
executed transaction: 9f95fc9f805526664867048660ef825f7f8dd846f3b1c509a813c2adcd597aa8 104 bytes 9412 us
# addressbook <= addressbook::search {"age":30}
>> id: 0 name: 山田 age: 38
warning: transaction executed locally, but may not be confirmed by the network yet ]
# nodeos 側のログを確認すると下記になります
debug 2018-12-12T21:42:06.546 thread-0 apply_context.cpp:28 print_debug ]
[(addressbook,search)->addressbook]: CONSOLE OUTPUT BEGIN =====================
id: 0 name: 山田 age: 38
id: 2 name: 山本 age: 45
[(addressbook,search)->addressbook]: CONSOLE OUTPUT END =====================
制限
- セカンダリーインデックスのデータ型は下記の型だけ
idx64 - Primitive 64-bit unsigned integer key
idx128 - Primitive 128-bit unsigned integer key, or a 128-bit fixed-size lexicographical key
idx256 - 256-bit fixed-size lexicographical key
idx_double - Double precision floating point key
idx_long_double - Quadruple precision floating point key
name 特別に、アカウントもサポートされている
- セカンダリーインデックスは、最大 16 個までしか作成できません
- 検索時は、範囲指定しか出来ません
まとめ
プライマリーキー以外に、セカンダリーインデックスを活用することで、他の条件でも検索出来ます。