DB2のNoSQLによるJSONサポート機能(第2部): コマンドライン・プロセッサーを使用する

DB2のNoSQLによるJSONサポート機能を使用したコマンドライン・インターフェースの設定方法と使用方法

アプリケーション環境が急速に変化するなか、さまざまなアプリケーション・レイヤー間でデータを保存・交換するための柔軟なメカニズムが必要となっています。JSON (Java Script Object Notation)はスキーマ設計のオーバーヘッドを削減し、データ加工を行う必要がないため、モバイル環境をサポートするインタラクティブなアプリケーションの構築のためには不可欠なテクノロジーとなっています。

DB2のNoSQLによるJSONサポート機能を活用すると、開発者はMongoDBに基づいて作成したポピュラーなJSONベースのクエリー言語を使用することで、IBM DB2 for Linux UNIX, and Windowsに保存されたデータを処理するアプリケーションを作成することができます。ドライバー・ベースの本ソリューションは実績のあるエンタープライズ機能と高い質のサービスを提供するRDBMSと連携し、JSONによるデータの柔軟な表示を実現します。DB2のNoSQLによるJSONサポート機能は、JSON文書を処理するにあたってコマンドライン・プロセッサー、Java API、および通信リスナーをサポートします。

本記事では、NoSQLアプリケーションをサポートするためにDB2データベースの設定を行い、独自のアプリケーションを開発することを目的としたNoSQLによるコマンドライン・プロセッサーの基本的な活用方法を確認します。

Marion Behnen, DB2 JSON and Spatial Development, IBM

Marion BehnenはIBMのソフトウェア・グループでシニア・ソフトウェア・エンジニアを務め、NoSQLによるJSONサポート機能を担当しています。NoSQLに取り組む前には、MarionはDB2アプリケーションとデータウェアハウス・アプリケーションのコンポーネントに関するテクニカル・リーダーを務めていました。IBMで勤務する以前は、Marionはビジネス・プロセスとデータ統合にさまざまな形で関わり、特に製造業のデータベース・アプリケーションの開発を担当していました。


developerWorks 貢献著者レベル

Tony Sun, Staff Software Engineer, IBM

Tony Sunは、IBMのソフトウェア・グループでソフトウェア・エンジニアとして勤務しています。IBMが提供するDB2のNoSQLによるJSONサポート・ソリューションを担当する前は、TonyはIBM PureQueryやIBM Optim Performance ManagerなどさまざまなDB2のランタイム製品を担当していました。



2013年 10月 21日

はじめに

DB2のNoSQLによるJSONサポート機能が、DB2 for Linux, UNIX, and Windows(バージョン10.5)で提供されます。そのテクノロジー・プレビューを図1に示します。

  • JSONデータの管理と検索を行うコマンドライン・シェル
  • アプリケーション開発をサポートする Java API
  • ネットワーク経由で送信されたリクエストの受け付けと対応を行う通信リスナー
図1. DB2によるJSONサポート機能のコンポーネント
図1. DB2によるJSONサポート機能のコンポーネント

本記事の目的と活用方法について

本記事では、DB2のNoSQLによる基本的な機能を使用して、JSONデータを管理しコマンドライン・シェルでクエリーを実行する方法について説明します。特に、以下のタスクの実行方法を説明します。

  1. セットアップを行う
  2. コレクションと文書を処理する
  3. 管理を行う
  4. クリーンアップを行う

本機能の概要やその他の機能に関する詳細情報が必要な場合は、本シリーズの他の記事をご参照ください。


セットアップを行う

システムの前提条件について

以下のステップを完了するには、システムにIBM DB2 10.5 for Linux, UNIX, and Windowsをインストール済みである必要があります。本記事に含まれるプログラミング・サンプルに関する権限の管理を簡略化するためには、使用するデータベースに関してDBADM権限を保有している必要があります。次のセクションで説明するデータベースを作成すると、必要な権限が自動的に割り当てられます。

データベースを作成する

このステップでは、以下のプログラミング・サンプルに適したデータベースを作成します。DB2のコマンド・ウィンドウまたはDB2のその他の管理機能を使用して、リスト1のとおりコマンドを実行します。

リスト1. データベースを作成するためのコマンド
CREATE DATABASE myjsondb 
       automatic storage yes 
       using codeset utf-8 territory US 
       collate using system  
       pagesize 32 k

さらに、データベース・サーバーのホスト名またはIPアドレスおよびポート番号が必要です。これらの情報は以下のステップで必要になります。

JSONのコマンドラインの処理環境を準備し、起動する

db2nosqlスクリプト(<db2home>/sqllib/json/binディレクトリーに存在)を使用してコマンドライン・プロセッサーを起動します。データベースの接続情報が必要です。依存関係に問題がないことを確認するために、以下を確認します。

  • PATHにJavaランタイム環境(JRE 1.5以上)が含まれていること
  • CLASSPATHにJDBCドライバー(db2jcc.jar or db2jcc4.jar)が含まれていること

このスクリプトでは、データベースがlocalhost:50000上で稼働していることを想定しています(–hostNameと-portのオプションを使用してその他のロケーションやポートを指定しないかぎり)。その他の詳細情報を確認するには、-helpオプションを使用してください(リスト2を参照)。

リスト2. db2nosqlスクリプトの使用例
db2nosql -help

db2nosql -db bobdb  -user bob -password mypassword

db2nosql -hostName bob.bobhome.com -port 50003 -db bobdb  -user bob -password mypwd

