Multi_index详解

本文深入解析EOS的Multi-Index API,介绍其在智能合约中读取和修改数据库的作用,详细阐述Multi-Index表的定义、使用及操作,包括主键、二级索引、迭代器等关键概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Multi-Index API 简介

1、Multi-Index API 是EOS提供的访问数据库的C++接口,使智能合约能够读取和修改EOS数据库中的数据;
2、Multi-Index源文件位置在:eos/contracts/eosiolib/multi_index.hpp;
3、Multi-Index来源于boost库的boost::multi_index,但又有一些重要不同,eos中使用eosio::multi_index;
4、eosio::multi_index在概念上和传统数据库的“表(table)”类似,数据“行(rows)”是独立的对象,数据“列(columns)”是对          象的成员属性;
5、eosio::multi_index提供和传统数据库的“键(key)”类似的成员属性,用于快速查找对象;
6、eosio::multi_index允许使用自定义函数作为索引,但它的返回值是受限制的,只能是支持的键类型;
7、eosio::multi_index支持主键(primary key),但必须是唯一的无符号64位整型(uint64_t);
8、eosio::multi_index按主键排序时,使用升序;
9、Multi-Index表允许多索引排序,最多可以使用16个二级索引;
10、Multi-Index迭代器可以双向迭代,即const_iterator或const_reverse_iterator;  
11、二级索引作为Multi-Index表构造函数的一部分创建,不支持直接构建。

创建Multi-Index表

1、使用C++类(class)或结构(struct)定义对象;
2、在class或struct中,定义一个const成员函数:primary_key(),返回uint64_t类型的主键值;
3、确定二级索引(最多支持16个),二级索引不局限于uint64_t,它支持更多类型;

二级索引支持的键类型:

idx64:64位无符号整型键
idx128:128位无符号整型键
idx256:256位固定大小字典键
idx_double:双精度浮点键
idx_long_double:四倍精度浮点键

4、为每个二级索引定义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));
};

如何使用Multi-Index表

1、实例化Multi-Index表;

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

2、CRUD函数

  • 增:emplace
  • 删:erase
  • 改:modify
  • 查:get/find

类模板

在multi_index.hpp中,multi_index类的定义使用了类模板:

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

  • 表名(TableName)

multi_index表的名称,最长12个字符,只能由小写字母、数字1-5、“.”组成。

  • T

表对象的类型,即表中“行(row)”的定义。

  • Indices
  1. 最多16个二级索引的list;
  2. 每个二级索引必须是默认可构造的class或struct;
  3. 每个二级索引必须在表结构中定义一个常量函数,返回二级索引类型或其引用;
  4. 建议使用eosio::const_mem_fun模板,它是boost::multi_index::const_mem_fun的一个类型别名。
     

使用模板参数的例子:

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

构造函数

multi_index构造函数有两个参数:code,scope它们都是uint64_t类型,可以用来为表建立访问权限。

  • code

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

  • scope

code层级内的范围标识符(即一行数据)

不支持的C++特性

  • eosio::multi_index不支持拷贝构造函数(Copy constructor)
  • eosio::multi_index不支持赋值运算符(Assignment operator)

表的操作

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迭代器。

成员访问

1、获取拥有主表的账户名

uint64_t get_code() const

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

uint64_t get_scope() const

迭代器

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

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

1、begin & cbegin

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

const_iterator begin() const
const_iterator cbegin() const

2、end & cend

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

const_iterator end() const
const_iterator cend() const

3、rbegin & crbegin

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

const_iterator rbegin() const​​​​​​​
const_iterator crbegin() const

4、rend & crend

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

const_iterator rend() const
 
const_iterator crend() const

5、lower_bound

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

const_iterator lower_bound( uint64_t primary ) const

6、upper_bound

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

const_iterator upper_bound( uint64_t primary ) const

7、get_index

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

secondary_index get_index<IndexName>()  
 
secondary_index get_index<IndexName>() const

8、iterator_to

返回给定对象的迭代器

const_iterator iterator_to( const object_type& obj ) const

9、indexed_by

indexed_by结构体用于实例化Multi-Index表的索引,EOS中,最多可以指定16个二级索引。

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


template<uint64_t IndexName, typename Extractor>
struct indexed_by {
   enum constants { index_name   = IndexName };
   typedef Extractor secondary_extractor_type;
};
  • 参数IndexName是索引的名称,这个名称是base32编码后的64位整数,且需要符合EOS命名规范:

最多13个字符,前12个字符只能是小写字母、0-5、“.”;
如果有第13个字符,则只能是小写字母a-p、“.”。

  • 参数Extractor是一个函数,用于从Multi-Index表的函数中获取键,返回一个二级索引类型或二级索引类型的引用。

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

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

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函数获取键。

10、完整例子:

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

工具函数

available_primary_key

