EOS入门指南PART7——如何操作multi_index

上一章我们学习了对智能合约开发来说至关重要的第一步:

  • 知道了RAM、multi_index和EOS数据库各是什么以及它们之间的关系;
  • 知道了multi_index是内存数据库的入口;
  • 了解了multi_index内部的结构长什么样子;

今天这章,主要介绍multi_index的相关操作,趁机巩固一波上一章学习到的理论知识。

摘要

上一章说到multi_index的概念——多索引容器,multi_index容器的内部结构可以类比成传统数据库里的一张表,只不过这张表只有一列,每一行都是一个struct结构体。

这一章就要向大家介绍,如何具体使用multi_index。

primary keys

还是上一章中的例子:

eos_iterator

multi_index的容器(表)里每一行都是上面的struct结构体,并且都是按照主键升序排列。所以每一个multi_index中会有一个默认索引,也就是主键。如上图,就是按照主键(pkey)升序排列的。

uint64_t primary_key() const { return pkey; }

一般而言,在struct结构体中会声明一行获得主键的方法primary_key(),这里定义了主键类型为uint64_t,返回结构体中的pkey字段,即pkey这个字段就是整个结构体的主键。

自定义索引

既然叫multi_index,肯定不只可以根据主键进行索引,事实上,我们还可以自定义索引。例如可以在结构体中定义如下方法:

account_name get_customer() const {
    return customer;
}

这就是我们自定义的索引。仿照之前的primary_key()可以看到,这个方法返回值的类型为account_name,返回的字段为customer。当然自定义的索引并不一定要返回结构体中的某个字段的变量,也可以将变量之间的运算结果作为索引。

这里先贴上结构体的完整表达,方便下面介绍如何使用多索引:

struct service_struct {
     uint64_t pkey;
     account_name customer;
     Date date;
     uint32_t odometer;
    
    auto primary_key() const {return pkey;}
    account_name get_customer() const {return customer;}
    
    EOSLIB_SERIALIZE(service_struct,( pkey )( customer )( date )( odometer ))
}

typedef eosio::multi_index<service, service_struct,
    index_by<N(bycustomer), const_mem_fun<service_struct,account_name, &service_struct::get_customer>
    >
> customer_index;

解释一下,使用上面的方式来定义索引,eosio::multi_index<...>的参数解释如下:

  • service : multi_index容器的表名(如上图)
  • service_struct : 智能合约重定义的struct结构体名称,也可以理解成表中的一行记录;
  • indexed_by<...>
    • N(bycustomer) : 给索引起个名字 - bycustomer
    • const_mem_fun<...> :
      • account_name : 索引的类型
      • &service_struct::get_customer:通过service_struct结构体中的get_customer函数获得(索引)

通过上述表达,就可以再生成一张按customer为索引的表bycustomer(见本文第一张图)。左边是按照主键索引的原表service,右边的表就是我们按照customer的索引生成的新表(按customer从小到大进行排列),可以看到新表内的行内容和之前是一行的(见虚线指向原表),只不过按照customer字段进行了重新排列。

这里定义了customer_index类型,这样以后就可以直接用这个类型去初始化真正的multi_index表。

迭代器

上一章我们介绍过迭代器,可以把迭代器想象成一个电梯,通过在索引中上下滑动来定位数据。接着上面介绍的自定义索引comstomer_index:

account_name customer_acct = eosio::chain::string_to_name(customer_name);
auto cust_itr = customer_index.find(customer_acct);

通过上述代码,我们就可以找到bycustomer表中,customer为某个特定值的所有行(结构体)了。比如,第一张图中,如果传入的参数为"bob",那就可以找出所有customer为bob的数据了,放入迭代器里。通过迭代器就可以逐条获取我们想要的数据信息。

得到了迭代器之后,咱们再来看看怎么使用迭代器:

while(cust_itr != service.end() && cust_itr -> customer == customer_acct) {
    // code to execute
    cust_itr++;
}

其中cust_itr != service.end()表示迭代器并没有走到service表的结尾,&&之后也很容易看懂,再筛选customer的值。每次小的遍历结束是,迭代器+1,表示继续访问迭代器中的下一行。

索引和迭代器如何配合工作

通过上面的介绍,大家应该就可以理清multi_index和迭代器是如何配合工作的:

i通过multi_index可以得到一张按照特定字段索引的表,再通过对索引设置一些条件,就可以筛选得到一些迭代器,再通过对迭代器的遍历,就可以访问我们想要访问的数据了。

multi_index的初始化

上文关于multi_index多与定义有关,现在终于要开始实例化multi_index了。

multi_index(uint64_t code, uint64_t scope)

在上面的初始化(实例化)的语句中,可以看到有两个参数:

  • code -拥有这张multi_index表的账户,该账户拥有对合约数据的读写权限;
  • scope - 用户账户名下的区域。可以在code下定义多个scope,把属于不同scope的表隔离开;

这里的code和scope,都是用来为表建立访问权限的。

所以要想初始化上面的customer_index,可以使用:

// customer_index 就是之前 typedef 定义的类型
// bycustomer就是新表
// _self就是当前调用方法的账户
customer_index bycustomer(_self, _self);

添加 - emplace

这里顺便说一下find,multi_index使用.find来查询,代码接上往下写:

void demo::create(const uint64_t id,
                  const account_name cust_acct,
                  const Date date,
                  const uint32_t odometer) {
    // 找出特定值的customer的数据
    // 这里的auto相当于其他语言中的var,声明变量时不指定类型
    auto itr = bycustomer.find(cust_acct);
    // 查询customer = cust_acct的记录并要求该记录不存在
    // 因为primary_key不能重复
    // 所以插数据之前先查询一下,保证不重复
    eosio_assert(itr == profile.end(), "Account already exists");
    // 往索引里添加记录
    bycustomer.emplace(cust_acct, [&](auto& c)) {
        c.pkey = id;
        c.customer = cust_acct;
        c.date = date;
        c.odometer = odometer;
    }
}

其中[&](auto& c)是c11之后一种新的lambda表达式,有兴趣的同学可以自行深入了解,这里简单介绍一下,这个表达式大概表示:

除了该函数已有的参数,其他的参数例如 id, date, odometer这些,都是在上层的作用域中通过引用的形式传入函数内。

其中auto我们之前说过,表示不限制类型,但是也为引用。

看懂了添加记录,其他诸如删、改、查的操作也就不难看懂了,大家可以去官网查看相关例子:

https://eosio-cpp.readme.io/docs/using-multi-index-tables

结束语

本章我们学习了:

  • 如何自定义索引;
  • 如何生成以及使用迭代器;
  • 索引和迭代器是如何配合使用的;
  • multi_index的相关操作

希望大家学完之后可以去官网继续补充删、改、查的相关例子,以达到举一反三的效果。下面几篇我们将正式了解EOS智能合约开发的二三事。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容