スクリプトの引数のプロンプト表示

  • データベース名は必ず必要です。データベース名が提供されない場合は、本スクリプトはデータベース名を要求するプロンプトを表示します。
  • ユーザー名が提供されるもののパスワードが提供されない場合は、本スクリプトはパスワードを要求するプロンプトを表示します。
  • ユーザー名もパスワードも提供されない場合は、本スクリプトはJDBC-Type2による接続を実行します。この場合、オペレーティング・システムのログイン・ユーザー名を使用します。

ヒント: 特定の設定のために処理環境を簡単に起動するには、本スクリプトをコピーし、スクリプトを編集して特定の値を入力してください。

JSONのコマンドライン・プロセッサーは"nosql"のプロンプトを表示します(リスト3のサンプルを参照)。

リスト3. コマンドライン・プロセッサー
nosql>Type your JSON query and end it with <ENTER>
nosql>Type help() or help for usage information

DB2データベースを有効化する

本スクリプトを実行し、コマンドラインの処理環境でenable(true)と入力することで、NoSQLコマンドに対応できるようデータベースを準備します。本ステップで必要となるシステム・オブジェクトを追加します。なお、本ステップはデータベースごとに1度だけ実行すれば済みます(リスト4を参照)。

リスト4. データベースを有効化する
nosql>enable(true)
Executing SQL...
Database Artifacts created successfully
nosql>

enable(false)オプションを使用すると関連するDDLが印刷されますが、コマンドは実行されません。

JSONの名前空間を選択する

DB2をNoSQLによるJSON文書のストアとして使用すると、DB2のSQLスキーマを修飾子として使用することで、複数のJSONの名前空間を定義できます。標準ではユーザー権限はJSONの名前空間に設定されますが、コマンドを使用する際にカスタム名を選択することもできます。新規のJSONの名前空間が選択されるまで(リスト5を参照)、JSONの名前空間(すなわちDB2のSQLスキーマ名)が現在有効なセッションのために使用されます。

リスト5. JSONの名前空間を設定する
nosql>use test
Switched to schema TEST

名前空間(スキーマ名)を設定する際には、大文字と小文字の区別が必要です。

現在のDB2データベースの接続を確認するには、nosqlのコマンドライン・プロセッサーでdbと入力します(リスト6を参照)。

リスト6. 現在のデータベース接続を表示する
nosql>db
Database: jdbc:db2://localhost:50000/myjsondb Schema: TEST

コマンドの前にdbを付加することで、選択した名前空間や名前空間に含まれるコレクションを処理することができます。dbのプレフィックスは便利なショートカット機能を提供し、コマンドを実行する際に該当する名前をプログラムに提供します。


コレクションと文書を処理する

明示的に作成したコレクションを使用して、文書の保存と検索を行う

DB2のNoSQLによるJSONサポート機能を使用すると、JSON文書はコレクション内に配置されます。NoSQLのコレクションはコレクション・スキーマや文書構造を必要としないものの、コレクションに含まれる文書は通常共通の特徴を共有することで、データの検索(および発見)が行いやすくなっています。

リレーショナル・テーブルとは異なり、コレクションについてテーブル構造を定義する必要はありません。文書を挿入するにはコレクションの名前を指定するだけでよく、コレクションが存在しない場合は、自動的に作成されます。その場合文書に識別子(_idの属性名が付いたフィールド)が含まれると、当該フィールドは一意性のある識別子として使用され、全ての新規の文書に同じデータ型の_idが含まれるものと考えられます。

最初の文書にこのような属性が含まれない場合は、システムは生成したオブジェクトの識別子を使用して、各文書を一意性のある文書として識別します。その後挿入した文書に_idフィールドが含まれる場合、データ型はVARCHAR(12) FOR BITフィールドに収まる必要があります。収まらなければ、当該文書は拒否されます。

文書を挿入するには、db.<collectionName>.insert(<document>)コマンドを使用します(リスト7を参照)。

リスト7. 文書を挿入する
nosql>db.books.insert({
          isbn: "123-456-789", 
          author: "Verne, Jules", 
          title: "Journey to the Center of the Earth", 
          abstract: "Classic science fiction novel in an unusual setting", 
          price: 6.00, 
          pages: 276, 
          category: "Fantasy",
          sales: 500.50
         })

上記のサンプル文書には、文字列(Verne, Julesなど)、数字(6.00など)、および整数(276など)の3種類のデータ型が含まれています。本文書には_id,の属性名が付いたフィールドが含まれないため、システムは生成したオブジェクトの識別子を使用して、本文書を一意性のある文書として識別します。

表1は、よく使用されるデータ型の一覧を示しています。完全な一覧を含む情報を参照したい場合は、DB2のNoSQLによるJSONサポートの参照資料をご覧ください。

表1. よく使用されるデータ型
データ型インデックスは使用できるか内容使用例
$string使用できる文字列
$int, $integer, $long使用できる整数、long$int: 123
$number使用できるDouble、float$number: 123.45
$date使用できる以下の形式を取る。'yyyy-mm-ttThh:mm:ssZ $date: '2013-05-18T18:56:00Z'
$timestamp使用できるTimestamp
$binary使用できるバイト配列
$oid使用できるオブジェクトの識別子(バイナリー)

find()コマンドを使用するとデータを検索できます(リスト8を参照)。サンプル文書の場合、booksという名前のコレクションを作成し、このコレクションにバイナリー・オブジェクトの識別子を自動的に生成しました。なお、コレクションの名前は大文字と小文字を区別します。

