TableStoreの使い方

Alibaba CloudのNoSQLであるTableStoreを使います。

目標

以下に示すTableStoreの基本的な操作をnodejsから行います。

  • テーブルの新規作成
  • テーブル一覧の表示
  • 行の作成
  • 行の表示
  • 複数行の表示

TableStoreとは

Alibaba CloudのNoSQLデータベースのサービスです。なんかもう公式の概要がめちゃくちゃ分かりやすいのでこっち載せておきます。

Table Storeの概要 - プロダクト概要| Alibaba Cloud ドキュメントセンター

準備

ドキュメントが最強なので基本はドキュメントに沿って進めます。
初期化 - 開発ガイド| Alibaba Cloud ドキュメントセンター

まずはRAMのユーザーに適切な権限を付与しておいてください。

次にTableStoreに新しくインスタンスを作ります。

画面でポチポチしてインスタンスを作ります。 f:id:asmsuechan:20180709135153p:plain

「sampleStore」をクリックしてインスタンスの画面に入ります。 f:id:asmsuechan:20180709135131p:plain

次に、VPCをバインドすれば準備完了です。 f:id:asmsuechan:20180709135239p:plain

クライアントの作成

TableStoreの操作に使うクライアントオブジェクトを作成します。

// client.js

const TableStore = require('tablestore')

const instanceName = 'sampleStore'

const client = new TableStore.Client({
  accessKeyId: ’LTAxxxxxxxxxxxx’,
  secretAccessKey: ’Rztxxxxxxxxxxxxxxxxxxxx’,
  endpoint: `https://${instanceName}.cn-xxxxxx.ots.aliyuncs.com`,
  instancename: instanceName,
})

console.log(client)

node clinet.jsでこのプログラムを実行します。

$ node list.js
Client {
  config:
   Config {
     accessKeyId: 'LTAxxxxxxxxxxxx',
     secretAccessKey: 'Rzxxxxxxxxxxxxxxxxxxxxxxx',
     accessKeySecret: null,
     stsToken: null,
     securityToken: null,
     logger: null,
     endpoint: 'https://teststorage.cn-xxxxxxx.ots.aliyuncs.com',
     httpOptions: {},
     maxRetries: undefined,
     instancename: 'teststorage',
     computeChecksums: true } }

初期化 - 開発ガイド| Alibaba Cloud ドキュメントセンター

テーブルの新規作成(createTable())

sampleTableという名前のテーブルを作成します。プライマリキーにはid: INTEGERを設定します。

// createTable.js
const TableStore = require('tablestore')

const instanceName = 'sampleStore'

const client = new TableStore.Client({
  accessKeyId: ’LTAxxxxxxxxxxxx’,
  secretAccessKey: ’Rztxxxxxxxxxxxxxxxxxxxx’,
  endpoint: `https://${instanceName}.cn-xxxxxx.ots.aliyuncs.com`,
  instancename: instanceName,
})

const params = {
  tableMeta: {
    tableName: 'sampleTable',
    primaryKey: [
      {
        name: 'id',
        type: 'INTEGER'
      }
    ]
  },
  reservedThroughput: {
    capacityUnit: {
      read: 0,
      write: 0
    }
  },
  tableOptions: {
    timeToLive: -1,
    maxVersions: 1
  }
}

client.createTable(params, (err, data) => {
  if (err) {
    console.log('error:', err)
    return
  }
  console.log('success:', data)
})

今の仕様だと特に返ってくるものはありません。

