eosio的multi_index

概述

multi_index是eosio上的数据库管理接口,通过eosio::multi_index智能合约能够写入、读取和修改eosio数据库的数据

multi_index在eosio中的位置:eos/contracts/eosiolib/multi_index.hpp

eosio::multi_index来源于boost库的boost::multi_index

eosio::multi_index在概念上和传统数据库的“表(table)”类似,数据“行(rows)”是独立的对象(通常是class对象或struct对象),数据“列(columns)”是对象的成员属性(class或struct的成员属性)

eosio::multi_index提供和传统数据库的“键(key)”类似的成员属性,用于快速查找对象

eosio::multi_index支持主键(primary key),但必须是唯一的无符号64位整型(uint64_t)

eosio::multi_index按主键排序时,使用升序

eosio::multi_index允许使用自定义函数作为索引,但它的返回值是受限制的,只能是支持的键类型

multi_index表允许多索引排序,最多可以使用16个二级索引

二级索引作为multi_index表构造函数的一部分创建,不支持直接构建

multi_index迭代器可以双向迭代,即const_iterator或const_reverse_iterator

创建multi_index表

使用C++类(class)或结构体(struct)定义对象

在class或struct中,定义一个const成员函数:primary_key(),返回uint64_t类型的主键值

确定二级索引(最多支持16个),二级索引不局限于uint64_t,它支持更多类型

二级索引支持的键类型:

idx64:64位无符号整型键

idx128:128位无符号整型键

idx256:256位固定大小字典键

idx_double:双精度浮点键

idx_long_double:四倍精度浮点键

1

2

3

4

5

6

为每个二级索引定义extractor,即一个函数,用于从Multi-Index表的函数中获取键,这个函数会被indexed_by(后面会讲)用到

一个完整的multi_index表定义如下:

//定义address表,i64表示索引使用默认的uint64_t类型

//@abi table address i64

struct address {

uint64_t account;

string name;

uint64_t phone;

uint64_t liked;

//定义address表的主键
uint64_t primary_key() const { return account; }
//定义extractor,二级索引是phone
uint64_t get_phone() const {return phone; } //序列化
EOSLIB_SERIALIZE(address, (account)(name)(phone)(liked))

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

使用multi_index表

(一).实例化

// 第一个参数是表名(即address),第二个参数是表对象的类型(address),其余为可变参数Indices(二级索引),数量不能超过16个

typedef eosio::multi_index< N(address), address, indexed_by<N(phone), const_mem_fun<address, uint64_t, &address::get_phone>>> address_index;

// 构造函数,有两个参数uint64_t类型参数:code, scope

// code: 拥有这张multi_index表的账户,该账户拥有对合约数据的读写权限

// scope: code层级内的范围标识符

address_index addresses(_self, _self);

1

2

3

4

5

6

7

(二).表的操作

  1. emplace

    添加一个新对象(row)到表中

    const_iterator emplace( unit64_t payer, Lambda&& constructor )

    参数

    payer:为新对象使用的存储付费的账户

    constructor:lambda函数,可以让新创建的对象就地初始化

    返回值

    返回一个新创建的对象的主键迭代器

    前置条件

    payer是被当前Action授权的有效账户,允许为使用存储付费

    操作结果

    带有唯一主键的新对象在multi-index表中被创建;

    这个对象会被序列化,然后写入表中;

    如果表不存在,则创建表。

    二级索引被更新,用以引用新添加的对象;

    如果二级索引表不存在,则创建它们。

    payer为创建新对象所使用的存储付费;

    如果multi-index表和二级索引表需要被创建,则payer为表的创建付费。

    异常

    当前接收者(multi_index的code参数)不是表的拥有者时,抛出异常
  2. erase

    使用主键从表中删除现有对象(两种形式)

    const_iterator erase( const_iterator itr )

    void erase( const object_type& obj )

    参数

    itr:指向待删除对象的迭代器

    obj:待删除对象的引用

    返回值

    使用itr查找对象时,返回被删除对象之后的对象的指针

    操作结果

    对象从表中删除,相关的存储被回收;

    表相关的二级索引被更新;

    退还被删除对象的payer所支付的存储费用和相关费用。

    异常

    待删除对象不存在时、Action无权修改表数据时、给定迭代器无效时,抛出异常
  3. modify

    修改表中已存在的对象(两种形式)

    void modify( const_iterator itr, uint64_t payer, Lambda&& updater )

    void modify( const object_type &obj, uint64_t payer, Lambda&& updater )

    参数

    itr:指向待更新对象的迭代器

    obj:待更新对象的引用

    payer:为更新数据付费的账户,为0表示更新数据的payer和创建时的payer相同