リスト8. 全ての文書を一覧で表示する
nosql>db.books.find()

nosql>Row 1:
nosql> {
nosql> "_id":{"$oid":"519b8727cd1552ed65b47a20"},
nosql> "isbn":"123-456-789",
nosql> "author":"Verne, Jules",
nosql> "title":"Journey to the Center of the Earth",
nosql> "abstract":"Classic science fiction novel in an unusual setting",
nosql> "price":6,
nosql> "pages":276,
nosql> "category":"Fantasy",
nosql> "sales": 500.5
nosql> }

新規の文書の_idのデータ型はコレクションの_idのデータ型(サンプルの場合はバイナリーのデータ型)と合致する必要があります。予測されるデータ型と合致しない_idを明示的に指定した新規の文書を挿入しようとすると、エラーが発生します(リスト9を参照)。

リスト9. 間違ったデータ型の_idを含む文書を挿入した場合には、エラーが発生する
nosql>db.books.insert({
   _id: "123-456-788", 
   author: "Verne, Jules", 
   title: "Journey to the Center of the Earth", 
   abstract: "Classic science fiction novel in an unusual setting", 
   price: 6.00, 
   pages: 276,
   category: "Fantasy",
   sales: 500.50
  })

nosql> Error:[nosql][1.0.146] DBException;
 Caused by: [jcc][1083][10403][3.66.33] Illegal conversion: can not convert from
 "java.lang.String" to "byte[]" ERRORCODE=-4474, SQLSTATE=null

明示的にコレクションを作成する

上記の例では、最初の文書に基づいて自動的にコレクションを作成しました。しかしながら自動的に作成したコレクションは標準設定を使用しているため、リスト10のとおりカスタム設定を使用して明示的にコレクションを作成するほうがよいこともあります。

リスト10. longのデータ型の_idを使用して、'media'という名前のコレクションを作成する
nosql>db.createCollection(“media”, {_id: “$long”)})
Collection: TEST."media" created. Use db.media.

文書の_id(一意性のある識別子として使用される)のみがコレクションの定義で指定されていることに注意してください。JSON文書はスキーマを使用しないため、文書のスキーマの定義は必要ありません。

また、DB2のフィーチャー(圧縮機能やタイム・トラベル照会機能など)を有効化し、テーブルスペースを割り当て、バッファー・プールの使用を制御するためのカスタム設定もできます。

テーブルスペースやバッファー・プールの管理機能、圧縮機能、およびタイム・トラベル照会機能に関するより詳細な情報を確認するには、参考文献セクションのDB2のインフォメーション・センターのリンクをクリックしてください。DB2のNoSQLによるJSONサポート機能に基づくアプリケーションの詳細情報については、本シリーズに含まれる他の記事をご参照ください。

文書をインポートする

単一の文書を挿入するコマンドを使用するだけでなく、ファイルからデータをインポートすることもできます。ダウンロード・セクションからサンプル・ファイルをダウンロードすることで、以下のステップを実行できます(リスト11を参照)。

リスト11. ファイルから文書をインポートする
nosql>db.books.importFile(“books_import.js”)
14 objects were imported

ヒント: コマンドが'file is not found'というエラー・メッセージを戻した場合、ファイルの絶対ロケーションを使用してください。

このインポート・コマンドを使用すると、コミットの頻度の設定もできます(リスト12を参照)。頻度を多くすることで、スループットが改善する場合があります。

リスト12. 文書を数回に分けてインポートする
nosql>db.books.importFile(“books_import.js”, 100)

インポートされたファイルには、JSONの表記方法に基づく適切な文書が含まれている必要があります。ファイルに不適切な文書が含まれていると、形式の正しくない文書ごとにエラーが報告されます。

NoSQLによるJSONベースのコレクションでは、コレクションのスキーマや文書構造は必要ありません。しかし、通常アプリケーションは文書の構造がある程度構造が共通していることを想定しています。つまり、NoSQLによるJSONベースのコレクションは全くスキーマを使用しないということではなく、柔軟性のある文書スキーマをサポートしています。文書で使用されている可能性がある構造に関する情報を確認するには、sampleSchema()コマンドを使用してください。本コマンドは一部の文書を分析することで、一連の属性と各属性の使用される回数を戻します(リスト13を参照)。

リスト13. 文書構造に関する情報を確認する
nosql>db.books.sampleSchema()

  {
  "._id":"15;type:ObjectId",
  ".abstract":"15;type:String",
  ".author":"15;type:String",
  ".category":"12;type:String",
  ".isbn":"15;type:String",
  ".pages":"14;type:Integer",
  ".price":"14;type:Double",
  ".sales":"13;type:Double",
  ".title":"15;type:String",
  ".year_published":"5;type:Integer"
  }

この例ではほとんどの属性は全ての文書に存在しているものの、category、sales、year_published、pages、およびpriceは全ての文書には存在していません。

JSON文書を検索する

db.<collection>.find(<conditions>, <projection list>)の検索コマンドを使用すると、条件を設定することで該当する文書を検索でき(リスト14を参照)、検索結果をカスタマイズできます(SQLのSELECT文と同様の機能を提供)。

リスト14. 特定の著者名の文書を検索する
nosql>db.books.find({author: "Tolkien, J.R"})