$ node createTable.js
success: { RequestId: '00000000-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }

テーブル一覧の表示(listTable())

作ったテーブルの一覧を返します。

// listTable.js
const TableStore = require('tablestore')

const instanceName = 'sampleStore'

const client = new TableStore.Client({
  accessKeyId: ’LTAxxxxxxxxxxxx’,
  secretAccessKey: ’Rztxxxxxxxxxxxxxxxxxxxx’,
  endpoint: `https://${instanceName}.cn-xxxxxx.ots.aliyuncs.com`,
  instancename: instanceName,
})

client.listTable({}, (err, data) => {
  if (err) {
    console.log('error:', err)
    return
  }
  console.log('success:', data)
})

実行するとちゃんと先ほど作ったテーブルの名前が返ってきます。

$ node listTable.js
success: { table_names: [ 'sampleTable' ],
  RequestId: '00000000-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }

行の作成(putRow())

paramsのconditionにRowExistenceExpectation.IGNOREを指定すると、既に保存されている行の中に指定するprimaryKeyと同じものがある場合内容が上書きされます。

RowExistenceExpectation.EXPECT_NOT_EXISTだと同じものがない時のみデータが挿入されます。

primaryKeyは自動でインクリメントするかユニークキーを発行して欲しいのですが、ユーザー側でよしなにするしかないのでしょうかね

// createRow.js
const TableStore = require('tablestore')
const Long = TableStore.Long
const currentTimeStamp = Date.now()
const instanceName = 'sampleStore'

const client = new TableStore.Client({
  accessKeyId: ’LTAxxxxxxxxxxxx’,
  secretAccessKey: ’Rztxxxxxxxxxxxxxxxxxxxx’,
  endpoint: `https://${instanceName}.cn-xxxxxx.ots.aliyuncs.com`,
  instancename: instanceName,
})

const params = {
  tableName: 'sampleTable',
  condition: new TableStore.Condition(TableStore.RowExistenceExpectation.IGNORE, null),
  primaryKey: [{ 'id': Long.fromNumber(1) }],
  attributeColumns: [
    { 'name': 'Table Store' },
    { 'created_at': currentTimeStamp }
  ],
  returnContent: { returnType: TableStore.ReturnType.Primarykey }
}

client.putRow(params, (err, data) => {
  if (err) {
    console.log('error:', err)
    return
  }
  console.log('success:', data)
})

作ったデータの内容が返ってきます。

$  node createRow.js
success: { consumed: { capacity_unit: { read: 0, write: 1 } },
  row: { primaryKey: [ [Object] ], attributes: [] },
  RequestId: '00000000-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }

attributesが空なのはバグ?

行の表示(getRow())

conditionを使うと他の列での検索もできます。

// getRow.js
const TableStore = require('tablestore')
const Long = TableStore.Long
const currentTimeStamp = Date.now()
const instanceName = 'sampleStore'

const client = new TableStore.Client({
  accessKeyId: ’LTAxxxxxxxxxxxx’,
  secretAccessKey: ’Rztxxxxxxxxxxxxxxxxxxxx’,
  endpoint: `https://${instanceName}.cn-xxxxxx.ots.aliyuncs.com`,
  instancename: instanceName,
})

var params = {
  tableName: 'sampleTable',
  primaryKey: [{ 'id': Long.fromNumber(1) }],
  maxVersions: 2
}
client.getRow(params, (err, data) => {
  if (err) {
    console.log('error:', err)
    return
  }
  console.log('success:', data)
  console.log(data.row.attributes)
})

data.rowに取得した行が格納されています。

$ node getRow.js
success: { consumed: { capacity_unit: { read: 1, write: 0 } },
  row:
   { primaryKey: [ [Object] ], attributes: [ [Object], [Object] ] },
  next_token: null,
  RequestId: '00000000-xxxx-xxxx-xxxx-xxxxxxxxxxxx' }

[ { columnName: 'created_at',
    columnValue: 1531110991789,
    timestamp:
     Int64 { buffer: <Buffer 59 a9 53 7d 64 01 00 00>, offset: 0 } },
  { columnName: 'name',
    columnValue: 'Table Store',
    timestamp:
     Int64 { buffer: <Buffer 59 a9 53 7d 64 01 00 00>, offset: 0 } } ]

複数行の表示(batchGetRow())

複数行を表示するbatchGetRow()の処理は、ちょっとクセがあります。以下のコードは内容はサンプル通りなのですがちょっとJSっぽく書き換えています。

複数行操作 - 開発ガイド| Alibaba Cloud ドキュメントセンター

const TableStore = require('tablestore')
const Long = TableStore.Long
const currentTimeStamp = Date.now()
const instanceName = 'sampleStore'
const maxRetryTimes = 3
const retryCount = 0

const client = new TableStore.Client({
  accessKeyId: ’LTAxxxxxxxxxxxx’,
  secretAccessKey: ’Rztxxxxxxxxxxxxxxxxxxxx’,
  endpoint: `https://${instanceName}.cn-xxxxxx.ots.aliyuncs.com`,
  instancename: instanceName,
})

const params = {
  tables: [{
    tableName: 'sampleTable',
    primaryKey: [
      [{ 'id': Long.fromNumber(1) }]
    ],
    startColumn: 'created_at',
    endColumn: 'name'
  }]
}

function batchGetRow(params) {
  client.batchGetRow(params, (err, data) => {
    if (err) {
      console.log('error:', err)
      return
    }

    var isAllSuccess = true
    var retryRequest = { tables: [] }
    data.tables.forEach(tables => {
      let failedRequest = { tableName: tables[0].tableName, primaryKey: [] };
      tables.forEach(table => {
        // if the action is failed
        if (!table.isOk && table.primaryKey !== undefined) {
          isAllSuccess = false
          let pks = []

          table.primaryKey.forEach(primaryKey => {
            const name = primaryKey.name
            const value = primaryKey.value
            let kp = {}
            kp[name] = value
            pks.push(kp)
          })
          failedRequest.primaryKey.push(pks)
        }
      })
    })

    if (!isAllSuccess && retryCount++ < maxRetryTimes) {
      batchGetRow(retryRequest)
    }

    console.log(data.tables[0][0])
    console.log(data.tables[0][0].capacityUnit)
    console.log(data.tables[0][0].attributes)
    console.log(data.tables[0][0].primaryKey)
  })
}
batchGetRow(params, maxRetryTimes)

えっリトライ処理もユーザーが書かなきゃいけないの

めっちゃ薄いなあ

$ node getBatchRow.js
{ isOk: true,
  errorCode: null,
  errorMessage: null,
  tableName: 'sampleTable',
  capacityUnit: { read: 1, write: 0 },
  primaryKey: [ { name: 'id', value: [Int64] } ],
  attributes:
   [ { columnName: 'created_at',
       columnValue: 1531110991789,
       timestamp: [Int64] } ] }
{ read: 1, write: 0 }
[ { columnName: 'created_at',
    columnValue: 1531110991789,
    timestamp:
     Int64 { buffer: <Buffer 59 a9 53 7d 64 01 00 00>, offset: 0 } } ]
[ { name: 'id',
    value:
     Int64 { buffer: <Buffer 01 00 00 00 00 00 00 00>, offset: 0 } } ]

data.tables[0][0].attributesに取得したデータが入っています。

環境

$ node --version
v10.6.0

まとめ

公式ドキュメントが分かりやすくて特に詰まることなくできた。

正しいけど難しいことがダラダラ書かれているドキュメントより最低限のことがズバッと書かれているドキュメントの方が分かりやすい。