    updater:用于更新目标对象的lambda函数

    前置条件

    itr指向的对象,或obj引用的对象是存在的

    payer是被当前Action授权的有效账户,允许为使用存储付费

    操作结果

    更新后的对象被序列化,然后替换表中的现有对象;

    二级索引被更新,被更新对象的主键不变。

    payer为更新对象所使用的存储付费;

    如果payer和该对象现有的payer相同,只需要为现有对象和更新对象不同的部分付费,如果差额为负,还会退还费用;

    如果payer和该对象现有的payer不同,则会退还费用给现有的payer。

    异常

    无效的前提条件下调用,会抛出异常,并中止执行

    当前接收者(multi_index的code参数)不是表的拥有者时,抛出异常
  4. get

    使用主键从表中查询已存在的对象

    const object_type& get( uint64_t primary ) const

    参数

    primary:要查询对象的主键值

    返回值

    包含指定主键的对象的常量引用

    异常

    没有任何对象与给定的主键匹配时,抛出异常
  5. find

    使用主键从表中查询已存在的对象

    const_iterator find( uint64_t primary ) const

    参数

    primary:要查询对象的主键值

    返回值

    返回一个查询到的对象的迭代器

    如果没有查询到指定对象,返回一个end迭代器

    (三).成员访问

    获取拥有主表的账户名

    uint64_t get_code() const

在code下的范围id(scope id),在该范围内可以找到期望的主表实例

uint64_t get_scope() const

(四). 不支持的C++特性

eosio::multi_index不支持拷贝构造函数(Copy constructor)

eosio::multi_index不支持赋值运算符(Assignment operator)

(五).迭代器

multi_index迭代器遵循C++迭代器模式,所有迭代器都是双向迭代,即const_iterator或const_reverse_iterator。

迭代器可以被间接引用,以提供对multi_index表中对象的访问。

  1. begin & cbegin

    返回指向对象类型的、从最小主键值开始的迭代器

    const_iterator begin() const