上記のサンプル・プログラムはJ.R. Tolkienによる書籍の全ての属性(自動的に生成された識別子)を戻します。プログラムが戻す文書の属性を指定するには、検索結果にどの属性を含めどの属性を除外すべきかを示すプロジェクション・リストを使用して検索を実行する必要があります。以下を使用します。

  • 1: 当該属性を検索結果に含める
  • 0: 当該属性を検索結果から除外する

ヒント: 同一のプロジェクションにおいては、属性を含めるか除外するかのいずれかを指定できます(両方を指定することはできません)。ただし、フィールドについては含めるべき属性と除外すべき属性(_id属性)の両方を指定できます(リスト15を参照)。

リスト15. 特定の文書の属性を選択する
Search for titles and prices of the documents with this author:
 
nosql>bdb.books.find({author: "Tolkien, J.R"}, {title: 1, price:1})


Search for titles and prices of the documents with this author in this category:

nosql>db.books.find({author: "Tolkien, J.R", category: “Fantasy”}, {title: 1, price:1})


Search for titles and prices of the documents with this author in this category, 
but exclude the _id:

nosql>db.books.find({author: "Tolkien, J.R", category: “Fantasy”}, 
{_id: 0, title: 1, price:1})

なお、_idは明示的に除外されない限り、自動的に検索結果に含まれることに注意してください。

比較演算子と結合演算子を使用することで、文書を選択することができます(リスト16)。

リスト16. 比較値を使用して文書を検索する
Search author, sales and price for books with sales less than 300, exclude the _id:

nosql>db.books.find({sales: {$lt:300}},{_id:0, author:1, sales:1, price:1})

よく使用される演算子は、表2のとおりです。

表2. 比較演算子と結合演算子
演算子内容使用例
$eq指定された値と合致するデータを検索しますauthor: { $eq : "Lindgren, Astrid" };
デフォルトでは、指定された値と合致するデータを検索しますauthor: "Lindgren, Astrid"
$le指定された値以下のデータを検索しますpages: { $le : 200 }
$lt指定された値より小さいデータを検索しますprice: { $lt : 6.50 }
$ge指定された値以上のデータを検索しますauthor.lastname: { $ge : "Doe" }
$gt指定された値より大きいデータを検索しますrating: { $gt : 3 }
$ne指定された値と合致しないデータを検索しますstatus: { $ne : 1 }
$in一連の値のいずれかの値を満たすデータを検索しますyear_published: { $in : [1990, 1991,1992] }
$nin一連の値のいずれの値も満たさないデータを検索しますyear_published: { $nin : [2012, 2013] }
$and両方の条件を満たすデータを検索します$and:[{"rating":5},{"category":"Mystery"}]
区切り記号としてコンマを使用すると、$andの意味になります"category":"Mystery","rating":5
$or指定された条件のいずれかを満たすデータを検索します$or:[{"rating":5},{"category":"Mystery"}]
$nor指定された条件のいずれも満たさないデータを検索します$nor:[{"rating":3},{"category":"Mystery"}]
$not否定(指定された条件に合致しないデータを検索します)$not:{"rating":1}

結果セットの制御オプション

値を見つけやすくするために最も高い値もしくは最も低い値から検索結果を表示したり、文書を並び替えるなど、検索結果をソートすることが役に立つことがあります。JSONクエリーの属性は、sort()関数で1つ以上の属性を指定することでソートすることができ、指定する際にはソートの方法として昇順(=1)または降順(= -1)を指定できます(リスト17を参照)。

リスト17. 書籍を検索し、検索結果をソートする
Search books with sales less than 300, lowest price first:

nosql>db.books.find({sales: {$lt:300}},
      {_id:0, author:1, sales:1, price:1}).sort({price: 1})


Search books with sales less than 300, 
sort on lowest price and largest number of pages within:

nosql>db.books.find({sales: {$lt:300}},
              {_id:0, author:1, pages:1, price:1}).sort({price: 1, pages: -1})

多くの文書が検索条件に合致する場合は、アプリケーションの内容によっては検索結果の一部を抽出するだけで十分な場合があります。戻される文書の最大数を指定するには、クエリー実行の際にlimit()関数を使用します(リスト18を参照)。

リスト18. 検索結果の件数を制限する
nosql>db.books.find({sales: {$lt:300}},{_id:0, author:1, pages:1, sales:1}).limit(3)

結果セットの件数を制限する機能は、skip()関数を使用してクエリーにオフセット追加することで大量の結果セットを簡単に参照する際にも活用できます(リスト19を参照)。

リスト19. 大量の検索結果を簡単に参照する
nosql>db.books.find({sales: {$lt:300}},
     {_id:0, author:1, pages:1, sales:1}).limit(3).skip(3)

検索結果として1件のデータのみが必要な場合は、findOne()関数を使用できます(リスト20を参照)。

リスト20. 最初に条件に合致した文書のみを参照する
nosql>db.books.findOne({author: "Tolkien, J.R"}, {title: 1, price:1})

データを集計する

文書の数が多くなると、一連の文書に関する検索結果の集計が必要になることがあります。このためには、さまざまな関数を使用することで文書の数を数えたり、一連の文書に関する個別の値や集計値を計算することができます。

特定の検索条件に合致する文書の数を数えるには、count()関数を使用します(リスト21を参照)。

リスト21. 文書の数を数える
Get the total count of documents in this collection:
 
nosql>db.books.count()
15

Get the number of documents for this author:

nosql>>db.books.count({author: "Tolkien, J.R"})
2

一連のデータに含まれる属性に関する個別の値を参照するには、当該属性に対してdistinct()関数を使用します(リスト22を参照)。

