a minute ago

Cosmos DB入門


概要

基本はDynamoDBのようなNoSQLドキュメントなので、joinしなくてもいい(非正規化)、自己完結型のデータ設計をしていくのが良い。

SELECT文(WHERE, ORDER BY)や簡単な集計関数が使える。

参考になるリンク集

Node.js SDK使用方法

このCosmos DBをNode.jsのSDKを使って一通り触っていく。

Tips

Node.js用SDKでCosmos DBを操作する

Install

$ npm install @azure/cosmos

Quick Use

RDBと同じように、データベースがあり、その中にコンテナ(テーブルみたいなもの)がある。

const CosmosClient = require("@azure/cosmos").CosmosClient;
const endpoint = "your cosmosdb endpoint"
const key = "your cosmosdb primary key"
// client初期化
const client = new CosmosClient({ endpoint, key });
// DB選択
const database = client.database(databaseId);
// コンテナーを選択
const container = database.container(containerId);

// コンテナ内全取得
const querySpec = {
  query: "SELECT * from c"
};
const { resources: items } = await container.items
  .query(querySpec)
  .fetchAll();

// 新規作成
const { resource: createdItem } = await container.items.create(newItem);

// 更新
const { id, category } = createdItem;
createdItem.isComplete = true;
const { resource: updatedItem } = await container
  .item(id, category)
  .replace(createdItem);

// 削除
const { resource: result } = await container.item(id, category).delete();

参考

より詳細な使い方は公式を参照

チュートリアル1. Cosmos DBリソース作成

以下設定でcosmosDB作成

  1. Azure portal のメニューまたは [ホーム] ページで、 [リソースの作成] を選択します。
  2. [新規] ページで、 [Azure Cosmos DB] を検索して選択します。
  3. [Azure Cosmos DB] ページで、 [作成] を選択します。
  4. [Azure Cosmos DB アカウントの作成] ページで、新しい Azure Cosmos アカウントの基本的な設定を入力します。

公式

本記事を執筆時には以下の設定でDBを作成した。

基本:
  場所: 東日本
  アカウント名: (新規) {アカウント名を入力}
  API: Core (SQL)
  Account Type: Non-Production
  geo 冗長性: 無効
  マルチ リージョン書き込み: 有効
  Availability Zones: 無効
ネットワーク:
    Connectivity methodAll: networks
  1. [Review + create](レビュー + 作成) を選択します。 [ネットワーク] セクションと [タグ] セクションはスキップできます。
  2. アカウントの設定を確認し、 [作成] を選択します。 アカウントの作成には数分かかります。 ポータル ページに "デプロイが完了しました" と表示されるまで待ちます。
  3. [リソースに移動] を選択し、Azure Cosmos DB アカウント ページに移動します。
  4. Azure Cosmos DB アカウント ページに移動し、 [キー] を選択します。 次に作成する Web アプリケーションで使用する値をコピーします。

公式

チュートリアル2. Node.jsアプリ作成

下準備

$ npm install express-generator -g
$ express todo
$ cd todo
$ npm install
$ npm start

ここまで入力してブラウザでhttp://localhost:3000/を開くとExpressの初期画面が見える

Backend実装

$ npm install @azure/cosmos
$ mkdir model

models/taskDao.jsを作成し以下を貼り付け

const CosmosClient = require('@azure/cosmos').CosmosClient
const debug = require('debug')('todo:taskDao')

// For simplicity we'll set a constant partition key
const partitionKey = undefined
class TaskDao {
  /**
   * Manages reading, adding, and updating Tasks in Cosmos DB
   * @param {CosmosClient} cosmosClient
   * @param {string} databaseId
   * @param {string} containerId
   */
  constructor(cosmosClient, databaseId, containerId) {
    this.client = cosmosClient
    this.databaseId = databaseId
    this.collectionId = containerId

    this.database = null
    this.container = null
  }

  async init() {
    debug('Setting up the database...')
    const dbResponse = await this.client.databases.createIfNotExists({
      id: this.databaseId
    })
    this.database = dbResponse.database
    debug('Setting up the database...done!')
    debug('Setting up the container...')
    const coResponse = await this.database.containers.createIfNotExists({
      id: this.collectionId
    })
    this.container = coResponse.container
    debug('Setting up the container...done!')
  }

