Skip to main content

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.

database.hpp
#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:

database.cpp
// 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
#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>;
};
Next steps

To compile, deploy and test your contract follow Compile and Deploy steps.