リスト22. 個別の値を参照する
nosql>db.books.distinct("author")

クエリーを実行する場合や、複数のキーの値を使用する場合は、本シリーズの次の記事(「DB2のNoSQLによるJSONサポート機能(第3部): Java APIを使用する」)をご参照下さい。

count()関数とdistinct()関数は特定の目的に役立つ便利な関数で、より一般的な目的のために使用されるgroup()関数の代わりに使用することができます。例えば、属性に対してgroup()関数を使用することで個別の値を参照することができます(リスト23を参照)。

リスト23. group()コマンドを使用することで、個別の値を参照する
nosql>db.books.group({"_id": {"author": 1}})

さらにgroup()関数を使用すると、グループ化のための複合的なキーを指定し、データ集計の関数を定義でき、合計値や平均値などを計算することができます(リスト24を参照)。

リスト24. 著者ごとに書籍の平均価格を計算する
nosql>db.books.group({"_id": {"author": 1}})

より複雑なクエリーに対してはaggregate()関数の使用ができます。本関数を使用すると一連のステップに基づいてクエリーを記述できるため、途中の検索結果をその後のステップの入力データとして使用ができます。例えば、著者ごとの平均価格を計算する際に、以下のクエリーを使用してFantasyのカテゴリーに含まれる全ての文書を選択し、その後文書から著者と価格の属性を選択し、著者別に平均価格を計算するなどです(リスト25を参照)。なお、以下のプログラムは参照しやすいように書式を整えてあります。

リスト25. あるカテゴリーにおいて著者別に書籍に平均価格を計算する
nosql>db.books.aggregate(
      {$match:   { category: "Fantasy" }}, 
      {$project: {author:1, price:1}},
      {$group:   {_id: {author:1}, avgPrice:{$avg:"$price"}}} 
   )

nosql>Row 1:
nosql> {
nosql> "_id":"Tolkien, J.R",
nosql> "avgPrice":15.5
nosql> }
nosql>Row 2:
nosql> {
nosql> "_id":"Verne, Jules",
nosql> "avgPrice":6.35
nosql> }
nosql>2 rows returned

$projectのタスクにおいては、四則演算、文字列、または時間に関する演算子を使用することができます。以下のサンプル・プログラムは$divide関数を使用することで、$groupタスクで著者別に合計値と平均値を計算した後に1ページごとの価格を計算しています(リスト26を参照)。