  async find(querySpec) {
    debug('Querying for items from the database')
    if (!this.container) {
      throw new Error('Collection is not initialized.')
    }
    const { resources } = await this.container.items.query(querySpec).fetchAll()
    return resources
  }

  async addItem(item) {
    debug('Adding an item to the database')
    item.date = Date.now()
    item.completed = false
    const { resource: doc } = await this.container.items.create(item)
    return doc
  }

  async updateItem(itemId) {
    debug('Update an item in the database')
    const doc = await this.getItem(itemId)
    doc.completed = true

    const { resource: replaced } = await this.container
      .item(itemId, partitionKey)
      .replace(doc)
    return replaced
  }

  async getItem(itemId) {
    debug('Getting an item from the database')
    const { resource } = await this.container.item(itemId, partitionKey).read()
    return resource
  }
}

module.exports = TaskDao

routes/tasklist.jsを作成し、以下を貼り付け

const TaskDao = require("../models/TaskDao");

 class TaskList {
   /**
    * Handles the various APIs for displaying and managing tasks
    * @param {TaskDao} taskDao
    */
   constructor(taskDao) {
     this.taskDao = taskDao;
   }
   async showTasks(req, res) {
     const querySpec = {
       query: "SELECT * FROM root r WHERE r.completed=@completed",
       parameters: [
         {
           name: "@completed",
           value: false
         }
       ]
     };

     const items = await this.taskDao.find(querySpec);
     res.render("index", {
       title: "My ToDo List ",
       tasks: items
     });
   }

   async addTask(req, res) {
     const item = req.body;

     await this.taskDao.addItem(item);
     res.redirect("/");
   }

   async completeTask(req, res) {
     const completedTasks = Object.keys(req.body);
     const tasks = [];

     completedTasks.forEach(task => {
       tasks.push(this.taskDao.updateItem(task));
     });

     await Promise.all(tasks);

     res.redirect("/");
   }
 }

 module.exports = TaskList;

プロジェクトルートにconfig.jsを作成し以下を貼り付け

const config = {};

config.host = process.env.HOST || "[the endpoint URI of your Azure Cosmos DB account]";
config.authKey =
  process.env.AUTH_KEY || "[the PRIMARY KEY value of your Azure Cosmos DB account";
config.databaseId = "ToDoList";
config.containerId = "Items";

if (config.host.includes("https://localhost:")) {
  console.log("Local environment detected");
  console.log("WARNING: Disabled checking of self-signed certs. Do not have this code in production.");
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
  console.log(`Go to http://localhost:${process.env.PORT || '3000'} to try the sample.`);
}

module.exports = config;

リソース作成時にメモしたURLをhostに、PRIMARY_KEYをauthKeyに記述

Frontend実装

views/layout.jadeを以下に書き換え

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='//ajax.aspnetcdn.com/ajax/bootstrap/3.3.2/css/bootstrap.min.css')
    link(rel='stylesheet', href='/stylesheets/style.css')
  body
    nav.navbar.navbar-inverse.navbar-fixed-top
      div.navbar-header
        a.navbar-brand(href='#') My Tasks
    block content
    script(src='//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.2.min.js')
    script(src='//ajax.aspnetcdn.com/ajax/bootstrap/3.3.2/bootstrap.min.js')

views/index.jadeを以下に書き換え

extends layout
block content
     h1 #{title}
     br

     form(action="/completetask", method="post")
      table.table.table-striped.table-bordered
         tr
           td Name
           td Category
           td Date
           td Complete
         if (typeof tasks === "undefined")
           tr
             td
         else
           each task in tasks
             tr
               td #{task.name}
               td #{task.category}
               - var date  = new Date(task.date);
               - var day   = date.getDate();
               - var month = date.getMonth() + 1;
               - var year  = date.getFullYear();
               td #{month + "/" + day + "/" + year}
               td
                if(task.completed) 
                 input(type="checkbox", name="#{task.id}", value="#{!task.completed}", checked=task.completed)
                else
                 input(type="checkbox", name="#{task.id}", value="#{!task.completed}", checked=task.completed)
       button.btn.btn-primary(type="submit") Update tasks
     hr
     form.well(action="/addtask", method="post")
       label Item Name:
       input(name="name", type="textbox")
       label Item Category:
       input(name="category", type="textbox")
       br
       button.btn(type="submit") Add item

再度npm startを実行するとタスク管理アプリが起動する。

参考

https://docs.microsoft.com/ja-jp/azure/cosmos-db/


Related Articles