EOSIO는 최대 16개의 인덱스를 활용하여 테이블을 정렬할 수 있다. 이번 섹션에서는 addressbook
컨트랙트에 다른 인덱스를 추가해서, 테이블의 레코드를 다른 방식으로 반복해 볼 것이다.
1단계: 테이블에 존재하는 데이터 삭제하기
이전에 언급했듯이, 데이터가 있으면 테이블의 구조를 변경시킬 수 없다. 첫 번째 단계는 이미 추가된 데이터를 삭제하는 것이다.
이전 튜토리얼에서 추가된 alice와 bob의 모든 레코드를 삭제한다.
cleos push action addressbook erase '["alice"]' -p alice@active
cleos push action addressbook erase '["bob"]' -p bob@active
2단계: 새로운 인덱스 멤버와 getter 추가하기
adressbook.cpp
컨트랙트에 새로운 변수를 추가하고 변수의 getter를 추가한다. 보조 인덱스는 숫자 속성을 가져야 하기 때문에 uint64_t
자료형인 age 변수를 추가한다.
uint64_t age;
uint64_t get_secondary_1() const { return age; }
3단계: addresses 테이블 구성에 보조 인덱스 추가하기
추가한 속성이 보조 인덱스로 정의 된 다음에는 address_index
테이블을 재설정 해주어야 한다.
typedef eosio::multi_index<"people"_n, person,
indexed_by<"byage"_n, const_mem_fun<person, uint64_t, &person::get_secondary_1>>
> address_index;
세 번째 변수를 보면, 인덱스를 인스턴스화하기 위해 indexed_by
구조체를 넘겨 주었다.
indexed_by
구조체에서, 인덱스의 이름은 "byage"
, 두 번째 타입 파라미터는 함수 호출 연산자를 지정한 것을 볼 수 있다. 이 때 두 번째 인자인 함수 호출 연산자는 인덱스 키로 const 값을 반환해야한다. 여기에선 직전 단계에서 만든 getter를 가리키고 있으므로, 다중 인덱스 테이블은 age
변수로 레코드를 인덱싱한다.
indexed_by<"byage"_n, const_mem_fun<person, uint64_t, &person::get_secondary_1>>
4단계: 컴파일하고 배포하기
컴파일하기
eosio-cpp -o addressbook.wasm addressbook.cpp --abigen
배포하기
cleos set contract addressbook CONTRACTS_DIR/addressbook
5단계: 테스트하기
레코드 삽입하기
cleos push action addressbook upsert '["alice", "alice", "liddell", 9, "123 drink me way", "wonderland", "amsterdam"]' -p alice@active
cleos push action addressbook upsert '["bob", "bob", "is a guy", 49, "doesnt exist", "somewhere", "someplace"]' -p bob@active
alice의 연락처를 나이 인덱스로 찾아보자. 여기서 --index 2
파라미터는 질의가 보조 인덱스에 적용됨을 나타내기 위해 사용된다. (2번 인덱스)
cleos get table addressbook addressbook people --upper 10 \
--key-type i64 \
--index 2
다음과 같은 결과가 나올 것이다.
{
"rows": [{
"key": "alice",
"first_name": "alice",
"last_name": "liddell",
"age": 9,
"street": "123 drink me way",
"city": "wonderland",
"state": "amsterdam"
}
],
"more": false
}
Bob의 나이를 찾아보자.
cleos get table addressbook addressbook people --upper 50 --key-type i64 --index 2
이것은 다음과 같은 값을 반환한다.
{
"rows": [{
"key": "alice",
"first_name": "alice",
"last_name": "liddell",
"age": 9,
"street": "123 drink me way",
"city": "wonderland",
"state": "amsterdam"
},{
"key": "bob",
"first_name": "bob",
"last_name": "is a loser",
"age": 49,
"street": "doesnt exist",
"city": "somewhere",
"state": "someplace"
}
],
"more": false
}
문제 없다!
마무리
이 지점까지의 모든 내용을 포함한 완전한 addressbook
컨트랙트의 코드는 다음과 같다:
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
using namespace eosio;
class [[eosio::contract]] addressbook : public eosio::contract {
public:
using contract::contract;
addressbook(name receiver, name code, datastream<const char*> ds): contract(receiver, code, ds) {}
[[eosio::action]]
void upsert(name user, std::string first_name, std::string last_name, uint64_t age, std::string street, std::string city, std::string state) {
require_auth( user );
address_index addresses(_code, _code.value);
auto iterator = addresses.find(user.value);
if( iterator == addresses.end() )
{
addresses.emplace(user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.age = age;
row.street = street;
row.city = city;
row.state = state;
});
}
else {
std::string changes;
addresses.modify(iterator, user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.age = age;
row.street = street;
row.city = city;
row.state = state;
});
}
}
[[eosio::action]]
void erase(name user) {
require_auth(user);
address_index addresses(_self, _code.value);
auto iterator = addresses.find(user.value);
eosio_assert(iterator != addresses.end(), "Record does not exist");
addresses.erase(iterator);
}
private:
struct [[eosio::table]] person {
name key;
std::string first_name;
std::string last_name;
uint64_t age;
std::string street;
std::string city;
std::string state;
uint64_t primary_key() const { return key.value; }
uint64_t get_secondary_1() const { return age;}
};
typedef eosio::multi_index<"people"_n, person, indexed_by<"byage"_n, const_mem_fun<person, uint64_t, &person::get_secondary_1>>> address_index;
};
EOSIO_DISPATCH( addressbook, (upsert)(erase) )