表格存储(Table Store)是阿里云自研的 NoSQL 多模型数据库,提供海量结构化数据存储以及快速的查询和分析服务。表格存储的分布式存储和强大的索引引擎能够支持 PB 级存储、千万 TPS 以及毫秒级延迟的服务能力。
表格存储让我不需要关心运维层面的问题:可靠、安全、快速,并且非常灵活,无限无缝拓展,价格实惠;
引用官方,上面有更全面的说明:产品优势
自己在用表格存储的时候,觉得官方的 SDK 需要我关心很多与表格存储设计原理相关的事情,例如 Long 类型或搜索;
而我作为用户使用,更希望关注与自己业务相关的问题,当业务复杂度日益增长,更需要想办法节约成本;
于是当我插入一条数据进表格、或者搜索的时候,把一些自己不想关心的操作放到了公共函数中处理,代码更加简洁,如下:
// PUT
const res = await tj.row('sampleTable')
.rowExistenceExpectation(RowExistenceExpectation.IGNORE)
.returnType(ReturnType.Primarykey)
.put({
gid: 20013,
uid: 20013,
col1: '表格存储',
col2: '2',
col3: 3.1,
col4: -0.32,
col5: 123456789,
});
// Nested search
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.TERM_QUERY,
fieldName: 'Col_Nested.Sub_Col_Keyword',
value: 'hangzhou',
}]);
以上操作等同官方 SDK 的以下两个示例: 单行数据操作、 嵌套类型查询
当然这是一个取舍的问题,带来了便捷性的提升同时也舍弃了复杂的操作;例如我需要操作多版本数据时更常用官方的方式。
谨慎用于生产环境,推荐使用阿里云官方 SDK:aliyun-tablestore-nodejs-sdk
这个 SDK 是基于官方的 aliyun-tablestore-nodejs-sdk 做了一层简易的封装,具体的一些改变如下:
-
使用 JSDoc 注释规范维护代码;
-
在 getRow、search、batchGetRow 和 getRange 方法上,返回的数据新增 data 字段,该字段由 row 或 rows 生成:
- 将数据转换为 key:value 的格式;
- 将 Int64 对象类型转换为 Number 类型;
- 将 Nested 字符串通过 JSON.parse 转换成对象;
- 生成 data 的过程不改变其他任何字段;
-
与 primaryKey, attributeColumns 相关的传入都使用 key:value 方式传入,不用区分 primaryKey, attributeColumns;
-
使用链式调用,不用关心参数结构;
-
添加更多默认值;
在使用之前,请确保已开通 表格存储,并已完成密钥配置。
参考:初始化
const { TJ } = require('tablestore-js');
const tj = new TJ(config, tables, indexes);
config {
required string accessKeyId = 1
required string accessKeySecret = 2
required string endpoint = 3
required string instancename = 4
optional string stsToken = 5
optional string maxRetries = 6
}
tables {
required string tableName = 1
required string primaryKey = 2
}
indexes {
required string tableName = 1
required string primaryKey = 2
}
- config: 参考:初始化
- tables: 参考:TableMeta
- indexes: 参考:创建多元索引
注意:参数的命名以 aliyun-tablestore-nodejs-sdk 为准
以下操作等同官方文档:
const res = await tj.table('sampleTable')
.capacityUnit({
read: 0,
write: 0,
})
.tableOptions({
timeToLive: -1,
maxVersions: 1,
})
.create;
const res = await tj.table('sampleTable')
.tableOptions({
maxVersions: 5,
})
.update;
const res = await tj.table('sampleTable').describe;
const res = await tj.table().list;
const res = await tj.table('sampleTable').delete;
以下操作等同官方文档:
const res = await tj.index('sampleTable').list;
const res = await tj.index('sampleTable')
.indexName('sampleIndex')
.create;
const res = await tj.index('sampleTable')
.indexName('sampleIndex')
.delete;
const res = await tj.index('sampleTable')
.indexName('sampleIndex')
.describe;
以下操作等同于官方文档 单行数据操作;
const { RowExistenceExpectation, ReturnType } = require('tablestore-js');
const res = await tj.row('sampleTable')
.rowExistenceExpectation(RowExistenceExpectation.IGNORE)
.returnType(ReturnType.Primarykey)
.put({
gid: 20013,
uid: 20013,
col1: '表格存储',
col2: '2',
col3: 3.1,
col4: -0.32,
col5: 123456789,
});
暂未实现 columnFilter 的参数组装;
const res = await tj.row('sampleTable')
.maxVersions(2)
.primaryKey({
gid: 20004,
uid: 20004,
})
.get();
const { RowExistenceExpectation } = require('tablestore-js');
const res = await tj.row('sampleTable')
.primaryKey({
gid: 9,
uid: 90,
})
.rowExistenceExpectation(RowExistenceExpectation.IGNORE)
.update({
PUT: {
col4: 4,
col5: '5',
col6: 6,
},
DELETE: {
col1: 1496826473186,
},
DELETE_ALL: ['col2'],
});
const { RowExistenceExpectation } = require('tablestore-js');
const res = await tj.row('sampleTable')
.rowExistenceExpectation(RowExistenceExpectation.IGNORE)
.primaryKey({
gid: 8,
uid: 80,
})
.delete;
以下操作等同于官方文档 多行数据操作;
以下仅对接口操作封装,文档中重试逻辑需要自行实现;
const res = await tj.batch.getRow([
{
tableName: 'sampleTable',
primaryKey: [
{ gid: 20013, uid: 20013 },
{ gid: 20015, uid: 20015 },
],
startColumn: 'col2',
endColumn: 'col4',
},
{
tableName: 'notExistTable',
primaryKey: [{ gid: 10001, uid: 10001 }],
},
]);
const res = await tj.batch.writeRow([{
tableName: 'sampleTable',
rows: [{
type: 'PUT',
rowExistenceExpectation: RowExistenceExpectation.IGNORE,
returnType: ReturnType.Primarykey,
rowChange: {
gid: 8,
uid: 80,
attrCol1: 'test1',
attrCol2: 'test2',
},
}],
}]);
const res = await tj.batch.getRange({
tableName: 'sampleTable',
direction: TableStore.Direction.FORWARD,
inclusiveStartPrimaryKey: {
gid: INF.MIN,
uid: INF.MIN,
},
exclusiveEndPrimaryKey: {
gid: INF.MAX,
uid: INF.MAX,
},
limit: 50,
});
以下操作等同官方文档:
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.TERM_QUERY,
fieldName: 'Col_Keyword',
value: 'hangzhou',
}]);
const { QueryType } = require('tablestore-js');
res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.TERMS_QUERY,
fieldName: 'Col_Keyword',
value: ['hangzhou', 'shanghai'],
}]);
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.returnFields(['Col_1', 'Col_2', 'Col_3'])
.query([{
queryType: QueryType.MATCH_ALL_QUERY,
}]);
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.MATCH_QUERY,
fieldName: 'Col_Keyword',
value: 'hangzhou',
}]);
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.MATCH_PHRASE_QUERY,
fieldName: 'Col_Text',
value: 'hangzhou shanghai',
}]);
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.PREFIX_QUERY,
fieldName: 'Col_Keyword',
value: 'hang',
}]);
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.RANGE_QUERY,
fieldName: 'Col_Long',
options: {
rangeFrom: 1,
includeLower: true,
rangeTo: 10,
includeUpper: false,
},
}]);
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.WILDCARD_QUERY,
fieldName: 'Col_Keyword',
value: 'table*e',
}]);
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.GEO_BOUNDING_BOX_QUERY,
fieldName: 'Col_GeoPoint',
options: {
topLeft: '10,0',
bottomRight: '0,10',
},
}]);
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.GEO_DISTANCE_QUERY,
fieldName: 'Col_GeoPoint',
options: {
centerPoint: '1,1',
distance: 10000,
},
}]);
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.GEO_POLYGON_QUERY,
fieldName: 'Col_GeoPoint',
options: {
points: ['0,0', '5,5', '5,0'],
},
}]);
const { QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.query([{
queryType: QueryType.TERM_QUERY,
fieldName: 'Col_Nested.Sub_Col_Keyword',
value: 'hangzhou',
}]);
const { BoolQueryType, QueryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.boolType(BoolQueryType.MUST_QUERIES)
.minimumShouldMatch(1)
.query([
{
queryType: QueryType.RANGE_QUERY,
fieldName: 'Col_Long',
options: {
rangeFrom: 3,
includeLower: true,
},
},
{
queryType: QueryType.TERM_QUERY,
fieldName: 'Col_Keyword',
value: 'hangzhou',
},
]);
const { SortOrder } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.scoreSort(SortOrder.ASC)
.query();
const { SortOrder } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.primaryKeySort(SortOrder.DESC)
.query();
const { SortOrder } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.fieldSort({
Col_Keyword: SortOrder.DESC,
Col_Long: SortOrder.DESC,
})
.query();
const { SortOrder } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.geoDistanceSort({
fieldName: 'Col_Geo_Point',
points: ['0,0'],
order: SortOrder.ASC,
})
.query();
const { queryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(90)
.limit(10)
.query([{
queryType: QueryType.MATCH_ALL_QUERY,
}]);
const { queryType } = require('tablestore-js');
const res = await tj.search(TABLE_NAME)
.indexName(INDEX_NAME)
.offset(0)
.limit(10)
.token(null)
.returnFields(['pic_tag', 'pic_description', 'time', 'pos'])
.query([{
queryType: QueryType.MATCH_ALL_QUERY,
}]);