数据存储
基础概念
术语
名字 | 中文描述 | 语义 |
---|---|---|
schema | class的元数据,在前端定义 | |
class | 数据集 | 相当于数据库中的table |
group | 数据空间 | 相当于数据库中的database,不指定使用默认值 |
partition | 分区 | 数据集水平拆分,当前只支持散列分区 |
row | 数据行 | 相当于数据库上的一行记录 |
column | 数据列列名 | 相当于数据库中的列名 |
key | 键 | 相当于数据库表中某一列的名字 |
value | 值 | 相当于数据库表中某一列的值 |
primaryKeys | 主键 | 数据集中唯一标识一行记录 |
partitionKeys | 分区键 | 数据集用于水平拆分的键 |
filter | 过滤条件 | 过滤结果集中数据的条件,如:a>=10 |
expr | 表达式 | 支持简单运算 |
connector | 连接符 | AND, OR |
operator | 操作符 | <, >, <=, >=, ==, !=, LIKE, in等 |
join | 联结 | 多表查询条件 |
select | 选择列 | 结果集中需要返回的列 |
ACContext | 上下文标识 |
支持的数据类型
类型 | 描述 |
---|---|
整型 | byte, short, int, long |
浮点型 | float, double |
字符串 | String |
布尔型 | Boolean |
支持的接口
名字 | 描述 |
---|---|
Create | 创建一行数据(行不存在,则执行创建; 行存在,报错) |
BatchCreate | 批量插入数据 |
Replace | 覆盖一行数据(行存在,则执行覆盖; 行不存在,则执行插入) |
BatchReplace | 批量覆盖数据 |
Delete | 删除一行数据(行存在,则执行删除; 行不存在,不执行) |
Update | 更改一行数据(行存在,则执行更改; 行不存在,执行创建) |
Modify | 更改一行数据(行存在,则执行更改; 行不存在,不执行) |
Find | 查找一行数据(行存在,则返回这行记录; 行不存在,返回NULL) |
Scan | 查找多行数据(行存在,则返回多行记录; 行不存在,结果集为空) |
BatchDelete | 批量删除数据(行存在,则执行删除; 行不存在,不执行) |
BatchUpdate | 批量更新数据(行存在,则执行更新; 行不存在,不执行) |
MultiScan | 多表查询(当前只支持处于同一个Group的非分区数据集) |
SimpleFullScan | 全表扫描(结果集不保证顺序) |
数据模型
AbleCloud目前提供基于MySQL的分布式存储服务。开发者需要预先设定数据集的结构,同时可以选择对数据进行分区或不分区。为定位数据所在分区,需要定义数据分区键(partitionKeys),可以为一列或多列。主键(primaryKeys)唯一表示一行数据, 可以为一列或多列。
分区数据集数据模型
非分区数据集数据模型
分区与非分区
什么是分区
分区指的是对数据集进行水平拆分,将同一个数据集中的数据分发到不同的分区,从而可以增大数据存储容量,实现负载均衡。
如何分区
当前是根据用户指定的分区键,内部使用散列分区算法来实现。分区键必须是主键的前缀,同一个分区键的数据会分布到同一个分区。
如何选择
如果是数据随着时间不断增加,建议使用分区数据集,如设备的上报数据,可以将设备ID设置为分区键,(设备ID, 时间戳)做为主键。
如果是数据随着时间变化不大,如设备表,用户表,则建议使用非分区数据集
分区的限制
分区一旦设定,不能更改,对于分区数据集,查询或是执行其它批量操作时,必须明确指定分区键。不能跨分区操作数据
基础数据结构
ACContext
ACContext 包含了用户的MajorDomain, SubDomain, DeveloperId, TraceId, 时间戳, 签名等信息,每个请求都必须带有ACContext才能与云端交互。单个ACContext可以认为是逻辑上一系列请求的唯一标识。
获取方式
ACContext ctx = ac.newContext();
ACFilter
ACFilter用于过滤结果集中的数据,当前支持:
名字 | 数学表示 | 描述 |
---|---|---|
EQUAL | == | 等于 |
NOT_EQUAL | != | 不等于 |
GREATER | > | 大于 |
GREATE_OR_EQUAL | >= | 大于等于 |
LESS | < | 小于 |
LESS_OR_EQUAL | <= | 小于等于 |
LIKE | like | 字符串模糊匹配 |
NOT_LIKE | not like | 字符串模糊匹配 |
BINARY_LIKE | binary like | 区分大小写的字符串模糊匹配 |
BINARY_NOT_LIKE | binary not like | 区分大小写的字符串模糊匹配 |
IN | in | 基于列表进行查找 |
NOT_IN | not in | 基于列表进行查找 |
AND | and | 与(与的优化级高于或) |
OR | or | 或 |
使用实例
// 实例1: 创建一个filter(key1>0 and key1<10)
ACFilter f1 = ac.filter().whereGreaterThan("key1", 0).andLessThan("key1", 10);
// 实例2: 创建一个filter(key1<=0 or key1>=10)
ACFilter f1 = ac.filter().whereLessThanOrEqualTo("key1", 0).orGreaterThanOrEqualTo("key1", 10);
// 实例3: 创建一个filter(key1以 "abcd" 为前缀, 不区分大小写)
ACFilter f1 = ac.filter().whereLike("key1", "abcd%");
// 实例4: 创建一个filter(key1以 "abcd" 为前缀, 不区分大小写)
ACFilter f1 = ac.filter().whereLike("key1", "abcd%");
// 实例5: 创建一个filter(key1以 "abcd" 为后缀, 不区分大小写)
ACFilter f1 = ac.filter().whereLike("key1", "%abcd");
// 实例6: 创建一个filter(key1包含子串 "abcd", 不区分大小写)
ACFilter f1 = ac.filter().whereLike("key1", "%abcd%");
// 实例7: 创建一个filter(key1 为 "v1" 或 "v2" 或 "v3"中的一个)
ACFilter f1 = ac.filter().whereIn("key1", String[]{"v1", "v2", "v3"});
// 实例8: 创建一个filter(数据集t1的c1列与数据集t2的c2列相等)
ACFilter f1 = ac.filter().whereEqualToColumn("t1.c1", "t2.c2");
ACJoin
ACJoin在多表查询时用于表示联表条件,当前仅支持内联结
使用实例
// t1为基准表,t2是需要联结的表
// 实例1: 联表条件(数据集t1的c1列 与 数据集t2的c2列相等)
ACFilter f1 = ac.filter().whereEqualToColumn("t1.c1", "t2.c2");
ACJoin j1 = ac.innerJoin("t2").where(f1);
// 实例2: 联表条件(数据集t1的c1列 与 数据集t2的c2列相等,并且 数据集t1的c1列的值大于100)
ACFilter f1 = ac.filter().whereEqualToColumn("t1.c1", "t2.c2").andGreaterThan("t1.c1", 100);
ACJoin j1 = ac.innerJoin("t2").where(f1);
使用实例
以空气净化器为例来说明。每个实例会针对分区数据集和非分区数据集分别说明接口使用方法。
列名 | 类型 | 描述 |
---|---|---|
device_id | 字符串 | 设备ID |
timestamp | 整数 | 时间戳 |
pm25 | 浮点数 | pm2.5值 |
speed | 整数 | 当前风机转速 |
mode | 字符串 | 当前净化器状态(auto(自动), high(高速), medium(中速), low(低速)) |
device_data(分区数据集)
device_data(非分区数据集)
数据写入
Create
在数据集中插入一条数据,如果数据不存在,则执行插入; 如果数据已经存在,则会报错。
分区数据集和非分区数据集使用方式相同
标准用法
ac.store(数据集名字, context).create(键值列表)
.put(key_1,value_1)
.put(key_..., value_...)
.put(key_n, value_n)
.execute();
注意事项
- 主键列表至少包含所有的主键列,可以包含非主键列
- key,value对根据需要选填,如果没有设置,则会填充默认值。整数,浮点数的默认值为0,字符串默认值为空字符串,布尔型的默认值为false
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc/日志 | 说明 |
---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | createParam is not set | createParam没有设置 |
ACServiceException | CLOUD | 3924 | data already exist | row[key1=xxx, keys2=xxx] is already exist | 数据行已经存在,创建失败 |
使用实例
- 实例1: 插入一条数据(device_id:"001", timestamp:1469098960, pm25:70.5, speed:40, mode:"low")
// 其它所有接口都有以下三种使用方式
// 使用方式一
ac.store("device_data", ctx).create("device_id", "001", "timestamp", 1469098960)
.put("pm25", 70.5)
.put("speed", 40)
.put("mode", "low")
.execute();
// 使用方式二
ac.store("device_data", ctx).create("device_id", "001", "timestamp", 1469098960, "pm25", 70.5, "speed", 40, "mode", "low")
.execute();
// 使用方式三
ACObject keyObj = new ACObject();
keyObj.put("device_id", "001");
keyObj.put("timestamp", 1469098960);
ac.store.("device_data", ctx).create(keyObj)
.put("pm25", 70.5)
.put("speed", 40)
.put("mode", "low")
.execute();
类比SQL
INSERT INTO `device_data` SET `device_id`='001', `timestamp`=1469098960, `pm25`=70.5, `speed`=40, `mode`='low';
BatchCreate
在数据集中指插入多行数据,如果数据不存在,则执行插入; 如果数据已经存在,则会报错。
分区数据集和非分区数据集使用方式相同
标准用法
// 用法1
ac.store(数据集名字, context).batchCreate()
.add(ACObject_1)
...
.add(ACObject_n)
.execute();
// 用法2
List<ACObject> objs = new ArrayList<>();
ac.store(数据集名字, context).batchCreate()
.put(objs)
.execute();
注意事项
- 一次批量最多支持1000条
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc/日志 | 说明 |
---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | batchCreateParam is not set | batchCreateParam没有设置 |
使用实例
- 实例1:批量插入三条数据
(device_id:"001", timestamp:1469098960, pm25:70.5, speed:40, mode:"low") (device_id:"001", timestamp:1469098961, pm25:70.6, speed:41, mode:"low") (device_id:"001", timestamp:1469098962, pm25:70.7, speed:42, mode:"low")
ACObject obj1 = new ACObject();
obj1.put("device_id", "001");
obj1.put("timestamp", 1469098960);
obj1.put("pm25", 70.5);
obj1.put("speed", 40);
obj1.put("mode", "low");
ACObject obj2 = new ACObject();
obj2.put("device_id", "001");
obj2.put("timestamp", 1469098961);
obj2.put("pm25", 70.6);
obj2.put("speed", 41);
obj2.put("mode", "low");
ACObject obj3 = new ACObject();
obj3.put("device_id", "001");
obj3.put("timestamp", 1469098962);
obj3.put("pm25", 70.7);
obj3.put("speed", 42);
obj3.put("mode", "low");
// 方法一:
ac.store("device_data", ctx).batchCreate()
.add(obj1, obj2, obj3)
.execute();
// 方法二:
ac.store("device_data", ctx).batchCreate()
.add(obj1)
.add(obj2)
.add(obj3)
.execute();
//方法三:
List<ACObject> objs = new ArrayList<>();
objs.add(obj1);
objs.add(obj2);
objs.add(obj3);
ac.store("device_data", ctx).batchCreate()
.put(objs)
.execute();
类比SQL
INSERT INTO `device_data`(`device_id`, `timestamp`, `pm25`, `speed`, `mode`) VALUES ('001', 1469098960, 70.5, 40, 'low'), ('001', 1469098961, 70.6, 41, 'low'), ('001', 1469098962, 70.7, 42, 'low');
Replace
当此行数据不存在时,则插入这行数据,如果已经存在,则覆盖已有数据
分区数据集和非分区数据集使用方式相同
标准用法
ac.store(数据集名字, context).replace(键值列表)
.put(key_1, value_1)
.put(key_..., value_...)
.put(key_n, value_n)
.execute();
注意事项
- 键值列表至少包含所有的主键列,可以包含其它键值
- (key,value)对选填,如果没有设置,则会使用默认值,整数,浮点型的默认值为0,字符串默认值为空字符串,布尔型的默认值为false。
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc/日志 | 说明 | |
---|---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | replaceParam is not set | replaceParam没有设置 |
使用实例
- 实例1:覆盖写入一条数据(device_id="001", timestamp:1469098960, pm25:70.5, speed:40, mode:low)
// 使用方法一
ac.store("device_data", ctx).replace("device_id", "001", "timestamp", 1469098960)
.put("pm25", 70.5)
.put("speed", 40)
.put("mode", "low")
.execute();
// 使用方法二
ac.store("device_data", ctx).replace("device_id", "001", "timestamp", 1469098960, "pm25", 70.5, "speed", 40, "mode", "low")
.execute();
类比SQL
REPLACE INTO `device_data` SET `device_id`='001', `timestamp`=1469098960, `pm25`=70.5, `speed`=40, `mode`='low';
BatchReplace
在数据集中批量覆盖多行数据,如果数据不存在,则执行插入; 如果数据已经存在,则执行覆盖。
分区数据集和非分区数据集使用方式相同
标准用法
// 用法1
ac.store(数据集名字, context).batchReplace()
.add(ACObject_1)
...
.add(ACObject_n)
.execute();
// 用法2
List<ACObject> objs = new ArrayList<>();
ac.store(数据集名字, context).batchReplace()
.put(objs)
.execute();
注意事项
- 一次批量最多支持1000条
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc/日志 | 说明 |
---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | batchReplaceParam is not set | batchReplaceParam没有设置 |
使用实例
- 实例1:批量插入三条数据
(device_id:"001", timestamp:1469098960, pm25:70.5, speed:40, mode:"low") (device_id:"001", timestamp:1469098961, pm25:70.6, speed:41, mode:"low") (device_id:"001", timestamp:1469098962, pm25:70.7, speed:42, mode:"low")
ACObject obj1 = new ACObject();
obj1.put("device_id", "001");
obj1.put("timestamp", 1469098960);
obj1.put("pm25", 70.5);
obj1.put("speed", 40);
obj1.put("mode", "low");
ACObject obj2 = new ACObject();
obj2.put("device_id", "001");
obj2.put("timestamp", 1469098961);
obj2.put("pm25", 70.6);
obj2.put("speed", 41);
obj2.put("mode", "low");
ACObject obj3 = new ACObject();
obj3.put("device_id", "001");
obj3.put("timestamp", 1469098962);
obj3.put("pm25", 70.7);
obj3.put("speed", 42);
obj3.put("mode", "low");
// 方法一:
ac.store("device_data", ctx).batchReplace()
.add(obj1, obj2, obj3)
.execute();
// 方法二:
ac.store("device_data", ctx).batchReplace()
.add(obj1)
.add(obj2)
.add(obj3)
.execute();
//方法三:
List<ACObject> objs = new ArrayList<>();
objs.add(obj1);
objs.add(obj2);
objs.add(obj3);
ac.store("device_data", ctx).batchReplace()
.put(objs)
.execute();
类比SQL
REPLACE INTO `device_data`(`device_id`, `timestamp`, `pm25`, `speed`, `mode`) VALUES ('001', 1469098960, 70.5, 40, 'low'), ('001', 1469098961, 70.6, 41, 'low'), ('001', 1469098962, 70.7, 42, 'low');
数据删除
Delete
删除单行数据,如果这行数据存在,则执行删除,如果数据不存在,不会执行
分区数据集和非分区数据集使用方式相同
标准用法
ac.store(数据集名字, context).delete(键值列表)
.execute();
注意事项
- 键值列表至少含所有的主键列
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc/日志 | 说明 |
---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | deleteParam is not set | deleteParam没有设置 |
使用实例
- 实例1: 删除一行设备数据记录(ID="001", timestamp=1469098960)
ac.store("device_data", ctx).delete("device_id", "001", "timestamp", 1469098960)
.execute();
类比SQL
DELETE FROM `device_data` WHERE `device_id`='001' AND `timestamp`=1469098960;
- 实例2: 删除一行设备数据记录(ID="001", timestamp=1469098960, mode='high')
ac.store("device_data", ctx).delete("device_id", "001", "timestamp", 1469098960, "mode", "high")
.execute();
类比SQL
DELETE FROM `device_data` WHERE `device_id`='001' AND `timestamp`=1469098960 AND `mode`='high';
BatchDelete
根据过滤条件删除多行数据
分区数据集的使用有限制,键值列表至少包含完整的分区键
标准用法
ac.store(数据集名字,context).batchDelete(键值列表)
.where(filter_1)
.and(filter_2)
.or(filter_3)
.execute();
注意事项
- 对于分区数据集,键值列表必须包含完整的分区键
- 对于非分区数据集,键值列表可以为空
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc/日志 | 说明 | |
---|---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | batchDeleteParam is not set | batchDeleteParam没有设置 |
使用实例
- 实例1:删除设备(ID="001", timestamp>=1469098960 and timestamp<=1469102560)之间的所有数据
ACFilter filter1 = ac.filter().whereGreaterThanOrEqualTo("timestamp", 1469098960).andLessThanOrEqualTo("timestamp", 1469102560);
ac.store("device_data", context).batchDelete("device_id", "001").
.where(filter1)
.execute();
类比SQL
DELETE FROM `device_data` WHERE `device_id`="001" AND (`timestamp`>=1469098960 AND `timestamp`<=1469102560);
- 实例2:删除设备(ID="001", timestamp>=1469098960 timestamp<=1469102560)之间并且speed大于40的所有数据
ACFilter filter1 = ac.filter().whereGreaterThanOrEqualTo("timestamp", 1469098960).andLessThanOrEqualTo("timestamp", 1469102560);
ACFilter filter2 = ac.filter().whereGreaterThan("speed", 40);
ac.store("device_data", context).batchDelete("device_id", "001").
.where(filter1)
.and(filter2)
.execute();
类比SQL
DELETE FROM `device_data` WHERE `device_id`="001" AND (`timestamp`>=1469098960 AND `timestamp`<=1469102560) AND `speed`>40;
修改数据
Update
修改单行数据,如果这行数据不存在则执行插入,如果存在,则修改指定的列。
分区数据集和非分区数据集使用方式相同
标准用法
ac.store(数据集名字, context).update(键值列表)
.put(key_1, value_1)
.put(key_..., value_...)
.put(key_n, value_n)
.execute();
注意事项
- 键值列表至少包含所有主键列
- (key,value)对可选
- 没有更新的列会使用原来的值
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc/日志 | 说明 |
---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | updateParam is not set | updateParam没有设置 |
使用实例
- 实例1:写入一条数据(device_id="001", timestamp=1469098960, speed=60)数据,如果数据行不存在,则插入此行数据(device_id="001", timestamp=1469098960, speed=60,其它列使用默认值),如果数据行已经存在,则将speed列的值改为60。
ac.store("device_data", context).batchUpdate("device_id", "001", "timestamp", 1469098960)
.put("speed", 60)
.execute();
类比SQL
INSERT INTO `device_data` SET `device_id`='001', `timestamp`=1469098960, `speed`=60 ON DUPLICATE KEY update `device_id`='001', `timestamp`=1469098960, `speed`=60;
Modify
修改单行数据,如里数据不存在,则报错,如果数据存在,则将指定列修改为特定的值,可以进行加减运算
分区数据集和非分区数据集使用方式相同
标准用法
ac.store(数据集名字, context).modify(键值列表)
.where(key_xxx, value_xxx)
.and(key_xxx, value_xxx)
.set(key_1, value_1)
.inc(key_..., value_...)
.dec(key_n, value_n)
.execute();
注意事项
- 键值列表至少包含所有主键列
- where指的是过滤条件
- set/inc/dec至少使用一个
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc/日志 | 说明 | |
---|---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | modifyParam is not set | modifyParam没有设置 |
使用实例
- 实例1:将(ID="001", timestamp=1469098960)的数据,speed加5,mode设备设置为high, pm25值减1.5
ac.store("device_data", context).modify("device_id", "001", "timestamp", 1469098960)
.inc("speed", 5)
.set("mode", "high")
.dec("pm25", 1.5)
.execute();
类比SQL
UPDATE `device_data` SET `speed`=`speed`+5, `mode`='high', `pm25`=`pm25`-1.5 WHERE `device_id`="001" AND `timestamp`=1469098960;
- 实例2:将(ID="001", timestamp=1469098960 并且 speed=5)的数据,mode设置为high
ac.store("device_data", context).modify("device_id", "001", "timestamp", 1469098960)
.where("speed", 5)
.set("mode", "high")
.execute();
类比SQL
UPDATE `device_data` SET `mode`='high' WHERE `device_id`="001" AND `timestamp`=1469098960 AND `speed`=5;
BatchUpdate
根据条件修改数据
分区数据集的使用方式有限制,键值列表至少包含完整的分区键
标准用法
ac.store(数据集名字, ctx).batchUpdate(键值列表)
.where(filter_1)
.or(filter_2)
.and(filter_3)
.set(key_n, value_n)
.inc(key_n, value_n)
.dec(key_n, value_n)
.execute();
注意事项
- 对于分区数据集,键值列表至少包含所有的分区键
- 对于非分区数据集,键值列表可以为空
- set/inc/dec可以为多个,至少包含一个
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc/日志 | 说明 | |
---|---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | batchUpdateParam is not set | batchUpdateParam没有设置 |
使用实例
- 实例1:将(device_id="001", pm25>40)的数据的mode改为"high", speed加20
ACFilter f1 = ac.filter().whereGreaterThan("pm25", 40);
ac.store("device_data", ctx).batchUpdate("device_id", "001")
.where(f1)
.set("mode", "high")
.inc("speed", 20)
.execute();
类比SQL
UPDATE `device_data` SET `mode`='high', `speed`=`speed`+20 WHERE `deviceId`='001' AND `pm25`>40;
查询数据
find
查询单条数据,如果数据不存在,则返回的数据为空,如果存在,则返回单行数据。
分区数据集和不分区数据集使用方式相同
标准用法
// 如果数据不存在,则返回null
ACObject result = ac.store(数据集名字, ctx).find(键值列表)
.select(key1, ..., keyn)
.execute();
注意事项
- 键值列表必须包含完整的主键列
- select可选,如果没有指定select,则会返回所有的列
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc | 日志 | 说明 |
---|---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | findParam is not set | findParam is not set | 没有设置findParam |
使用实例
- 实例1:查询(device_id="001", timestamp=1469098960)这一行的所有列
ACObject result = ac.store("device_data", ctx).find("device_id", "001", "timestamp", 1469098960)
.execute();
// 结果集如果为空则, result==null
// 结果集不为空输出数据
String device_id = result.getString("device_id");
Long timestamp = result.getLong("timestamp");
Double pm25 = result.getDouble("pm25");
String mode = result.getString("mode");
Long speed = result.getLong("speed");
System.out.Println(device_id + ", " + timestamp + ", " + pm25 + ", " + mode + ", " + speed);
类比SQL
SELECT * FROM `device_data` WHERE `device_id`='001' AND `timestamp`=1469098960;
- 实例2:查询(device_id="001", timestamp=1469098960)这一行的speed列和mode列
ACObject result = ac.store("device_data", ctx).find("device_id", "001", "timestamp", 1469098960)
.select("speed", "mode")
.execute();
// 如果为空则, result==null
// 如果结果不为空,取出数据
Long speed = result.getLong("speed");
String mode = result.getString("mode");
System.out.Println(speed + ", " + mode);
类比SQL
SELECT `speed`, `mode` FROM `device_data` WHERE `device_id`='001' AND `timestamp`=1469098960;
scan
根据条件查询数据,可能有多条。
分区数据集的使用有限制,键值列表至少包含完整的分区键
标准用法
// 如果符合条件的数据不存在,则返回一个空的数组
List<ACObject> results = ac.store(数据集名字, ctx).scan(键值列表)
.select(key_1, ...., key_n)
.start(键值列表)
.end(键值列表)
.where(filter_1)
.and(filter_2)
.or(filter_3)
.groupBy(key_1, ..., key_n)
.sum(key_1, ..., key_n)
.avg(key_1, ..., key_n)
.max(key_1, ..., key_n)
.min(key_1, ..., key_n)
.count()
.orderByAsc(key_1, ..., key_n)
.orderByDesc(key_1, ..., key_n)
.offset(数值)
.limit(数值)
.execute();
注意事项
- select可以包含一列或多列,如果未指定,则表示返回所有列
- start/end至少包含完整的分区键
- start/end最多可以包含一个非主键key
- sum(key1, ..., keyn)指的是对每一列分别求sum
- limit最大为1000, 每次最多返回1000行数据
错误码
Exception | 报错位置 | errorCode | errorMsg | errorDesc | 日志 | 说明 |
---|---|---|---|---|---|---|
ACServiceException | CLOUD | 3002 | invalid param | scanParam is not set | scanParam is not set | 没有设置scanParam |
使用实例
- 实例1:查询(device_id="001", timestamp>=1469098960, timestamp<=1469102560) 的pm25值和对应的timestamp
List<ACObject> results = ac.store("device_data", ctx).scan("device_id", "001")
.select("timestamp", "pm25")
.start("timestamp", 1469098960)
.end("timestamp", 1469102560)
.execute();
// 输出结果
for (ACObject result:results) {
long timestamp = result.getLong("timestamp");
double pm25 = result.getDouble("pm25");
System.out.Println(timestamp + "," + pm25)
}
类比SQL
SELECT `timestamp`, `pm25` FROM `device_data` WHERE `device_id`="001" AND `timestamp`>-1469098960 AND `timestamp`<=1469102560;
- 实例2:查询(device_id="001", timestamp>=1469098960) 的第11条到第30条的 pm25值和对应的timestamp
List<ACObject> results = ac.store("device_data", ctx).scan("device_id", "001")
.select("timestamp", "pm25")
.start("timestamp", 1469098960)
.offset(10)
.limit(20)
.execute();
// 输出结果
for (ACObject result:results) {
long timestamp = result.getLong("timestamp");
double pm25 = result.getDouble("pm25");
System.out.Println(timestamp + "," + pm25)
}
类比SQL
SELECT `timestamp`, `pm25` FROM `device_data` WHERE `device_id`='001' AND `timestamp`>-1469098960 limit 10,20;
- 实例3:查询(device_id="001")的最近的20条数据的pm25值和对应的时间戳
List<ACObject> results = ac.store("device_data", ctx).scan("device_id", "001")
.select("timestamp", "pm25")
.orderByDesc("timestamp")
.limit(20)
.execute();
// 输出结果
for (ACObject result:results) {
long timestamp = result.getLong("timestamp");
double pm25 = result.getDouble("pm25");
System.out.Println(timestamp + "," + pm25)
}
类比SQL
SELECT `timestamp`, `pm25` FROM `device_data` WHERE `device_id`='001' ORDERY BY `timestamp`DESC limit 10,20;
- 实例4:查询device_id="001"并且pm25>40.5的pm25值和时间戳
ACFilter f1 = ac.filter().whereGreaterThan("pm25", 40.5);
List<ACObject> results = ac.store("device_data", ctx).scan("device_id", "001")
.select("timestamp", "pm25")
.where(f1)
.execute();
// 输出结果
for (ACObject result:results) {
long timestamp = result.getLong("timestamp");
double pm25 = result.getDouble("pm25");
System.out.Println(timestamp + "," + pm25)
}
类比SQL
SELECT `timestamp`, `pm25` FROM `device_data` WHERE `device_id`='001' AND `pm25`>40.5 LIMIT 1000;
- 实例5:查询device_id="001" 并且 pm25>40.5的数据行数
Filter f1 = ac.filter().whereGreaterThan("pm25", 40.5);
List<ACObject> results = ac.store("device_data", ctx).scan("device_id", "001")
.where(f1)
.count()
.execute();
// 输出结果
Long count = results.get(0).getLong("_count");
System.out.Println(count)
类比SQL
SELECT count(*) FROM `device_data` WHERE `device_id`='001' AND `pm25`>40.5;
- 实例6:计算device_id="001" 并且 timestamp>=1469098960 && timestamp<=146910256的pm25平均值
List<ACObject> results = ac.store("device_data", ctx).scan("device_id", "001")
.start("timestamp", 1469098960)
.end("timestamp", 1469102560)
.avg("pm25")
.execute();
// 输出结果
Long avg_pm25 = results.get(0).getLong("_avg_pm25");
System.out.Println(avg_pm25)
类比SQL
SELECT AVG(`pm25`) as _avg_pm25 FROM `device_data` WHERE `device_id`='001' AND (`timestamp`>=1469098960 AND `timestamp`<=1469102560);
- 实例7:查询device_id="001" (mode="auto"并且pm25>40) 或 (mode="high"并且pm25>30)的pm25,mode,timestamp,最多返回500条
ACFilter f1 = ac.filter().whereEqualTo("mode", "auto").andGreateThan("pm25", 40);
ACFilter f2 = ac.filter().whereEqualTo("mode", "high").andGreateThan("pm25", 30);
List<ACObject> results = ac.store("device_data", ctx).scan("device_id", "001")
.select("timestamp", "mode", "pm25")
.where(f1)
.or(f2)
.limit(500)
.execute();
// 输出结果
for (ACObject result:results) {
long timestamp = result.getLong("timestamp");
String mode = result.getString("mode");
double pm25 = result.getDouble("pm25");
System.out.Println(timestamp + ", " + mode + ", " + pm25)
}
类比SQL
SELECT `timestamp`, `mode`, `pm25` FROM `device_data` WHERE `device_id`='001' AND (`mode`='auto' AND `pm25`>40) AND (`mode`='high' AND `pm25`>30) LIMIT 500;
- 实例8:查询device_id="001"并且(timestamp>1469098960并且timestamp<=1469102560)的时间戳和pm25值,并按pm25值逆序排列
List<ACObject> results = ac.store("device_data", ctx).scan("device_id", "001")
.start("timestamp", 1469098960)
.end("timestamp", 1469102560)
.orderByDesc("pm25")
.execute();
// 输出结果
for (ACObject result:results) {
long timestamp = result.getLong("timestamp");
double pm25 = result.getDouble("pm25");
System.out.Println(timestamp + ", " + pm25)
}
类比SQL
SELECT * FROM `device_data` WHERE `device_id`='001' AND (`timestamp`>=1469098960 AND `timestamp`<=1469102560) ORDER BY `pm25`DESC;
- 实例9:查询device_id="001"并且(timestamp>1469098960并且timestamp<=1469102560)的数据中的pm25的最大值和时间点
List<ACObject> results = ac.store("device_data", ctx).scan("device_id", "001")
.select("timestamp")
.start("timestamp", 1469098960)
.end("timestamp", 1469102560)
.max("pm25")
.execute();
// 输出结果
Long timestamp = results.get(0).getLong("timestamp");
Double pm25 = result.get(0).getString("pm25");
类比SQL
SELECT `timestamp`, MAX(`pm25`) as _max_pm25 FROM `device_data` WHERE `device_id`='001' AND (`timestamp`>=1469098960 AND `timestamp`<=1469102560);
- 实例10:查询device_id="001"在使用不同mode时的speed平均值
List<ACObject> results = ac.store("device_data", ctx).scan("device_id", "001")
.select("mode", "speed")
.groupBy("mode")
.execute();
// 输出结果
for (ACObject result:results) {
String mode = result.getString("mode");
long speed = result.getLong("speed");
System.out.Println(mode + ", " + speed)
}
类比SQL
SELECT `mode`, `speed`, AVG(`pm25`) as _avg_pm25 FROM `device_data` WHERE `device_id`='001' GROUP BY(`mode`);
多表数据查询
同时查询多个数据集的数据
执行多表查询的数据集,必须是非分区数据集,并且属于同一个数据空间
数据集实例
以商场的订单系统为例来说明
user数据集: 商场的用户信息,记录了用户的id, 名字,地址,电话号码
order数据集: 商场的订单数据,记录了订单ID,下订单的用户,商品ID,当时的价格,下订单的时间
MultiScan
查询多个数据集的数据
标准用法
// 如果符合条件的数据不存在,则返回一个空的数组
// 查询请求和结果中的列名,必须是完整的格式(表名.列表),不支持使用不完整的列名
List<ACObject> results = ac.store(ctx, "数据集1", "数据集2", "数据集n")
.multiScan()
.select(key_1, ..., key_n)
.from("基准数据集")
.join(join条件)
.where(filter条件)
.and(filter条件)
.or(filter条件)
.sum(key_1, ..., key_n)
.avg(key_1, ..., key_n)
.max(key_1, ..., key_n)
.min(key_1, ..., key_n)
.count()
.orderByAsc(key_1, ..., key_n)
.orderByDesc(key_1, ..., key_n)
.offset(数值)
.limit(数值)
.execute();
注意事项
- limit最大为1000, 每次最多返回1000行数据
- 当前仅支持内联结
- 支持多个(>=2)数据集的联表查询
- 如果数据集的数据量很大,则不建议使用多表查询,而是通过使用多次查询来实现
错误码
使用实例
- 实例1: 查询用户名为"jack"的地址和所有订单(订单ID, 商品ID, 下单价格)
ACContext ctx = ac.newContext();
ACFilter f1 = ac.filter().whereEqualTo("user.user_name", "jack");
ACFilter f2 = ac.filter().whereEqualToColumn("user.user_id", "order.user_id");
ACJoin j1 = ac.innerJoin("order").where(f2);
List<ACObject> results = ac.store(ctx, "user", "order").multiScan()
.select("user.user_name", "user.user_address", "order.order_id", "order.commodity_id", "order.price")
.from("user")
.join(j1)
.where(f1)
.limit(100)
.execute();
// 输出结果
for (ACObject result:results) {
String userName = result.getString("user.user_name");
String userAddress = result.getString("user.user_address");
long userOrderId = result.getLong("order.order_id");
long commodityId = result.getLong("order.commodity_id");
long price = result.getLong("order.price");
System.out.println(userName + ", " + userAddress + ", " + userOrderId + ", " + commodityId + ", " + price);
}
类比SQL
SELECT `user`.`user_name`, `user`.`user_address`, `order`.`order_id`, `order`.`commodity_id`, `order`.`price`
FROM `user`
INNER JOIN `order` ON (`user`.`user_id` = `order`.`user_id`)
WHERE (`user`.`user_name` = 'jack')
LIMIT 1000;
常见问题
无法存储emojis表情
因为数据集用的是UTF-8字符集,所以无法存储emojis表情,但可以将emoji转化为Base64写入数据集,然后读取时,再转化回emojis
使用实例:
// emoji转Base64
String srcStr = "Here is a boy: \uD83D\uDC66\uD83C\uDFFF"; // emojis字符串
String desStr = Base64.encodeToString(srcStr.getBytes("UTF-8"), Base64.NO_WRAP); // emojis转Base64
// base64转emoji
String srcStr = "SGVyZSBpcyBhIGJveTog8J+RpvCfj78="; // base64字符串
Byte[] desData = Base64.decode(srcStr, Base64.NO_WRAP); // Base64转emojis
String desStr = new String(desData, "UTF-8");
通用错误码
SDK错误
Exception | errorDesc | 日志 | 说明 |
---|---|---|---|
IllegalArgumentException | param keys is invalid | param key must not be null | key名字不能为NULL |
IllegalArgumentException | param keys is invalid | param key must be string, key[xxx] keyType[xxx] | key名字必须是字符串 |
IllegalArgumentException | param keys is invalid | param value must not be null, key[xxx] | value不能为NULL |
IllegalArgumentException | param keys is invalid | param value type is not supported, key[xxx] value[xxx] valueType[xxx] | value的数据类型不支持 |
IllegalArgumentException | param keys is invalid | count of param keys must not be 0 | (key-value)数量不能为0 |
IllegalArgumentException | param keys is invalid | count of param keys must be even, count[xxx] | (key-value)必须是一一对应,所以数量必须是偶数 |
IllegalArgumentException | invalid put param | param key must not be null | put的key是null |
IllegalArgumentException | invalid put param | param value must not be null | put的value是null |
IllegalArgumentException | invalid put param | param value type is not supported, key[xxx], value[xxx] valueType[xxx] | put的value的数据类型不支持 |
IllegalArgumentException | invalid filter param | param key must not be null | filter的key是null |
IllegalArgumentException | invalid filter param | param value must not be null | filter的value是null |
IllegalArgumentException | invalid filter param | param value type is not supported, key[xxx], value[xxx] valueType[xxx] | filter的value的数据类型不支持 |
IllegalArgumentException | invalid expr param | param key must not be null | expr的key是null |
IllegalArgumentException | invalid expr param | param value must not be null | expr的value是null |
IllegalArgumentException | invalid expr param | param value type is not supported, key[xxx], value[xxx] valueType[xxx] | expr的value的数据类型不支持 |
IllegalArgumentException | param filter must not be empty | param filter must not be empty | filter不能为空 |
IllegalArgumentException | param key of select must not be empty | param key of select must not be empty | select key不能为空 |
IllegalArgumentException | param startKeys is invalid | param primaryKey must not be null | startKey名字不能为NULL |
IllegalArgumentException | param startKeys is invalid | param primaryKey must be string, key[xxx] keyType[xxx] | startKey名字必须是字符串 |
IllegalArgumentException | param startKeys is invalid | param primaryValue must not be null, key[xxx] | startValue不能为NULL |
IllegalArgumentException | param startKeys is invalid | param primaryValue type is not supported, key[xxx] value[xxx] valueType[xxx] | startValue的数据类型不支持 |
IllegalArgumentException | param endKeys is invalid | param primaryKey must not be null | endKey名字不能为NULL |
IllegalArgumentException | param endKeys is invalid | param primaryKey must be string, key[xxx] keyType[xxx] | endKey名字必须是字符串 |
IllegalArgumentException | param endKeys is invalid | param primaryValue must not be null, key[xxx] | endValue不能为NULL |
IllegalArgumentException | param endKeys is invalid | param primaryValue type is not supported, key[xxx] value[xxx] valueType[xxx] | endValue的数据类型不支持 |
IllegalArgumentException | param offset must be >=0, offset[xxx] | param offset must be >=0, offset[xxx] | offset必须大于等于0 |
IllegalArgumentException | param limit must be >=0 and <=1000, limit[xxx] | param limit must be >=0 and <=1000, limit[xxx] | limit必须>=0 and <=1000 |
IllegalArgumentException | param key of orderBy is conflict, key[xxx] | param key of orderBy is conflict, key[xxx] | orderBy的列设置的排序方式不一致 |
IllegalArgumentException | param key of aggregate must not be empty | param key of aggregate must not be empty | aggregate指定的key不能为空 |
IllegalArgumentException | param key of select must not be empty | param key of select must not be empty | select key 不能为null |
云端错误
Exception | errorCode | errorMsg | errorDesc/日志 | 说明 | |
---|---|---|---|---|---|
ACServiceException | 3002 | invalid param | context is not set | 没有设置context | |
ACServiceException | 3002 | invalid param | majorDomain is not set | 没有设置majorDomain | |
ACServiceException | 3002 | invalid param | common user is not allowed to use store | 普通终端用法无法直接使用store | |
ACServiceException | 3002 | invalid param | class is not set | 没有设置数据集 | |
ACServiceException | 3002 | invalid param | param offset must be >=0 | 参数offset必须>=0 | |
ACServiceException | 3002 | invalid param | param limit must be >=0 and <=1000 | 参数limit必须>=0并且<=1000 | |
ACServiceException | 3004 | not allowed | common user is not allowed to use store | 普通用户不允许直接访问store | |
ACServiceException | 3920 | class not exist | class[xxx] is not exist | 数据集不存在 | |
ACServiceException | 3925 | invalid value param | value of key[xxx] must not be null | value的值不能为null | |
ACServiceException | 3925 | invalid value param | valueType of key[xxx] is not inconsistent with schema | value的数据类型与schema中不一致 | |
ACServiceException | 3927 | invalid filter param | key[xxx] of filter is not exist in schema | filter的key在schema中不存在 | |
ACServiceException | 3927 | invalid filter param | filter value of key[xxx] must not be null | filter的value不能为null | |
ACServiceException | 3927 | invalid filter param | filter valueType is not inconsistent with schema | filter的value的数据类型与schema不一致 | |
ACServiceException | 3927 | invalid filter param | not supported filter connector[xxx] | 不支持的filter connector | |
ACServiceException | 3927 | invalid filter param | filter is not set | filter没有设置 | |
ACServiceException | 3928 | invalid expr param | param expr(set, inc, dec) is empty | 表达式参数不存在 | |
ACServiceException | 3928 | invalid expr param | expr key[xxx] must not be null | 表达式key是null | |
ACServiceException | 3928 | invalid expr param | expr key[xxx] is not exist | 表达式key在schema不存在 | |
ACServiceException | 3928 | invalid expr param | expr operator[xxx] is not supported | 不支持的表达式运算符 | |
ACServiceException | 3928 | invalid expr param | expr value of key[xxx] must not be null | 表达式的value是null | |
ACServiceException | 3929 | column not exist | column[xxx] is not exist in schema | column在schema中不存在 | |
ACServiceException | 3930 | invalid partition key | partitionKeys is not set | 没有设置分区键 | |
ACServiceException | 3930 | invalid partition key | partitionKey[xxx] is not set | 分区键的某一个key没有设置 | |
ACServiceException | 3932 | invalid primary key | primaryKey[xxx] is not set | 主键列不存在 | |
ACServiceException | 3934 | invalid aggregate param | keys of aggr[xxx] must not be empty | 聚集函数指定的key不能为空 | |
ACServiceException | 3934 | invalid aggregate param | key[xxx] of aggr[xxx] is not exist in schema | 聚焦函数指定的key在schema中不存在 | |
ACServiceException | 3934 | invalid aggregate param | not supported aggregate[xxx] | 不支持的聚集函数 | |
ACServiceException | 3935 | invalid groupBy param | key[xxx] of groupBy is not exist in schema | groupBy指定的key在schema中不存在 | |
ACServiceException | 3936 | invalid orderBy param | key[xxx] of orderBy is not exist in schema | orderBy指定的key在schema中不存在 | |
ACServiceException | 3937 | invalid select param | key[xxx] of select is not exist in schema | select的key在schema中不存在 |