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 功能支持命令行处理器、Java API 和处理 JSON 文档的 Wire Listener。

在本文中,您将设置一个 DB2 数据库来支持 NoSQL 应用程序,本文将通过一个场景来介绍 NoSQL 命令行处理器的基本特性,帮助您开始创建自己的应用程序。

Marion Behnen, DB2 Text Search 开发, IBM China

Marion Behnen 是 IBM Software Group 的高级软件工程师,担任 DB2 全文本搜索技术主管。她拥有超过 20 年的数据库应用程序开发、企业报告、数据仓库和业务流程集成的经验。在加入 IBM 之前,她参与过与业务流程和数据集成相关的各个方面的工作,尤其是制造业。


developerWorks 投稿作者

Tony Sun (tonysun@us.ibm.com), Staff Software Engineer, IBM China

Tony Sun is the primary QA engineer for the pureQuery runtime product.



2013 年 9 月 16 日

开始之前

DB2 NoSQL JSON 是 DB2 for Linux, UNIX, and Windows 10.5 中提供的一个技术预览版,它提供了以下特性,如图 1 所示:

  • 一个管理和查询 JSON 数据的命令行 shell。
  • 一个用于应用程序开发的 Java™ API。
  • 一个接受并响应通过网络发送的请求的 Wire Listener。
图 1. DB2 JSON 组成部分
应用程序使用 Wire Listener 或 Java API 来处理数据库中的 JSON 文档。命令行接口也使用了 Java API。

了解能从本文学到什么以及如何最充分地掌握本文中的知识

本文将介绍 DB2 NoSQL 在命令行 shell 中管理 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 或 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 命令准备数据库。这一步将添加必要的系统对象,一个数据库只需要创建一次,如清单 4 所示。

清单 4. 启用数据库
nosql>enable(true)
Executing SQL...
Database Artifacts created successfully
nosql>

使用 enable(false) 选项将会打印有关联的 DDL,但不会执行该命令。

选择 JSON 命名空间

使用 DB2 作为 NoSQL JSON 文档存储,这样就可以使用 DB2 SQL 模式作为限定符来定义多个 JSON 命名空间。在默认情况下,用户授权被设置为 JSON 命名空间,但也可使用命令 use 来选择一个自定义名称。在选择新 JSON 命名空间之前,这个 JSON 命名空间(也即 DB2 SQL 模式名称)将用于活动的会话,如清单 5 所示。

清单 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
         })

前面的示例文档包含 3 种不同的数据类型:字符串(比如 Verne、Jules)、数字(比如 6.00)和整数(比如 276)。因为它不包含标记了属性名称 _id 的字段,所以系统使用了一个已生成的对象标识符来惟一标识该文档。

表 1 包含一组常见的数据类型。请参阅 DB2 NoSQL JSON 参考文档,了解完整的数据类型列表。

表 1. 常见的数据类型
数据类型是否允许索引备注示例
$string字符串
$int、$integer、$long整数、长整型$int: 123
$number双精度、浮点数$number: 123.45
$date必须具有以下格式:'yyyy-mm-ttThh:mm:ssZ $date: '2013-05-18T18:56:00Z'
$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. 创建一个名为 “media” 且具有 _id 类型 long 的集合
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>) 允许通过指定条件来选择您感兴趣的文档(类似于 SQL where 子句),如清单 14 所示,还支持调节结果输出(类似于一个 SQL select 语句)。

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

上面的示例返回 J.R. Tolkien 编写的图书的所有属性,包括自动生成的标识符。要选择应返回文档的哪些属性,可与查询一起提交一个投影列表 (projection list),表明要在结果中包含或排除哪些属性。使用以下值:

  • 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二者都必须为 true$and:[{"rating":5},{"category":"Mystery"}]
$and 使用一个偶好作为分隔符来表示"category":"Mystery","rating":5
$or至少有一个必须为 true$or:[{"rating":5},{"category":"Mystery"}]
$nor二者都不得为 true$nor:[{"rating":3},{"category":"Mystery"}]
$not逻辑非(不匹配)$not:{"rating":1}

控制结果集的选项

对输出进行排序常常很有用,例如首先列出最高或最低的值,或者排序文档以便可更容易地查找值。要对 JSON 查询中的属性排序,可在 sort() 函数列出一个或多个属性来排序,指定每个属性应按升序 (=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)

