Composition Secondary Index
Instructions to define a secondary index for a indexed composition.
Complete the following steps to define a secondary index for the indexed composition container
.
Extend The Composition Data Structure
Add a second data member secondary
, of type inery::name
, to the data_structure
data structure that defines the container
data.
struct [[inery::table]] data_structure {
name owner; // primary index
name secondary; // secondary index
// additional data stored in table row
uint64_t data;
// mandatory definition for primary key getter
uint64_t primary_key( ) const { return owner.value; }
};
Add The Secondary Index Getter
Add by_secondary()
method, which is the index accessor method to the new data member added. The secondary index, that will be added in the next step, will index this new data structure data member.
struct [[inery::table]] data_structure {
name owner;
name secondary;
uint64_t data;
uint64_t primary_key( ) const { return owner.value; }
// definition for the secondary key getter
uint64_t by_secondary( ) const { return secondary.value; }
};
Define The Secondary Index
In the data_structure_t
type definition, add the definition of the secondary index with the use of the inery::indexed_by
template. inery::index_by
needs two parameters: the name of the index, "secid"_n
, and a function call operator which extracts the value from the secondary data member as an index key. The function call operator is defined with the use of inery::const_mem_fun
template which receives two parameters: the data structure data_structure
and the reference to the getter function member by_secondary
.
typedef inery::multi_index<"container"_n, data_structure, inery::indexed_by<"secid"_n, inery::const_mem_fun<data_structure, uint64_t, &data_structure::by_secondary>>> data_structure_t;
For reference see below the full contract definition code with all the changes described above.
#include <inery/inery.hpp>
using namespace inery;
// Composition example contract class
class [[inery::contract]] database : public contract {
public:
using contract::contract;
// contract class constructor
database( name receiver, name code, datastream<const char*> ds ) :
// contract base class contructor
contract(receiver, code, ds),
// instantiate Composition instance as data member (find it defined below)
container(receiver, receiver.value)
{ }
struct [[inery::table]] data_structure {
// this data member stores a name for each row of the indexed composition
name owner;
name secondary;
// additional data stored in table row
uint64_t data;
// mandatory definition for primary key getter
uint64_t primary_key( ) const { return owner.value; }
uint64_t by_secondary( ) const { return secondary.value; }
};
// the Composition type definition, for ease of use a type alias `data_structure_t` is defined,
// 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<"container"_n, data_structure, inery::indexed_by<"secid"_n, inery::const_mem_fun<data_structure, uint64_t, &data_structure::by_secondary>>> data_structure_t;
// the indexed composition instance declared as a data member of type data_structure_t
data_structure_t container;
[[inery::action]] void set( name user );
[[inery::action]] void print( name user );
};
Now you have instantiated the container
as a indexed composition which has a primary index defined for its owner
data member and a secondary index for its secondary
data member.
Retreive data based on secondary index
Iterate and retrieve data from a composition based on a secondary index defined in the indexed composition.
Complete the following steps to iterate, retrieve and print data from the container
indexed composition using the secondary index.
Define The bysecond() Action
Add an action to the definition of the indexed composition which takes as parameter an account name. This action will retrieve the user object stored in the Composition based on the passed in account name parameter. The action will use the secondary index.
[[inery::action]] void bysecond( name secid );
Implement The bysecond() Action
Search for the user
name in the indexed composition using the secondary index. If found, print out the value of field data
. Otherwise raise and error with a custom message. In the contract definition add the following implementation for print
action:
// iterates the indexed composition rows using the secondary index and prints the row's values
[[inery::action]] void database::bysecond( name secid ) {
// access the secondary index
auto idx = container.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->owner, itr->secondary, itr->data);
}
}
Put It All Together
The full definition and implementation files for the contract should look like this
- database.hpp
- database.cpp
#include <inery/inery.hpp>
using namespace inery;
// Composition example contract class
class [[inery::contract]] database : public contract {
public:
using contract::contract;
// contract class constructor
database( name receiver, name code, datastream<const char*> ds ) :
// contract base class contructor
contract(receiver, code, ds),
// instantiate Composition instance as data member (find it defined below)
container(receiver, receiver.value)
{ }
// the row structure of the indexed composition, 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 indexed composition
name owner;
name secondary;
// additional data stored in table row
uint64_t data;
// mandatory definition for primary key getter
uint64_t primary_key( ) const { return owner.value; }
uint64_t by_secondary( ) const { return secondary.value; }
};
// the Composition 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<"container"_n, data_structure, inery::indexed_by<"secid"_n, inery::const_mem_fun<data_structure, uint64_t, &data_structure::by_secondary>>> data_structure_t;
// the indexed composition instance declared as a data member of type data_structure_t
data_structure_t container;
[[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, &database::set>;
using print_action = action_wrapper<"print"_n, &database::print>;
using bysec_action = action_wrapper<"bysecond"_n, &database::bysecond>;
};
#include <database.hpp>
[[inery::action]] void database::set( name user ) {
// check if the user already exists
auto itr = container.find(user.value);
if ( itr == container.end() ) {
// user is not found in table, use emplace to insert a new row data structure in table
container.emplace( _self, [&]( auto& u ) {
u.owner = user;
u.secondary = "second"_n;
u.data = 0;
});
}
}
[[inery::action]] void database::print( name user ) {
// searches for the row that corresponds to the user parameter
auto itr = container.find(user.value);
// asserts if the row was found for user parameter, if fails use the given message
check( itr != container.end(), "user does not exist in table" );
// prints the owner and data fields stored for user parameter
inery::print_f("Test Table : {%, %}\n", itr->owner, itr->data);
}
// iterates the indexed composition rows using the secondary index and prints the row's values
[[inery::action]] void database::bysecond( name secid ) {
// access the secondary index
auto idx = container.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->owner, itr->secondary, itr->data);
}
}
To compile, deploy and test your contract follow Compile and Deploy steps.