Advanced EOS Series — Part 3 — Secondary Indexes

Mitch Pierias
Coinmonks
4 min readDec 17, 2018

--

Welcome back to the Advanced EOS development series, here I’ll be touching on advanced techniques and functionality which is rarely covered by tutorials or courses. The purpose of this series is to bring together the missing pieces you’ll need to complete your skills as a distributed application developer on the EOS network. Each post is ordered by difficulty, so if you’d like a general overview I’d recommend starting with Part 1 and working your way up. The full code for these examples can be found here on GitHub.

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 namedbyowner.

  • 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.
  • 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.

And that’s secondary indexes, now we have our index, we can perform all the functions we would normally do with the primary_index. In the example we’re using lower_bound, but you could use find, get, upper_bound, begin, or end to suit your particular use case.

Get Best Software Deals Directly In Your Inbox

Click to read today’s top story

--

--