Advanced EOS Series — Part 3 — Secondary Indexes
By: Mitch Pierias
Tags: Blockchain Development, Tutorial, Dapps, Programming, Eos

As these are advanced or extended topics, I’m dangerously assuming you already know the basics and are looking to further your knowledge. For that reason, the code shared in these articles will be concise only to the purpose being discussed.

Primary Indexes

By now you should know how to define a multi-index table in EOS using a struct, but for the sake of completeness let’s briefly glance over our basic table definition.

Defining the struct

we’re going to start by defining our struct and it’s required primary_key function which is the index used when we call find(KEY) . For now let’s use the users account_name for uniqueness and cover a more versatile method in the following chapter.

// @abi table items i64

struct Item {

account_name id;

string name;

uint64_t attack;

account_name owner;



auto primary_key() const { return id; };

EOSLIB_SERIALIZE(Item, (id)(name)(attack)(owner));

};

Creating the Table

Now we’ve outlined our structure. we can define our multi-index table using the multi_index<TABLE_NAME, TABLE_STRUCT> method from the EOS library;

typedef multi_index<N(items), Item> items_table;

After our typedef , we can simply define a reference and then initialize our table in our constructor, or we can initialize a reference within each function as we need it.

Secondary Indexes

Let’s expand the functionality of our table by adding a secondary index to fetch items by their respective owner. Our Item struct will now look like;

// @abi table items i64

struct Item {

auto id;

string name;

uint64_t attack;

account_name owner;



auto primary_key() const { return id; };

uint64_t get_owner() const { return owner; };

EOSLIB_SERIALIZE(Item, (id)(name)(attack)(owner));

};

I’ve added the line unit64_t get_owner() const { return owner; }; below our primary_key index. This function simply returns the value for owner when called.

Defining the Secondary Index

Let’s define this as a secondary index in our multi-index table so we can access it later;

typedef multi_index<N(items), Item, indexed_by<N(byowner), const_mem_fun<Item, uint64_t, &Item::get_owner>>> item_table;

Don’t let that line scare you, it’s just our previous declaration with one additional argument.

indexed_by<N(byowner), const_mem_fun<Item, uint64_t, &Item::get_owner>>

This is the line which defines our secondary index. We’re using the indexed_by<INDEX_NAME, LOOKUP_FUNCTION_DEFINITION> to specify the index named byowner .

INDEX_NAME; can be whatever you like and doesn’t need to syntactically match our function get_owner , just remember it’s the name we will use to access the secondary index later in our actions.

, just remember it’s the name we will use to access the secondary index later in our actions. LOOKUP_FUNCTION_DEFINITION: which looks like cons_mem_fun<STRUCT, RETURN_VALUE, LOOKUP_FUNCTION> in our example, constructs our secondary index function and assigns it to the function of our index definition.

Using the Secondary Index

So we’ve indexed our item’s owner , but how can we use our index to find items owned by a particular user? For this we will be stepping into an action and using the multi-index table method get_index<INDEX_NAME>() , let’s create an action called inventory to fetch a users items.

// @abi action

void inventory(const account_name account) {

item_table items(_self, _self);

auto playerItems = items.get_index<N(byowner)>();

auto iter = playerItems.lower_bound(account);

while (iter != playerItems.end()) {

print("Item ", iter->name);

iter++;

}

}

Let’s break out the following line and delve a little deeper into what it’s responsible for;

auto playerItems = items.get_index<N(byowner)>();

This line is using the get_index function of our item_table instance to fetch the index we defined previously. Notice we’re using the same name N(byowner) we specified earlier to reference the index we’re seeking.