リスト26. 著者と1ページごとの価格を昇順で表示する
nosql>db.books.aggregate(
    {$group: {"_id": {author:1},  "sumSales":{"$sum": "$sales"}, 
                avgPages: {"$avg": "$pages"},  
                avgPrice: {"$avg": "$price"} }},
    {$project: {author: "$_id.author", sumSales: 1, 
                pricePerPage:  {$divide: ["$avgPages", "$avgPrice"]}}
    {$sort: {pricePerPage: 1}}
  )

ヒント: 関数内に実際の属性名や計算した属性名を記載する際には、属性名の前に$を付けダブル・クオテーションで囲んでください(例: $divide関数で記載された"$avgPrice")。

このデータ集計関数には1つ以上のタスクが含まれる場合があり、必ずしも$groupタスクが含まれる必要はありません。例えば、リスト27のとおり、aggregate()関数を単純なプロジェクション・タスクに使用することもできます(値を修正する場合もあれば、しない場合もあります)。

リスト27. 属性の部分文字列を選択する
nosql>db.books.aggregate({"$project": {author:1, title:1, 
                 shortabs: {"$substr": ["$abstract",0,10]} } })

データ集計タスクを使用すると、検索条件に合致するフィールドのみを指定することができます。プロジェクションやグループ化のステップで属性を明示的に選択する場合は、事前のステップで参照名を割り当てることで、次のステップで属性を使用することができます。その他のフィールドは全て無視されます。

ヒント: aggregate()関数は指定された属性のみを戻します。find()関数とは異なり、$projectタスクにおいて生成された_idを自動的に戻すことはしません。

ヒント: 本テクニカル・レビューでは、計算したフィールドやリネームしたフィールドをまず記載した後で、その後のタスクでこれらのフィールドを参照するようにしてください。

表3は、該当するタスクを一覧で表示しています。

表3. データを集計するタスク
$group合計値や平均値の計算などのデータ集計処理を行います。1つ以上の属性に基づいて構築したグループ・キーが必要です。
$limit 検索結果として戻される文書の数を制限します。
$match 検索条件を使用することで、文書の一部を選択します。
$project ある文書のフィールドを選択し、参照名を割り当てることができます(オプション)。
$skip オフセットを使用して、ページの範囲を設定します。
$sort 特定の属性に関する検索結果をソートします(昇順の場合は1、降順の場合は-1を指定)。

表4は、データ集計を行う演算子を一覧で表示します。使用方法の詳細を確認する必要がある場合は、DB2のNoSQLによるJSONサポート機能の参照資料をご覧ください。

表4. データ集計を行う演算子
データ型演算子
$string$concat, $max, $min, $substr, $toLower, $toUpper
$date, $timestamp$year, $month, $week, $hour, $minute, $second, $dayOfYear, $dayOfMonth, $dayOfWeek
$int, $number$add, $divide, $multiply, $subtract, $mod, $min, $max, $avg, $sum,$inc

ヒント: 特定の演算子がサポートされていないことを示すエラーが表示された場合は、構文を検証し、{operator: {field: value}}のシーケンスが存在していることを確認してください。例: {“$gt”: {rating: 1}}

文書を更新する

update()関数は1つ以上の文書に含まれるデータを更新します。本関数では、更新のスコープを決定するいくつかの引数を使用することができます。本関数を使用する際には、以下のとおり記載します。

update(<条件>, <更新対象のフィールド>, <upsertFlag>, <multiRowFlag>)

注意: 新規のフィールドの値を指定する場合で、文書内の指定したフィールドのみを更新しその他のフィールドを変更したくない場合は、$set演算子を必ず使用してください。この演算子を使用しないと、文書のコンテンツは指定したフィールドで上書きされてしまいます(リスト28およびリスト29を参照)。

リスト28. $set演算子を使用したうえで、isbnを指定することで文書を更新する
nosql>db.books.find({isbn: "123-456-234"}, {_id:0, isbn:1, author:1, title:1, pages:1})
Row 1:
{
 "isbn":"123-456-234",
 "author": "Verne, Jules",
 "title":"Journey to the Center of the Earth",
 "pages":276
 }

nosql>db.books.update({isbn: "123-456-234"}, {$set: {pages: 299}})
Updated 1 rows.

nosql>db.books.find({isbn: "123-456-234"}, {_id:0, isbn:1, author:1, title:1, pages:1})
Row 1:
{
 "isbn":"123-456-234",
 "author": "Verne, Jules",
 "title":"Journey to the Center of the Earth",
 "pages":299
 }
リスト29. $set演算子を使用せずに文書を更新する
db.books.update({isbn: "123-456-234"}, {isbn: "123-456-234" , price: 6.50})
Updated 1 rows.

nosql>db.books.find({isbn: "123-456-234"}, {_id:0, isbn:1, author:1, title:1, price:1})

 Row 1: {
 "_id":{"$oid":"519e2bfb37a817b9cba77ab0"},
 "isbn":"123-456-234",
 "author": null,
 "title": null,
 "price":6.50
 }

更新のスコープを決定するために使用できるその他のオプションとしては、upsertフラグとmultiRowフラグが挙げられます。upsertフラグを使用すると、欠けている文書を挿入すべきか無視すべきかを指定できます。すなわち、本フラグを使用することで、コレクション内に特定の識別子に合致するデータが存在しない場合にシステムがどのような対応を取るべきかを指定することができます。

  • upsertフラグをtrueに設定すると、欠けている文書が挿入されます。
  • 本フラグをfalseに設定すると、更新は拒否されます。

さらにmultiRrowフラグを使用すると、更新を検索条件に合致する最初の文書にのみ適用すべきか、それとも検索条件に合致する全ての文書に適用すべきかを決定することができます(リスト30を参照)。

リスト30. 文書を更新するオプションの例
Do not insert if no matching document exists, 
update the first document that matches the query:

nosql>db.books.update({author: “Climber, Joe”}, {$set {price: 9.99}}, false, false)


Do not insert if no matching document exists, update all documents that match the query:

nosql>db.books.update({author: “Climber, Joe”}, {$set {price: 9.99}}, false, true)

save()メソッドは挿入機能と更新機能を合わせて提供し、文書の識別子を使用して挿入・更新のスコープ を決定します。文書に_idが含まれる場合は、当該文書が挿入・更新されます。 文書に_idが含まれない場合は、新規の識別子が生成され、当該文書が挿入されます。

以下の例では、書籍に関する文書に_idが含まれないため、新規の識別子が生成され、新規の書籍がコレクションに挿入されます(リスト31を参照)。

リスト31. 文書を保存する
nosql>db.books.save({isbn: "123-456-239", 
              "author": "Verne, Jules",  "title": "Mysterious Island" })"

インデックスを活用する

属性をクエリーの検索基準やソート基準として使用する際には、インデックスを作成することで大規模なデータ・ワークロードのデータ抽出時間の短縮ができます。JSON属性に対するインデックスは1つ以上の属性に対し作成でき、昇順または降順でソートすることができます。インデックスを指定しない場合は、標準の名前が割り当てられます。インデックスを作成するには、ensureIndex()コマンドを使用します(リスト32を参照)。

リスト32. インデックスを作成する
Create an index on field 'author' in ascending order, 
     using the default type string with default length 50:

nosql>db.books.ensureIndex({"author": 1})
Index <books_xauthor> was created successfully.


Create an index on field 'category' with type string and field length 40:

nosql>db.books.ensureIndex({"category": [1, "$string", 40]})
Index <books_xcategory> was created successfully.

 
Create an index on field price with type number in descending order, name it 'mypriceidx':

nosql>db.books.ensureIndex({"price": [-1, "$number"]}, “mypriceidx”)
Index <mypriceidx> was created successfully.

getIndexes()コマンドを使用すると、コレクションに対して設定したインデックスの一覧を参照することができます。なお、_id属性に対しては一意性インデックスが自動的に作成されます(リスト33を参照)。

リスト33. 識別子に対して設定されたインデックスを含むインデックス情報(書式を整えたもの)
nosql>db.books.getIndexes()

[{"v":0,"key":{"_id_":1},"ns":"TEST.books","name":"_id_","unique":true}, 
{"v":1,"key":{"author":1},"ns":"TEST.books","name":"books_xauthor","unique":false},
{"v":2,"key":{"category":1},"ns":"TEST.books","name":"books_xcategory","unique":false}, 
{"v":3,"key":{"price":1},"ns":"TEST.books","name":"mypriceidx","unique":false}]

属性に対して一意性インデックスを作成するには、一意性フラグを使用します。システムは一意性インデックスを作成しようとします。コレクションに既に文書が含まれている場合は、指定したフィールドに重複データが存在していないことを確認する必要があります(リスト34を参照)。

リスト34. 重複データに対して一意性インデックスを作成すると、エラーが発生する
nosql>db.books.ensureIndex({"isbn": [1, "$string", 30]}, "myisbnix", true)

nosql> Error:[nosql][1.0.66] Failed to create index. ;
 Caused by: A unique index cannot be created because the table contains data that would 
 result in duplicate index entries.. SQLCODE=-603, SQLSTATE=23515, DRIVER=3.66.33

重複データを検出するには、データ集計クエリーを活用することができます(リスト35を参照)。

リスト35. 重複データを検出するクエリーの例
nosql>db.books.aggregate({"$group": {_id: "isbn", countdocs: {"$sum": 1}}}, 
    {"$project": {"grouped_isbn": "_id", "countdocs": 1}}, 
    {"$match": {"countdocs": {$gt: 1}}})

ネストされたオブジェクト

文書にはネストされたオブジェクトが含まれる場合があります。例えば、著者情報を含む単一の文字列ではなく、リスト36のとおり姓と名に分けて情報が文書に含まれる場合があります。(ダウンロード・セクションで提供されるbooks_importNested.jsを参照すること)。

リスト36. ネストされたオブジェクトを含む文書
{
  isbn: "123-456-234", 
  author: 
      {
         lastname: "Verne", 
         firstname: "Jules"
      }, 
  title: "Journey to the Center of the Earth", 

  sales: 333.0
  })