如果只需要一个结果,出于方便,可使用 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 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}, "avgprice": {"$avg": "$price"}} )

对于更复杂的查询,可使用 aggregate() 函数。它允许您将一个查询描述为一系列步骤,以便中间结果可再次用作后续步骤的输入。例如,计算每位作者的平均价格,或利用以下查询首先选择所有具有类别 “Fantasy” 的图书,然后从文档中挑选属性 author 和 price,对作者进行分组以计算平均价格,如清单 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 任务中应用算术、字符串或日期时间运算。下一个示例是在一个 $group 任务中计算每位作者的总价格和平均价格后,使用 $divide 运算符来计算每页的价格,如清单 26 所示。

清单 26. 按升序列出每页的作者和价格
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" 一样。

聚合函数可包含单个或多个任务,不需要包含 $group 任务。例如,可为一个简单的投影任务使用 aggregate(),无论修不修改值,如清单 27 所示。

清单 27. 选择一个属性的一个子字符串
nosql>db.books.aggregate({"$project": {author:1, title:1, 
                 shortabs: {"$substr": ["$abstract",0,10]} } })

聚合任务只允许使用通过包含在内而指定的字段。如果已通过投影或分组步骤在一个步骤中显式选择属性,那么可以在下一步中通过上一步中分配的引用名称来使用它们。其他所有字段都会被丢弃。

技巧:aggregate() 函数仅返回指定的属性,不同于 find() 函数,它不会自动在 $project 任务中包含生成的 _id。

技巧:对于技术预览版,请确保在以后的任务中引用计算或重命名的字段之前已经对它们进行了投影。

表 3 中给出了有效的任务。

表 3. 聚合任务
$group应用聚合运算,比如 sum 或 avg;需要使用一个从一个或多个属性构建的分组键
$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

技巧:如果收到一个错误,表明特定运算符不受支持,那么请查阅语法,确保顺序为 {运算符: {字段: 值}},例如 {“$gt”: {rating: 1}}。

更新文档

update() 函数更新一个或多个文档中的数据。它支持使用多个可选的参数来确定更新范围。用法为 update(<condition>, <fields to 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,则会插入遗漏的文档。

使用 multiRow 标志,也可确定更新应应用到与搜索条件匹配的第一个文档,还是所有匹配的文档,如清单 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,所以将生成一个新标识符并将新图书插入到 books 集合中,如清单 31 所示。

清单 31. 保存一个文档
nosql>db.books.save({isbn: "123-456-239", 
              "author": "Verne, Jules",  "title": "Mysterious Island" })"

使用索引

如果一个索引常常用作查询中的选择或排序条件,那么创建一个索引对加快较大数据工作负载的检索速度很有用。JSON 属性上的索引可在单个或多个属性上创建,可按升序或降序排序。如果未指定索引名称,则会分配一个默认名称。要创建一个索引,可使用 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}]

使用 unique 标志在一个属性上创建一个惟一索引。系统会尝试创建一个惟一索引。如果集合已包含文档,则必须确保指定的字段中没有重复数据,如清单 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 命名空间中提供了 3 个集合,如清单 42 所示。

清单 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
  }

删除数据、索引和集合

要从集合中删除一个文档或多个文档,可使用 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 系统目录,可使用 disable() 命令和值 true,如清单 50 所示。与 dropAllCollections() 命令一样,丢弃所有受影响的对象需要足够的特权。

清单 50. 禁用 NoSQL JSON 特性
nosql>disable(true)

如果对 disable() 命令使用了选项 false ,将打印该 DDL 语句,但不会执行它。


结束语

本文介绍了 NoSQL JSON 命令行环境的设置,介绍了一些处理集合,存储和查询 JSON 文档的基本命令。要了解 DB2 NoSQL JSON 的更多信息,了解如何使用 Java API 或如何使用 Wire Listener 接收和处理请求,请参阅本系列的其他文章。有关命令和运算的详细信息,请参阅 DB2 NoSQL JSON 参考文档。


下载

描述名字大小
示例 JSON 文档books_import.zip2KB
包含嵌套的示例 JSON 文档books_importNested.zip2KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Information Management, Java technology, Open source
ArticleID=945224
ArticleTitle=DB2 NoSQL JSON 功能,第 2 部分: 使用命令行处理器
publish-date=09162013