Расширенная серия EOS - Часть 3 - Вторичные индексы

Добро пожаловать в серию статей о разработке Advanced EOS, здесь я коснусь передовых методов и функций, которые редко рассматриваются в учебных пособиях или курсах. Цель этой серии - собрать воедино недостающие элементы, которые вам понадобятся для завершения своих навыков в качестве разработчика распределенных приложений в сети EOS. Каждый пост отсортирован по сложности, поэтому, если вы хотите получить общий обзор, я бы рекомендовал начать с части 1 и постепенно продвигаться вверх. Полный код этих примеров можно найти здесь, на GitHub.



Поскольку это сложные или расширенные темы, я опасно предполагаю, что вы уже знаете основы и хотите расширить свои знания. По этой причине код, представленный в этих статьях, будет кратким только для обсуждаемой цели.

Первичные индексы

К настоящему времени вы должны знать, как определить многоиндексную таблицу в EOS с помощью структуры, но для полноты картины давайте кратко рассмотрим наше базовое определение таблицы.

Определение структуры

мы собираемся начать с определения нашей struct, и это обязательная primary_key функция, которая является индексом, используемым при вызове find(KEY). А пока давайте воспользуемся пользователями account_name для уникальности и рассмотрим более универсальный метод в следующей главе.

// @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));
};

Создание таблицы

Теперь мы обрисовали нашу структуру. мы можем определить нашу мультииндексную таблицу, используя метод multi_index<TABLE_NAME, TABLE_STRUCT> из библиотеки EOS;

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

После нашего typedef мы можем просто определить ссылку, а затем инициализировать нашу таблицу в нашем конструкторе, или мы можем инициализировать ссылку в каждой функции по мере необходимости.

Вторичные индексы

Давайте расширим функциональность нашей таблицы, добавив вторичный индекс для получения элементов по их владельцу. Наша структура Item теперь будет выглядеть так:

// @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));
};

Я добавил строку unit64_t get_owner() const { return owner; }; под нашим primary_key индексом. Эта функция просто возвращает значение для owner при вызове.

Определение вторичного индекса

Давайте определим его как вторичный индекс в нашей многоиндексной таблице, чтобы мы могли получить к нему доступ позже;

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

Пусть вас не пугает эта строка, это всего лишь наше предыдущее заявление с одним дополнительным аргументом.

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

Это строка, которая определяет наш вторичный индекс. Мы используем indexed_by<INDEX_NAME, LOOKUP_FUNCTION_DEFINITION> для указания индекса с именемbyowner.

  • INDEX_NAME; может быть любым, и не обязательно синтаксически соответствовать нашей функции get_owner, просто помните, что это имя мы будем использовать для доступа к вторичному индексу позже в наших действиях.
  • LOOKUP_FUNCTION_DEFINITION: который в нашем примере выглядит как cons_mem_fun<STRUCT, RETURN_VALUE, LOOKUP_FUNCTION>, создает нашу вторичную индексную функцию и назначает ее функции нашего определения индекса.

Использование вторичного индекса

Итак, мы проиндексировали owner нашего элемента, но как мы можем использовать наш индекс, чтобы найти элементы, принадлежащие определенному пользователю? Для этого мы перейдем к действию и, используя метод многоиндексной таблицы get_index<INDEX_NAME>(), давайте создадим действие под названием inventory для получения пользовательских элементов.

// @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++;
  }
}

Давайте выделим следующую строку и немного углубимся в то, за что она отвечает;

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

Эта строка использует get_index функцию нашего item_table экземпляра для получения индекса, который мы определили ранее. Обратите внимание, что мы используем то же имя N(byowner), которое мы указали ранее, для ссылки на индекс, который мы ищем.

И это вторичные индексы, теперь у нас есть наш индекс, мы можем выполнять все функции, которые обычно выполнялись бы с primary_index. В этом примере мы используем lower_bound, но вы можете использовать find, get, upper_bound, begin или end в соответствии с вашим конкретным вариантом использования.

Получайте лучшие предложения по программному обеспечению прямо в свой почтовый ящик