文書に含まれる各要素は、個別に識別可能な情報である必要があります。例えば、"title"という要素は一度だけ使用でき、"author.lastname"という要素に関しても同様です(リスト37を参照)。しかしながら属性名が異なるパスに存在する場合は、同じ属性名になる場合があります。したがって、属性名が文書内の別の絶対パスを有するかぎり、"lastname"に対して別の属性名を設定する必要があります("editor.lastname"など)。

リスト37. ネストされた属性を使用して文書のインポートと検索を行う
nosql>db.booksnest.importFile(“books_importNested.js”)

nosql>db.booksnest.findOne()
{
 "_id":{"$oid":"51b7a63d3503e8eca2e84556"},
 "isbn":"122-456-789",
 "author":{"lastname":"Tolkien","firstname":"J.R"},
 "title":"The Hobbit",
 "abstract":"Spiders and Dragons",
 "category:["Fantasy", "Fiction"],
 "price":5.0,
 "pages":216,
 "sales":800.8
 }

ネストされたオブジェクト検索する場合は、属性の完全なパスを指定する際にピリオドを使用して記載する必要があります。例えば、著者の姓を検索する場合は、"author.lastname"と記載します(リスト38を参照)。

リスト38. ネストされたオブジェクトを検索する
nosql>db.booksnest.find({"author.lastname": "Verne"})

注意: フィールド名は必ずダブル・クオテーションで囲んでください。

配列

文書には配列の形式で単一のオブジェクトが複数回現れることがあります。例えば、あるユーザーは複数の電話番号や電子メールアドレスを持ち、ある書籍は複数のカテゴリーに属し、複数の著者が存在している場合があります(リスト39を参照)。

リスト39. 配列を使用して複数のカテゴリーを表示する
nosql>db.booksnest.insert({isbn: "876-543-321", 
           author: {lastname: "Doe", firstname: "John" },
           title: "A new book", 
           abstract: "The most curious items", 
           pages: 111,
           category: ["Humor", "YA"]
         })

配列の位置を指定せずに属性を検索すると、全ての位置の値が検索されます(リスト40を参照)。

リスト40. 配列に含まれる値を検索する
nosql>db.booksnest.find({category: "Humor"})

nosql>Row 1:
nosql> {
nosql> "_id":{"$oid":"51a293e437a81d6b7f8c1971"},
nosql> "isbn":"876-543-321",
nosql> "author{lastname: "Doe", firstname: "John"},
nosql> "title":"A new book",
nosql> "abstract":"The most curious items",
nosql> "pages":111,
nosql> "category":["Humor", "YA"]
nosql> }

ただし、ピリオドを使用して属性の配列上の位置を追加することで、特定の位置を検索することができます(リスト41を参照)。

リスト41. 配列上の特定の位置に存在する配列の値を検索する
nosql>db.booksnest.find({"category.0": "Humor"})
nosql>Row 1:
nosql> {
nosql> "_id":{"$oid":"51a293e437a81d6b7f8c1971"},
nosql> "isbn":"876-543-321",
nosql> "author{lastname: "Doe", firstname: "John"},
nosql> "title":"A new book",
nosql> "abstract":"The most curious items",
nosql> "pages":111,
nosql> "category":["Humor", "YA"]
nosql> }
nosql>1 row returned 

nosql>db.booksnest.find({"category.1": "Humor"})
nosql>0 rows returned

ヒント: 配列の最初の位置は0から始まっていることに注意してください。

配列に含まれるデータに対してコマンドや関数を使用する際の最新の詳細情報については、DB2のNoSQLによるJSONサポート機能に関する参照資料(配列の処理に関する資料)をご覧ください。


管理を行う

コレクションを管理する

