Skip to content

Latest commit

 

History

History
696 lines (549 loc) · 15.7 KB

README.md

File metadata and controls

696 lines (549 loc) · 15.7 KB

Build Status FOSSA Status NPM Package NPM Downloads

什么是表格存储

FOSSA Status

表格存储(Table Store)是阿里云自研的 NoSQL 多模型数据库,提供海量结构化数据存储以及快速的查询和分析服务。表格存储的分布式存储和强大的索引引擎能够支持 PB 级存储、千万 TPS 以及毫秒级延迟的服务能力。

为什么要选择阿里云表格存储

表格存储让我不需要关心运维层面的问题:可靠、安全、快速,并且非常灵活,无限无缝拓展,价格实惠;

引用官方,上面有更全面的说明:产品优势

为什么要自己写一个 SDK

自己在用表格存储的时候,觉得官方的 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 做了一层简易的封装,具体的一些改变如下:

  1. 使用 JSDoc 注释规范维护代码;

  2. getRowsearchbatchGetRowgetRange 方法上,返回的数据新增 data 字段,该字段由 rowrows 生成:

  • 将数据转换为 key:value 的格式;
  • Int64 对象类型转换为 Number 类型
  • Nested 字符串通过 JSON.parse 转换成对象
  • 生成 data 的过程不改变其他任何字段;
  1. primaryKey, attributeColumns 相关的传入都使用 key:value 方式传入,不用区分 primaryKey, attributeColumns

  2. 使用链式调用,不用关心参数结构;

  3. 添加更多默认值;

如何使用

准备工作

在使用之前,请确保已开通 表格存储,并已完成密钥配置。

参考:初始化

初始化

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
}

注意:参数的命名以 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,
});

多元索引查询操作

以下操作等同官方文档:

精确查询

TermQuery
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',
  }]);
TermsQuery
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'],
  }]);

匹配查询

MatchAllQuery
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,
  }]);
MatchQuery
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',
  }]);
MatchPhraseQuery
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',
  }]);

地理位置查询

GeoBoundingBoxQuery
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',
    },
  }]);
GeoDistanceQuery
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,
    },
  }]);
GeoPolygonQuery
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',
    },
  ]);

排序和翻页

查询时指定排序方式
ScoreSort
const { SortOrder } = require('tablestore-js');

const res = await tj.search(TABLE_NAME)
  .indexName(INDEX_NAME)
  .scoreSort(SortOrder.ASC)
  .query();
PrimaryKeySort
const { SortOrder } = require('tablestore-js');

const res = await tj.search(TABLE_NAME)
  .indexName(INDEX_NAME)
  .primaryKeySort(SortOrder.DESC)
  .query();
FieldSort
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();
GeoDistanceSort
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();
翻页方式
使用 limit 和 offset
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,
  }]);
使用 token 进行翻页
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,
  }]);

License

FOSSA Status