🔬
InfraBlockchain-Antelope
  • Introduction To InfraBlockchain-Antelope
    • What is InfraBlockchain-Antelope?
    • Resources and Essentials
    • Products and Supports
  • Users
    • Accounts and Wallets
  • Block Producers
    • Getting started with InfraBlockchain-Antelope Block Producers
      • What is InfraBlockchain-Antelope Block Producer node
      • Types of nodes
      • InfraBlockchain-Antelope Block Producer Requirements
      • Setting up InfraBlockchain-Antelope Block Producer node
    • InfraBlockchain-Antelope Network Resources
  • For Developers
    • Getting started for developers
    • Overview
      • Core Concepts
      • Technical Features
      • Protocol
        • Consensus Protocol
        • Transactions Protocol
        • Network Peer Protocol
        • Accounts & Permissions
    • Developer guides: InfraBlockchain-Antelope Native
      • Platform & Toolchain
      • Development Environment InfraBlockchain-Antelope Native
        • Try InfraBlockchain-Antelope
        • Prerequisites
        • Before You Begin
        • Install the Contract Dev Toolkit
        • Create Development Wallet
        • Start infra-keychain and infra-node
        • Create Test Accounts
      • Smart Contract Development
        • Hello World Contract
        • Deploy, Issue & Transfer Tokens
        • Understanding ABI Files
        • Data Persistence
        • Secondary Indices
        • Adding Inline Actions
        • Inline Actions to External Contracts
        • Creating & Linking Custom Permissions
        • Payable Actions
      • InfraBlockchain-Antelope System Contracts
        • infra.system
          • authproducer
          • addsystoken
          • rmvsystoken
          • settxfee
          • unsettxfee
        • sys.tokenabi
          • settokenmeta
          • issue
          • transfer
          • txfee
          • retire
      • Tutorials
        • infra-cli
        • infrablockchain-js
        • Hello World Contract
    • Developer guides: InfraBlockchain EVM
Powered by GitBook
On this page
  • Step 1: Remove existing data from table
  • Step 2: Add new index member and getter
  • Step 3: Add secondary index to addresses table configuration
  • Step 4: Modify code
  • Step 5: Compile and Deploy
  • Step 6: Test it
  • Wrapping Up
  • What's Next?
  1. For Developers
  2. Developer guides: InfraBlockchain-Antelope Native
  3. Smart Contract Development

Secondary Indices

Antelope has the ability to sort tables by up to 16 indices. In the following section, we're going to add another index to the addressbook contract, so we can iterate through the records in a different way.

Step 1: Remove existing data from table

As mentioned earlier, a table's struct cannot be modified when it contains data. This first step allows the removal of the data already added.

Remove all records of alice and bob that were added in previous tutorial.

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

Step 2: Add new index member and getter

Add a new member variable and its getter to the addressbook.cpp contract. Since the secondary index needs to be numeric field, a uint64_t age variable is added.

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

Step 3: Add secondary index to addresses table configuration

A field has been defined as the secondary index, next the address_index table needs to be reconfigured.

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

In the third parameter, we pass a index_by struct which is used to instantiate a index.

In that index_by struct, we specify the name of index as "byage" and the second type parameter as a function call operator which extracts a const value as an index key. In this case, we point it to the getter we created earlier so this multiple index table will index records by the age variable.

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

Step 4: Modify code

With all the changes in previous steps, we can now update the upsert function. Change the function parameter list to the following:

void upsert(name user, std::string first_name, std::string last_name, uint64_t age, std::string street, std::string city, std::string state)

Add additional lines to update age field in upsert function as the following:

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( get_first_receiver(), get_first_receiver().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;
      // -- Add code below --
      row.age = age;
      row.street = street;
      row.city = city;
      row.state = state;
    });
  }
  else {
    addresses.modify(iterator, user, [&]( auto& row ) {
      row.key = user;
      row.first_name = first_name;
      row.last_name = last_name;
      // -- Add code below --
      row.age = age;
      row.street = street;
      row.city = city;
      row.state = state;
    });
  }
}

Step 5: Compile and Deploy

Compile

infrablockchain-cpp --abigen addressbook.cpp -o addressbook.wasm

Deploy

infra-cli set contract addressbook CONTRACTS_DIR/addressbook

Step 6: Test it

Insert records

infra-cli push action addressbook upsert '["alice", "alice", "liddell", 9, "123 drink me way", "wonderland", "amsterdam"]' -p alice@active
infra-cli push action addressbook upsert '["bob", "bob", "is a guy", 49, "doesnt exist", "somewhere", "someplace"]' -p bob@active

Look up alice's address by the age index. Here the --index 2 parameter is used to indicate that the query applies to the secondary index

infra-cli get table addressbook addressbook people --upper 10 \
--key-type i64 \
--index 2

You should see something like the following

{
  "rows": [{
      "key": "alice",
      "first_name": "alice",
      "last_name": "liddell",
      "age": 9,
      "street": "123 drink me way",
      "city": "wonderland",
      "state": "amsterdam"
    }
  ],
  "more": false,
  "next_key": ""
}

Look it up by Bob's age

infra-cli get table addressbook addressbook people --upper 50 --key-type i64 --index 2

It should return

{
  "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
}

Wrapping Up

The complete addressbook contract up to this point:

#include <eosio/eosio.hpp>
#include <eosio/print.hpp>

using namespace eosio;

class [[eosio::contract("addressbook")]] addressbook : public eosio::contract {

public:

  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(get_first_receiver(),get_first_receiver().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 {
      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(get_self(), get_first_receiver().value);

    auto iterator = addresses.find(user.value);
    check(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;

};

What's Next?

PreviousData PersistenceNextAdding Inline Actions

Last updated 1 year ago

: Learn how to construct actions and send those actions from within a contract.

Adding Inline Actions