JSONの名前空間に含まれる全てのコレクション名を表示するには、getCollectionNames()コマンドを使用します。サンプル・プログラムのJSONの名前空間には、リスト42のとおり3つのコレクションが存在しています。

リスト42. 現在のコレクションを表示する
nosql>db.getCollectionNames()
[media, books, booksnest]

コレクションのリネームを行う場合(コレクションを使用し続けるものの、既存のコンテンツをアーカイブしたい場合など)、rename()コマンドを使用して新しい名前を指定します(リスト43を参照)。

リスト43. 既存のコレクションをリネームする
nosql>db.books.rename(“oldbooks”)

同じ名前のコレクションが既に存在している場合は、本コマンドは失敗します。しかしながら、既存のコレクションを強制的に削除するフラグを指定することで、コレクションのリネームができます。本サンプル・プログラムの場合、"oldbooks"という名前のコレクションが既に存在している場合は、db.books.rename(“oldbooks”, true)というコマンドを実行することで、まず"oldbooks"というコレクションを削除しその後"books"というコレクションを"oldbooks"にリネームします。

統計情報

stats()コマンドは、コレクションに関する情報(特に名前空間、文書の数、インデックス情報など)を戻します。さらに統計情報として、文書の平均サイズやコレクションのトータル・サイズに関する情報も含まれます(リスト44を参照)。本情報はDB2の統計情報から生成され、一定の遅延は発生するものの情報を更新することができます。使用できない値として、-1が設定されています。

リスト44. コレクションの統計情報を取得する(出力書式を簡略化したもの)
nosql>db.books.stats()

  {
  "ns":"TEST.books",
  "count":15,
  "size":4.5263671875,
  "avgObjSize":309.0,
  "numExtents":-1,
  "nindexes":2,
  "totalIndexSizes":-1,
  "indexSizes":[{"books_xauthor": -1},{"_id_":-1}],
  "ok":1
  }

データ、インデックス、およびコレクションを削除する

コレクションから1つ以上の文書を削除する場合は、remove()関数を使用します。全ての文書を削除する(インデックスの定義は維持されます)こともできれば、条件を設定することで一部の文書削除もできます(リスト45を参照)。

リスト45. 文書を削除する
nosql>db.books.remove({isbn: "123-456-789"})

インデックスの必要がなくなった場合や、異なる特徴を持つインデックスを再作成したい場合は、インデックスを削除することができます。その際インデックス名を参照するか、当該インデックスを作成するために使用されたインデックスの特徴を指定します(リスト46を参照)。

リスト46. インデックスを削除する
nosql>db.books.removeIndex("mypriceidx")
Index <mypriceidx> was removed successfully.

nosql>db.books.removeIndex({"author": 1})
Index <books_xauthor> was removed successfully.

コレクションから全てのデータを削除するものの、空のコレクションと関連するインデックスをそのまま維持する場合は、remove()コマンドを使用します(リスト47を参照)。

リスト47. コレクションを削除する
nosql>db.books.remove()
OK
nosql>db.books.getIndexes()
[{"v":0,"key":{"_id_":1},"ns":"TEST.books","name":"_id_","unique":true}, 
{"v":1,"key":{"pages":1},"ns":"TEST .books","name":"books_xpages","unique":false}, 
{"v":2,"key":{"isbn":1},"ns":"TEST .books","name":"myisbnix","unique":true}]

コレクションと全てのインデックスを削除する場合は、当該コレクションに対してdrop()コマンドを実行します(リスト48を参照)。

リスト48. コレクションを削除する
nosql>db.books.drop()
OK
nosql>db.books.getIndexes()
[]

クリーンアップを行う

特定のJSONの名前空間から全てのコレクションを削除する場合は、dropAllCollections()コマンドを使用することができます(リスト49を参照)。JSONの名前空間にユーザーが必要となる削除権限を持たないコレクションが含まれている場合は、当該コレクションに関するエラー・メッセージが印刷されます。

リスト49. JSONの名前空間から全てのコレクションを削除する
nosql>db.dropAllCollections()

コレクションの強制削除を行う場合や、NoSQLによるJSONのシステム・カタログを削除する場合は、trueの値を指定したうえでdisable()コマンドを使用します(リスト50を参照)。dropAllCollections()コマンドの場合と同様に、全ての関連するオブジェクトを削除するには適切な権限が必要です。

リスト50. NoSQLによるJSONのフィーチャーを無効化する
リスト50. NoSQLによるJSONのフィーチャーを無効化する

falseオプションを指定したうえでdisable()コマンドを使用する場合は、DDLステートメントは印刷されますが、実行はされません。


結論

本記事はNoSQLによるJSONのコマンドライン環境の設定方法を説明し、JSON文書を保存・検索するためにコレクションを処理する基本的なコマンドをいくつか紹介しました。DB2のNoSQLによるJSONサポート機能の詳細、Java APIの使用方法、通信リスナーを使用してリクエストの受付と処理を行う方法を確認するには、本シリーズに含まれる別の記事をご参照ください。コマンドと演算子に関する詳細情報が必要な場合は、DB2のNoSQLによるJSONサポート機能に関する参照資料をご覧ください。


ダウンロード

内容ファイル名サイズ
Sample JSON documentsbooks_import.zip2KB
Sample JSON documents with nestingbooks_importNested.zip2KB

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Information Management, Java technology, Open source
ArticleID=947456
ArticleTitle=DB2のNoSQLによるJSONサポート機能(第2部): コマンドライン・プロセッサーを使用する
publish-date=10212013