    const_iterator cbegin() const

end & cend

返回指向虚拟行的迭代器,代表刚刚过去的最后一行,不能被间接引用;

可以向后推进,不能向前推进。

const_iterator end() const

const_iterator end() const

rbegin & crbegin

返回和begin/cbegin类似的,但反向的迭代器

const_iterator rbegin() const

const_iterator crbegin() const

rend & crend

返回和end/cend类似的,但反向的迭代器

const_iterator rend() const

const_iterator crend() const

lower_bound

查找大于等于给定主键值的对象

const_iterator lower_bound( uint64_t primary ) const

upper_bound

查找大于给定主键值的对象

const_iterator upper_bound( uint64_t primary ) const

get_index

返回一个适当类型的二级索引

secondary_index get_index()

secondary_index get_index() const

iterator_to

返回给定对象的迭代器

const_iterator iterator_to( const object_type& obj ) const

indexed_by

indexed_by结构体用于实例化multi_index表的索引

indexed_by在multi_index.hpp中的定义如下:

// 参数IndexName是索引的名称,这个名称是base32编码后的64位整数,且需要符合EOS命名规范:

// 1. 最多13个字符,前12个字符只能是小写字母、0-5、“.”

// 2. 如果有第13个字符,则只能是小写字母a-p、“.”

// 参数Extractor是一个函数,用于从multi_index表的函数中获取键,返回一个二级索引类型或二级索引类型的引用

template<uint64_t IndexName, typename Extractor>

struct indexed_by {

enum constants { index_name = IndexName };

typedef Extractor secondary_extractor_type;

};

// 推荐使用eosio::const_mem_fun模板,它是boost::multi_index::const_mem_fun的类型别名,例子:

// multi_index表的名字是N(address), N是一个宏,可以把base32编码后的字符串转换为uint64_t

// multi_index表的对象类型是address

// multi_index通过名为N(phone)的二级索引进行检索

// const_mem_fun是一个用于address类型的键提取器(key extractor )

// const_mem_fun提取的键类型是uint64_t,通过address::get_phone函数获取键

eosio::multi_index< N(address), address, indexed_by<N(phone), const_mem_fun<address, uint64_t, &address::get_phone>>>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

完整例子

//@abi action

void likebyphone(uint64_t phone) {

address_index addresses(_self, _self);

auto phone_index = addresses.get_index<N(phone)>();
auto itr = phone_index.lower_bound(phone);
for(; itr != phone_index.end() && itr->phone == phone; ++itr) {
phone_index.modify(itr, 0, [&](auto& address){
eosio::print("Liking: ", address.name.c_str(), "\n");
address.liked++;
});
}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

(六). 工具函数

available_primary_key

返回一个可用(未使用)的主键值,用于主键严格自增的表,它不会被设置为自定义值

uint64_t available_primary_key() const

eos对数据库的操作的更多相关文章

  1. php模拟数据库常用操作效果

    test.php <?php header("Content-type:text/html;charset='utf8'"); error_reporting(E_ALL); ...

  2. Android-Sqlite数据库的操作

    Sqlite数据库的简单操作: 设置增删改查的按钮,xml界面布局设置 <?xml version="1.0" encoding="utf-8"?> ...

  3. (四)SQL入门 数据库的操作与事务管理

    数据库的操作,有三个最基本的语句,insert插入,update修改,delete删除. 不同的数据库厂商的实现可能不同,所以就不说具体的语法怎么写的了.说语法也没有意义,到处都可以复制粘贴,记得听某 ...

  4. Laravel框架数据库CURD操作、连贯操作使用方法

    Laravel框架数据库CURD操作.连贯如何来操作了这个操作性是非常的方便简单了我们在这里来为各位介绍一篇相关的教程,具体的细节步骤如下文介绍.   Laravel是一套简洁.优雅的PHP Web开 ...

  5. zabbix数据库mariadb从服务器迁移到云mysql数据库的操作

    zabbix数据库mariadb从本机迁移到云mysql数据库的操作 1.将zabbix数据库导出,并导入到云数据库中 由于数据库较大,如果直接使用shell会话中断会导致数据库导出或者导入失败,使用 ...

  6. 使用my exclipse对数据库进行操作(4)

    四.删除 public class class4 { public static void main(String[] args) { // TODO Auto-generated method st ...

  7. 从C#到Objective-C,循序渐进学习苹果开发(7)--使用FMDB对Sqlite数据库进行操作

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本篇主要开始介绍基于XCod ...

  8. MYSQL数据库的操作

    Mysql的连接方式: 1.原生函数:mysql_connect($server,$username,$password);   //打开一个到Mysql服务器的连接 mysql_select_db( ...

  9. DBA必备:MySQL数据库常用操作和技巧

    DBA必备:MySQL数据库常用操作和技巧 2011-02-25 15:31 kaduo it168 字号:T | T MySQL数据库可以说是DBA们最常见和常用的数据库之一,为了方便大家使用,老M ...

随机推荐

  1. Autolayout中Hugging和Compression使用注意

    前言 本文主要侧重Autolayout使用过程中,通过代码和SB添加含有intrinsicSize属性控件约束的一些细节. 来自我的博客,欢迎访问:To Be Independent. Hugging ...

  2. linux静态IP最简配置

    vi /etc/sysconfig/network-scripts/ifcfg-xxx TYPE="Ethernet"BOOTPROTO="static"ONB ...

  3. IIS配置导入导出

    使用管理员身份运行cmd 应用程序池: # 导出所有应用程序池 %windir%\system32\inetsrv\appcmd list apppool /config /xml > c:\a ...

  4. Nginx与Tomcat实现请求动态数据与请求静态资源的分离

    上篇博客说明了Nginx在应用架构中的作用,以及负载均衡的思路.这篇实践一下其中的访问静态资源与访问动态资源的操作. 一.认识访问静态资源与访问动态资源的区别 静态资源:指存储在硬盘内的数据,固定的数 ...

  5. 【python 虚拟环境 virtualenv的配置】

    该目录内生成一个虚目录: #运行activcate下的shell脚本,激活虚拟环境 #pip  python包管理器

  6. linux的用户管理相关配置文件

    Linux的管理命令的本质不过是对配置文件/etc相关文件的修改罢了 

  7. [译]C语言实现一个简易的Hash table(2)

    上一章,简单介绍了Hash Table,并提出了本教程中要实现的几个Hash Table的方法,有search(a, k).insert(a, k, v)和delete(a, k),本章将介绍Hash ...

  8. CentOS6编译安装gcc高版本

    编译安装gcc高版本 因CentOS中gcc版本仅有4.4,故编译安装gcc高版本. 安装依赖库(如果你已安装过gcc低版本,可跳过这步) yum install glibc-static libst ...

  9. Python学习 :面向对象 -- 成员修饰符

    成员修饰符 两种成员 - 公有成员 - 私有成员, __字段名 - 无法直接访问,只能通过内部方法来间接访问私有成员 简例:公有成员与私有成员  class Info: country = '中国' ...

  10. C语言链队列

    链队列类似于单链表,为了限制只能从两端操作数据,其结构体内有2个指针分别指向头尾,但队列里的节点用另一种结构体来表示,头尾指针则为指向该结构体的类型.只能通过操作头尾指针来操作队列. typedef ...