Apache Cassandraには、いろいろな言語のドライバーが実装されています。
今回は、この中でNode.jsのクライアントを試してみたいと思います。
GitHub - datastax/nodejs-driver: DataStax Node.js Driver for Apache Cassandra
DataStaxの提供するドライバーですね。
インストール
まずは、インストール。npmプロジェクトの配下で、以下を実行。
$ npm i --save cassandra-driver
これで、DataStaxのNode.jsドライバーが利用可能になります。
Apache Cassandra側の準備
Apache Cassandraを起動しておき、キースペースを作成します。
cqlsh> CREATE KEYSPACE IF NOT EXISTS my_keyspace WITH REPLICATION = { 'class': 'SimpleStrategy', 'replication_factor': 3 }; cqlsh> USE my_keyspace;
テーブルも作成しておきましょう。お題は書籍とします。
cqlsh:my_keyspace> CREATE TABLE book ( isbn text PRIMARY KEY, title text, price int);
また、今回はApache Cassandraにリモートから接続します。
DataStax Node.js Driverを使う
それでは、DataStaxのNode.js Driverを使ってコードを書いていきましょう。
基本的な使い方は、こちらにそのまま書いてあるというのと
DataStax Node.js Driver for Apache Cassandra / Basic usage
データタイプや
DataStax Nodejs Driver - CQL data types to JavaScript types
APIリファレンスを見ていけばだいたいOKでしょう(?)。
DataStax Nodejs Driver - API docs
Cassandraに接続する
では、始めていきます。最初に「cassandra-driver」をrequireして、それからClientを作成します。接続先は、今回は
「172.17.0.2」とキースペースは先ほど作成した「my_keyspace」としました。
const cassandra = require("cassandra-driver"); const client = new cassandra.Client({ contactPoints: ["172.17.0.2"], keyspace: "my_keyspace" });
データを登録してみる
Node.js Driverのプログラムは、Promiseを使って書くかコールバック関数を定義するかのどちらかのスタイルで
書くことになります。
今回は、Promoiseを使いましょう。
では、データ登録。対象のデータは、こちらとします。
const books = [ { isbn: "978-4873115290", title: "Cassandra", price: 3672 }, { isbn: "978-4798128436", title: "Cassandra実用システムインテグレーション", price: 3456 }, { isbn: "978-4798138824", title: "HBase徹底入門 Hadoopクラスタによる高速データベースの実現", price: 4298 } ];
それぞれ、登録してみます。
client .execute("INSERT INTO book (isbn, title, price) VALUES (:isbn, :title, :price)", books[0], { prepare: true }) .then(() => client.execute("INSERT INTO book (isbn, title, price) VALUES (?, ?, ?)", [books[1].isbn, books[1].title, books[1].price], { prepare: true })) .then(() => client.execute("INSERT INTO book (isbn, title, price) VALUES (:isbn, :title, :price)", books[2], { prepare: true }))
CQLの発行には、executeメソッドを使用します。
実は、今回全部PreparedStatementを使っています。PreparedStatementの書き方には2種類あって、「:名前」と「?」の
2つの書き方があります。
DataStax Node.js Driver for Apache Cassandra / Prepare your queries
まずは名前で指定する方法。
client .execute("INSERT INTO book (isbn, title, price) VALUES (:isbn, :title, :price)", books[0], { prepare: true })
第1引数がCQL、第2引数にバインドするパラメータを渡すわけですが、この時オブジェクトを渡します。その時の
メンバーの名前がバインドするパラメーターの名前と合っていればOKです。今回は、こちらですね。
{ isbn: "978-4873115290", title: "Cassandra", price: 3672 },
また、最後にオプションを指定するのですが、PreparedStatemtとして使う場合は以下の設定が必要です。
{ prepare: true }
続いて、「?」…要するにインデックス指定でパラメーターをバインドする方法です。この場合、バインドする
パラメーターを「?」の順番に配列として渡してあげればOKです。
client.execute("INSERT INTO book (isbn, title, price) VALUES (?, ?, ?)", [books[1].isbn, books[1].title, books[1].price], { prepare: true })
データを取得してみる
続いて、登録したデータを取得してみます。
SELECTの場合も、Clientのexecuteメソッドを使用します。
.then(() => client.execute("SELECT * FROM book")) .then(result => { console.log(result); result.rows.forEach(row => { console.log(">> isbn = " + row.isbn + ", title = " + row.title + ", price = " + row.price); }); })
Promise経由で結果が渡ってくるので、今回はそれぞれconsole.logで出力してみました。
今回の例だと、以下のコードからは
console.log(result);
こういう結果が出力され、
ResultSet { info: { queriedHost: '172.17.0.2:9042', triedHosts: {}, achievedConsistency: 10, traceId: undefined, warnings: undefined, customPayload: undefined }, rows: [ Row { isbn: '978-4798128436', price: 3456, title: 'Cassandra実用システムインテグレーション' }, Row { isbn: '978-4798138824', price: 4298, title: 'HBase徹底入門 Hadoopクラスタによる高速データベースの実現' }, Row { isbn: '978-4873115290', price: 3672, title: 'Cassandra' } ], rowLength: 3, columns: [ { name: 'isbn', type: [Object] }, { name: 'price', type: [Object] }, { name: 'title', type: [Object] } ], pageState: null, nextPage: undefined }
こちらのコードからは
.then(result => { console.log(result); result.rows.forEach(row => { console.log(">> isbn = " + row.isbn + ", title = " + row.title + ", price = " + row.price); }); })
こういう結果が得られます。
>> isbn = 978-4798128436, title = Cassandra実用システムインテグレーション, price = 3456 >> isbn = 978-4798138824, title = HBase徹底入門 Hadoopクラスタによる高速データベースの実現, price = 4298 >> isbn = 978-4873115290, title = Cassandra, price = 3672
ちゃんとデータが取得できていますね。PreparedStatementは使っていませんが、INSERTで使った時と同じ書き方でOKです。
この他、eachRowやstreamといったメソッドもあるようなので、興味のある方は見てみるとよいでしょう。
DataStax Node.js Driver for Apache Cassandra / Row streaming and pipes
データを削除する
登録したデータを削除します。
削除では、バッチ更新をしてみました。
.then(() => { const deleteCql = "DELETE FROM book WHERE isbn = :isbn"; const queries = books.map((b) => { return { query: deleteCql, params: b }}); return client.batch(queries, { prepare: true }); })
バッチ更新には、Clientのbatchメソッドを使用します。
DataStax Node.js Driver for Apache Cassandra / Batch multiple statements
batchメソッドに、「query」と「params」からなる配列を渡してあげればOKです。また今回はPreparedStatementを
使っているので、「prepare: true」も指定しています。
切断
最後は、Cassandraからの切断。
.then(() => client.shutdown());
Clientのshutdownメソッドを呼び出します。
まとめ
Apache CassandraにDataStax Node.js Driverを使ってアクセスしてみました。
自分がJavaScript、Node.jsに不慣れなのですが、ちょっとずつ慣れていきましょう。APIもそれほど難しいわけではないみたいですし。
最後に、今回作成したコード全体を載せておきます。
src/getting-started.js
const cassandra = require("cassandra-driver"); const client = new cassandra.Client({ contactPoints: ["172.17.0.2"], keyspace: "my_keyspace" }); const books = [ { isbn: "978-4873115290", title: "Cassandra", price: 3672 }, { isbn: "978-4798128436", title: "Cassandra実用システムインテグレーション", price: 3456 }, { isbn: "978-4798138824", title: "HBase徹底入門 Hadoopクラスタによる高速データベースの実現", price: 4298 } ]; client .execute("INSERT INTO book (isbn, title, price) VALUES (:isbn, :title, :price)", books[0], { prepare: true }) .then(() => client.execute("INSERT INTO book (isbn, title, price) VALUES (?, ?, ?)", [books[1].isbn, books[1].title, books[1].price], { prepare: true })) .then(() => client.execute("INSERT INTO book (isbn, title, price) VALUES (:isbn, :title, :price)", books[2], { prepare: true })) .then(() => client.execute("SELECT * FROM book")) .then(result => { console.log(result); result.rows.forEach(row => { console.log(">> isbn = " + row.isbn + ", title = " + row.title + ", price = " + row.price); }); }) .then(() => { const deleteCql = "DELETE FROM book WHERE isbn = :isbn"; const queries = books.map((b) => { return { query: deleteCql, params: b }}); return client.batch(queries, { prepare: true }); }) .catch(e => console.log(e)) .then(() => client.shutdown());