2.5 第二索引

优质
小牛编辑
133浏览
2023-12-01

EOSIO有能力存储最多16个索引.在这一章节,我们将会添加另一个索引到addressbook合约中, 以让我们以另一种方式来迭代记录.

Step 1: 移除表中已经存在的数据

前面提到过, 当一个表有数据时,它的strcut不能被修改. 首先,让我们先将添加过的数据都移除掉.

移除alice和bob的记录:

cleos push action addressbook erase '["alice"]' -p alice@active
cleos push action addressbook erase '["bob"]' -p bob@active

Step 2: 添加新的索引成员以及getter

首先让我们添加新的成员变量以及它的getter到addressbook.cpp合约.

因为第二索引需要是数字类型的字段,所以我们选择使用uint64_t.

uint64_t age;
uint64_t get_secondary_1() const { return age;}

第二索引也允许其他类型,我们会在另外的教程中提到.

Step 3: 添加第二索引到address表配置

我们现在将要有一个字段成为第二索引, 我们需要重新配置 address_index table.

typedef eosio::multi_index<"people"_n, person, 
indexed_by<"byage"_n, const_mem_fun<person, uint64_t, &person::get_secondary_1>>
  > address_index;

第三个参数,我们传入了index_bystruct, 它会被用来实例化索引.

index_by struct中, 我们指定了索引的名字为byage,作为函数调用操作符的第二个类型参数应该提取一个const值作为索引键.现在,我们将先前创建好的get_secondary_1指定给它,现在这个multiple index table会根据age变量来索引记录了.

indexed_by<"byage"_n, const_mem_fun<person, uint64_t, &person::get_secondary_1>>

Step 4: 修改upsert函数

因为我们在multi_index_table中声明了age字段,现在需要去upsert函数中增加该字段,以让其可以存到table中.

修改完multi_index_table和upsert函数后的addressbook.cpp如下所示:

#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;
        std::string street;
        std::string city;
        std::string state;
        uint64_t age;

        uint64_t get_secondary_1() const { return age; }

        uint64_t primary_key() const { return key.value; }
    };

    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)
)

Step 5: 编译与部署

编译:

eosio-cpp -o addressbook.wasm addressbook.cpp --abigen

部署:

cleos set contract addressbook /Users/zhong/coding/CLion/contracts/addressbook

Step 6: 测试它

插入记录

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

现在通过age索引来查询alice的地址.我们使用--index 2参数来指定我们将使用哪个索引,使用--upper 10来指定查询的上限值是多少.

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的地址.我们增加了--lower 10来指定查询索引的最小值应该为10.

cleos get table addressbook addressbook people --upper 50 --lower 10 \
--key-type i64 --index 2

你应该可以得到以下返回:

{
  "rows": [{
      "key": "bob",
      "first_name": "bob",
      "last_name": "is a guy",
      "street": "doesnt exist",
      "city": "somewhere",
      "state": "someplace",
      "age": 49
    }
  ],
  "more": false
}