uint64_t available_primary_key() const

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

 

转自:https://blog.youkuaiyun.com/yhc166188/article/details/82724494

### 回答1: Pandas MultiIndex是一种用于在Pandas DataFrame中处理多维数据的工具。它允许您在一个DataFrame中使用多个索引级别,以便更好地组织和分析数据。MultiIndex可以在行和列上使用,使您能够轻松地对数据进行切片、分组和聚合。 MultiIndex的主要特点是它可以将多个索引级别组合在一起,从而形成一个层次结构。每个级别都可以有自己的标签和名称,这使得数据的组织和访问变得更加灵活和方便。 在使用MultiIndex时,您可以使用许多不同的方法来访问和操作数据。例如,您可以使用.loc和.iloc属性来选择特定的行和列,或者使用groupby方法来对数据进行分组和聚合。 总之,Pandas MultiIndex是一个非常强大的工具,可以帮助您更好地组织和分析多维数据。如果您需要处理复杂的数据集,那么MultiIndex是一个非常有用的工具,值得您深入学习和掌握。 ### 回答2: Pandas是一个数据分析常用的工具,MultiIndex是Pandas中处理复杂数据、多级索引的重要功能。在Pandas中,常见的索引方式是单级索引,而MultiIndex则允许我们在一个轴上拥有多个层级的索引。 1.创建MultiIndex索引 可以通过Pandas中的MultiIndex类来创建MultiIndex索引。我们可以将MultiIndex视为一个由元组构成的列表。例如,下面的代码通过二维数组的方式创建了一个双层索引: ```python import pandas as pd import numpy as np arrays = [np.array(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux']), np.array(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'])] tuples = list(zip(*arrays)) index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second']) print(index) ``` 输出结果为: ``` MultiIndex([('bar', 'one'), ('bar', 'two'), ('baz', 'one'), ('baz', 'two'), ('foo', 'one'), ('foo', 'two'), ('qux', 'one'), ('qux', 'two')], names=['first', 'second']) ``` 2.使用MultiIndex索引 有了MultiIndex索引之后,我们就可以使用它来进行数据的选择和提取。 - 多层级索引的创建 通过将MultiIndex作为DataFrame的索引,我们就可以通过多层级索引来访问DataFrame中的数据。例如,我们可以通过下面的语句创建一个包含随机数据的DataFrame,并以MultiIndex作为该DataFrame的索引: ```python import pandas as pd import numpy as np arrays = [np.array(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux']), np.array(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'])] index = pd.MultiIndex.from_arrays(arrays, names=('first', 'second')) df = pd.DataFrame(np.random.randn(8, 4), index=index, columns=['A', 'B', 'C', 'D']) print(df) ``` 输出结果为: ``` A B C D first second bar one -1.214380 -0.526019 0.522447 -0.549709 two -0.219676 -1.266149 -1.050014 -0.380561 baz one -0.714797 1.327005 -0.796460 -0.563436 two -0.301347 -2.073073 -0.780802 0.086017 foo one 0.733098 1.153226 -0.512038 -0.111355 two -1.902571 -1.294116 -0.706356 0.742038 qux one -0.861275 -0.557358 -1.058479 -2.117281 two 1.469990 0.111327 1.023728 2.130335 ``` - 数据选择 使用MultiIndex索引时,我们可以通过指定第一层级的标签名和第二层级的标签名来选取数据。例如,下面的代码选择了第一层级索引为'bar'且第二层级索引为'two'的数据: ```python print(df.loc[('bar', 'two')]) ``` 输出结果为: ``` A -0.219676 B -1.266149 C -1.050014 D -0.380561 Name: (bar, two), dtype: float64 ``` - 列索引 除了行索引之外,MultiIndex还可以作为列索引。例如,下面的代码创建了一个包含MultiIndex作为行索引和列索引的DataFrame: ```python import pandas as pd import numpy as np arrays = [np.array(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux']), np.array(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'])] tuples = list(zip(*arrays)) index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second']) data = np.random.randn(8, 4) df = pd.DataFrame(data, index=index, columns=[('A', 'foo'), ('A', 'bar'), ('B', 'foo'), ('B', 'qux')]) print(df) ``` 输出结果为: ``` A B foo bar foo qux first second bar one -0.577478 0.242338 1.978065 0.125214 two 0.291272 -2.438773 -1.534891 1.702976 baz one 1.212083 1.277529 0.437934 1.474582 two -1.744299 0.191257 1.607997 1.080287 foo one 0.067381 0.205383 1.600982 0.299079 two -0.154448 0.346428 0.775834 -0.538028 qux one -1.232866 0.110620 -0.815084 2.217145 two -1.391950 -1.013497 -0.310378 0.239469 ``` 我们可以通过指定 MultiIndex 索引的方式来访问 DataFrame 中的数据,如下面的代码所示: ```python print(df.loc[('bar', 'two'), ('A', 'bar')]) ``` 此时输出结果为: ``` -2.438773338782212 ``` 3.总结 在Pandas中,MultiIndex是一种非常有用的数据结构,它可以处理复杂的数据结构,具有良好的可扩展性和灵活性。在实际中,我们可以通过 MultiIndex 来进行复杂数据的处理,精确地选取所需的数据,并能够很容易地对数据进行排序、计算和绘图等操作。因此,掌握 MultiIndex 的使用方法,对于进行数据处理和分析的人员来说是非常重要的。 ### 回答3: Pandas是一个高效的数据处理库,它可以方便地处理多维数据。在Pandas中,MultiIndex是一种方便的数据结构,它可以用来处理具有多个层级的索引。 MultiIndex是指Pandas中的一个数据结构,它允许我们在一个轴上具有多个层级的索引。例如,我们可以在一个DataFrame中,同时使用多个属性来作为多个层级的索引。举个例子,假如我们想要处理一个电商平台的销售数据,其中不同的产品可以按照日期、品类、子品类和产品名称进行分类,那么我们就可以使用MultiIndex来进行数据的处理。 多级索引可以通过使用Pandas的IndexMultiIndex对象来创建。从技术上讲,每个层级的MultiIndex都是创建用于管理数据集中某个维度上的标签。MultiIndex可以有两个或两个以上的层级,每个层级有一个标识符,可以是整数或字符串。 要创建一个MultiIndex,我们可以使用Pandas的MultiIndex.from_arrays()、MultiIndex.from_tuples()或MultiIndex.from_product()等方法。以下是一些创建MultiIndex的例子: # 假设有以下数据 data = [['2021-01-01', 'iphone', '6', '64G', 10], ['2021-01-01', 'iphone', '6', '128G', 20], ['2021-01-01', 'iphone', '7', '64G', 30], ['2021-01-01', 'iphone', '7', '128G', 40], ['2021-01-02', 'iphone', '6', '64G', 50], ['2021-01-02', 'iphone', '6', '128G', 60], ['2021-01-02', 'iphone', '7', '64G', 70], ['2021-01-02', 'iphone', '7', '128G', 80]] # 从多个一维数组中创建MultiIndex mi_arrays = pd.MultiIndex.from_arrays([['2021-01-01', '2021-01-01', '2021-01-01', '2021-01-01', '2021-01-02', '2021-01-02', '2021-01-02', '2021-01-02'], ['iphone', 'iphone', 'iphone', 'iphone', 'iphone', 'iphone', 'iphone', 'iphone'], ['6', '6', '7', '7', '6', '6', '7', '7'], ['64G', '128G', '64G', '128G', '64G', '128G', '64G', '128G']], names=['日期', '品牌', '型号', '内存']) # 从元组序列中创建MultiIndex mi_tuples = pd.MultiIndex.from_tuples([('2021-01-01', 'iphone', '6', '64G'), ('2021-01-01', 'iphone', '6', '128G'), ('2021-01-01', 'iphone', '7', '64G'), ('2021-01-01', 'iphone', '7', '128G'), ('2021-01-02', 'iphone', '6', '64G'), ('2021-01-02', 'iphone', '6', '128G'), ('2021-01-02', 'iphone', '7', '64G'), ('2021-01-02', 'iphone', '7', '128G')], names=['日期', '品牌', '型号', '内存']) # 从笛卡尔积中创建MultiIndex mi_product = pd.MultiIndex.from_product([['2021-01-01', '2021-01-02'], ['iphone'], ['6', '7'], ['64G', '128G']], names=['日期', '品牌', '型号', '内存']) 其中,MultiIndex.from_arrays()方法需要传入一个二维数组,每一列都代表一个层级的索引;MultiIndex.from_tuples()需要传入一个元组序列,每个元组代表一个索引坐标;MultiIndex.from_product()需要传入一个可迭代对象序列,每个可迭代对象代表了一个可能的层级所有标签。 在创建MultiIndex之后,我们就可以使用它来对数据进行索引、切片或者分组了。以下是一些基本的操作: # 创建Series对象 s = pd.Series([10, 20, 30, 40, 50, 60, 70, 80], index=mi_tuples) # 使用索引器进行切片 print(s.loc[('2021-01-01', 'iphone', '6', '64G')]) print(s.loc[('2021-01-02', 'iphone', '6')]) print(s.loc[('2021-01-01', 'iphone', '7', slice(None))]) # 使用groupby对数据进行分组 s.groupby(['品牌', '型号']).sum() MultiIndex在Pandas中的使用非常广泛,它可以方便地处理多维数据,并且提供了方便的索引、切片和分组功能。对于需要处理多维数据的应用场景,MultiIndex是一种非常强大的数据结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值