Skip to main content

Iterate by secondary index

Overview

This guide provides instructions on how iterate and retrieve data from a multi-index table based on a secondary index defined in the multi-index table.

Procedure

Complete the following steps to iterate, retrieve and print data from the datatable multi-index table using the secondary index.

1. Define The bysecond( ) Action

Add an action to the definition of the multi-index table which takes as parameter an account name. This action will retrieve the user object stored in the multi-index based on the passed in account name parameter. The action will use the secondary index.

  [[inery::action]] void bysecond( name secid );

Optionally, for ease of use add the action wrapper definition as well.

  [[inery::action]] void bysecond( name secid );

+using bysec_action = action_wrapper<"bysecond"_n, &multi_index_example::bysecond>;

2. Implement The bysecond( ) Action

Search for the user name in the multi-index table using the secondary index. If found, print out the value of field datum. Otherwise raise and error with a custom message. In the contract definition add the following implementation for print action:

// iterates the multi-index table rows using the secondary index and prints the row's values
[[inery::action]] void multi_index_example::bysecond( name secid ) {
// access the secondary index
auto idx = datatable.get_index<"secid"_n>();
// iterate through secondary index
for ( auto itr = idx.begin(); itr != idx.end(); itr++ ) {
// print each row's values
inery::print_f("Test Table : {%, %, %}\n", itr->test_primary, itr->secondary, itr->datum);
}
}

3. Put It All Together

The full definition and implementation files for the contract should look like this:

multi_index_example.hpp

#include <inery/inery.hpp>
using namespace inery;

// multi-index example contract class
class [[inery::contract]] multi_index_example : public contract {
public:
using contract::contract;

// contract class constructor
multi_index_example( name receiver, name code, datastream<const char*> ds ) :
// contract base class contructor
contract(receiver, code, ds),
// instantiate multi-index instance as data member (find it defined below)
datatable(receiver, receiver.value)
{ }

// the row structure of the multi-index table, that is, each row of the table
// will contain an instance of this type of structure
struct [[inery::table]] data_structure {
// this data member stores a name for each row of the multi-index table
name test_primary;
name secondary;
// additional data stored in table row
uint64_t datum;
// mandatory definition for primary key getter
uint64_t primary_key( ) const { return test_primary.value; }
uint64_t by_secondary( ) const { return secondary.value; }
};

// the multi-index type definition, for ease of use define a type alias `data_structure_t`,
// based on the multi_index template type, parametarized with a random name, the
// data_structure data structure, and the secondary index
typedef inery::multi_index<"datatable"_n, data_structure, inery::indexed_by<"secid"_n, inery::const_mem_fun<data_structure, uint64_t, &data_structure::by_secondary>>> data_structure_t;

// the multi-index table instance declared as a data member of type data_structure_t
data_structure_t datatable;

[[inery::action]] void set( name user );
[[inery::action]] void print( name user );
[[inery::action]] void bysecond( name secid );

using set_action = action_wrapper<"set"_n, &multi_index_example::set>;
using print_action = action_wrapper<"print"_n, &multi_index_example::print>;
using bysec_action = action_wrapper<"bysecond"_n, &multi_index_example::bysecond>;
};

multi_index_example.cpp

#include <multi_index_example.hpp>

[[inery::action]] void multi_index_example::set( name user ) {
// check if the user already exists
auto itr = datatable.find(user.value);

if ( itr == datatable.end() ) {
// user is not found in table, use emplace to insert a new row data structure in table
datatable.emplace( _self, [&]( auto& u ) {
u.test_primary = user;
u.secondary = "second"_n;
u.datum = 0;
});
}
}

[[inery::action]] void multi_index_example::print( name user ) {
// searches for the row that corresponds to the user parameter
auto itr = datatable.find(user.value);

// asserts if the row was found for user parameter, if fails use the given message
check( itr != datatable.end(), "user does not exist in table" );

// prints the test_primary and datum fields stored for user parameter
inery::print_f("Test Table : {%, %}\n", itr->test_primary, itr->datum);
}

// iterates the multi-index table rows using the secondary index and prints the row's values
[[inery::action]] void multi_index_example::bysecond( name secid ) {
// access the secondary index
auto idx = datatable.get_index<"secid"_n>();

// iterate through secondary index
for ( auto itr = idx.begin(); itr != idx.end(); itr++ ) {
// print each row's values
inery::print_f("Test Table : {%, %, %}\n", itr->test_primary, itr->secondary, itr->datum);
}
}

Summary

In conclusion, the above instructions show how to iterate and retrieve a multi-index table based on secondary index.