使用 InfoSphere BigInsights 查询社交媒体和结构化数据

Jaql 简介

如果您希望快速启动涉及 IBM® InfoSphere® BigInsights™ 的大数据项目,那么了解如何查询、操作和分析数据的基础知识就非常重要。本文将引导您完成一些简单的查询示例,向您展示如何读、写、筛选、细化社交媒体数据和结构化数据。您甚至可以看到业务分析师如何使用电子表格样式的工具来可视化查询结果。

Cynthia M. Saracco, 高级解决方案架构师, IBM

照片:Cynthia SaraccoCynthia M. Saracco 是 IBM 硅谷实验室的高级解决方案架构师,擅长新兴技术和数据库管理主题。她有 23 年软件行业从业经验,曾经撰写了三本书和 60 多篇技术文章,拥有七项专利。


developerWorks 专家作者

Marcel Kutsch, 软件工程师, IBM

作者照片Marcel Kutsch 是位于加利福尼亚州圣何塞市的 IBM 硅谷实验室的大数据小组的一名软件工程师。他目前的工作任务是进行 Jaql 运行时开发。在此之前,他曾有 5 年致力于 DB2 开发,并担任过多种职务。Marcel 是有关 DB2 z/OS Stored Procedure 开发的 IBM 红皮书的合著者,并在数据库技术领域拥有多项专利。



2012 年 10 月 08 日

免费下载:IBM® InfoSphere BigInsights Basic Edition V1.3 试用版
下载更多的 IBM 软件试用版,并加入 IBM 软件下载与技术交流群组,参与在线交流。

处理大数据常常需要查询该数据,以隔离关注的信息,并以不同的方式对其进行操作。本文将向您介绍 Jaql,这是一个随 InfoSphere BigInsights 一起提供的查询和脚本语言。本文还讨论了如何查询从社交媒体站点中收集的数据,并将这些数据与从关系数据库管理系统 (DBMS) 中检索到的信息进行联接。

背景知识

也许您不熟悉 InfoSphere BigInsights,它是一个软件平台,旨在帮助企业发现和分析隐藏在不同范围的大量数据中的业务洞察,这些数据经常被忽略或丢弃,因为使用传统的手段来处理它们过于不切实际或困难。这样的数据包括日志记录、点击流、社交媒体数据、新闻提要、电子传感器输出,甚至是一些事务数据。

为了帮助企业以高效的方式从这些数据中获取价值,BigInsights 的 Enterprise Edition 提供了几个开源项目(包括 Apache™ Hadoop™)和一些 IBM 开发的技术。Hadoop 和补充项目为数据密集型应用程序提供了有效的软件框架,利用分布式计算环境实现高可扩展性。IBM 技术通过分析软件、企业软件集成、平台扩展和工具丰富了这个开源框架。有关 BigInsights 的更多信息,请参阅 参考资料 部分。

本文向您介绍 BigInsights 通过 Jaql 提供的一些基本查询功能,Jaql 是一种查询和脚本语言,它使用基于 JavaScript Object Notation (JSON) 的数据模型。虽然 Jaql 并不是查询由 BigInsights Basic 或 Enterprise Editions 管理的数据的惟一方式(例如,您可以使用 Hive 或 Pig),但它能够与不同的数据结构(包括深层嵌套的数据结构)良好地进行配合。BigInsights 还提供了一个 Jaql 模块,可用于访问支持 JDBC 功能的数据源。这种功能对于我们的场景特别有用,其中涉及分析被收集为 JSON 记录的少量社交媒体数据,使用 Jaql 的 JDBC 模块,将该数据与从关系型 DBMS 中提取的 CSV(逗号分隔值)格式的企业记录相结合。

理解示例场景

在本文中,您将使用 Jaql 从社交媒体站点中搜集有关 IBM Watson 的文章,然后调用各种 Jaql 表达式来筛选、转换和操作这些数据。如果您不熟悉 IBM Watson,可以先简单介绍一下,这是一个研究项目,执行复杂的分析,解答使用自然语言提出的问题。为了做到这一点,Watson 的软件使用了在 IBM Power 750 服务器集群上运行的 Apache Hadoop,高效地处理从各种来源收集的数据。在 2011 年,IBM Watson 在电视转播的 Jeopardy! 游戏节目比赛中获得了第一,击败两个领先的人类参赛者。有关 IBM Watson 的更多详细信息,请参阅 参考资料 部分。

很多企业的业务分析师都对监视某个特定品牌或服务的知名度、覆盖范围和议论感兴趣,因此,在本文中,IBM Watson 将作为这样一个品牌的示例。参考资料 部分中引用了之前的一篇文章,其中探讨了业务分析师如何使用 BigSheets(BigInsights 提供的电子表格风格的工具)分析从多个站点中收集到的有关 IBM Watson 的社交媒体数据。在本文中,您会收集和处理来自某个站点 (Twitter) 的小数据集,让您可以重点学习 Jaql 的关键方面。

值得一提的是,许多社交媒体站点都提供了 API,程序员可以用它来获取公共数据。在本文中,您将使用 Twitter 的基于 REST 的 API 来检索少量数据,其中包含您关注的最新推文。某生产应用程序可能会使用其他 Twitter 服务来获得大量数据,但是,由于本文的讨论重点是 Jaql,所以数据的收集工作会尽量保持简单。

通常情况下,社会媒体站点所提供的 API 会以 JSON 格式返回数据,Jaql 基于相同的数据模型。在深入探讨 Jaql 示例之前,您需要熟悉社交媒体数据的 JSON 结构,因为您会在本文中用到它。您可以轻松地做到这一点,只需在可以处理 JSON 数据的 Web 浏览器(如 Chrome)中输入下面的 URL:http://search.twitter.com/search.json?q=IBM+Watson

这使 Twitter 返回匹配您的搜索条件 ("IBM Watson") 的 15 篇最新的文章。虽然根据执行搜索的时间不同,返回的内容页会有所不同,但结果的结构将类似于 清单 1 中的摘录,其中包括为了进行说明而创建的示例数据。很快,您就可以浏览该摘录的一些关键部分,为本文的测试提供相应的上下文。

清单 1. 来自基于 Twitter 的搜索的示例 JSON 记录结构
{
    "completed_in": 0.021,
    "max_id": 99999999111111,
    "max_id_str": "99999999111111",
    "next_page": "?page=2&max_id=99999999111111&q=IBM%20Watson",
    "page": 1,
    "query": "IBM+Watson",
    "refresh_url": "?since_id=99999999111111&q=IBM%20Watson",
    "results": [
    {
        "created_at": "Mon, 30 Apr 2012 18:42:37 +0000",
        "from_user": "SomeSampleUser",
        "from_user_id": 444455555,
        "from_user_id_str": "444455555",
        "from_user_name": "Some Sample User",
        "geo": null,
        "id": 000000000000000001,
        "id_str": "000000000000000001",
        "iso_language_code": "en",
        "metadata": {
            "result_type": "recent"
        },
        "profile_image_url": 
            "http://a0.twimg.com/profile_images/222222/TwitterPic2_normal.jpg",
        "profile_image_url_https": 
            "https://si0.twimg.com/profile_images/222222/TwitterPic2_normal.jpg",
        "source": "<a href="http://news.myUniv.edu/" rel="nofollow">MyUnivNewsApp</a>",
        "text": "RT @MyUnivNews: IBM's Watson Inventor will present at 
          a conference April 12   http://confURL.co/xrr5rBeJG",
        "to_user": null,
        "to_user_id": null,
        "to_user_id_str": null,
        "to_user_name": null
    },
                
    {
        "created_at": "Mon, 30 Apr 2012 17:31:13 +0000",
        "from_user": "anotheruser",
        "from_user_id": 76666993,
        "from_user_id_str": "76666993",
        "from_user_name": "Chris",
        "geo": null,
        "id": 66666536505281,
        "id_str": "66666536505281",
        "iso_language_code": "en",
        "metadata": {
        "result_type": "recent"
    },
        "profile_image_url": 
            "http://a0.twimg.com/profile_images/3331788339/Mug_Shot.jpg",
        "profile_image_url_https": 
            "https://si0.twimg.com/profile_images/3331788339/Mug_Shot.jpg",
        "source": "<a href="http://www.somesuite.com" rel="nofollow">SomeSuite</a>",
        "text": "IBM's Watson training to help diagnose and treat cancer 
            http://someURL.co/fBJNaQE6",
        "to_user": null,
        "to_user_id": null,
        "to_user_id_str": null,
        "to_user_name": null
},
. . . 
"results_per_page": 15,
"since_id": 0,
"since_id_str": "0"
}

以下是对 JSON 的一个非常简短的介绍。清单 1 中包含一个 JSON 记录,其开头和结尾都用花括号 “{ }” 指出。在此记录中,有多个名称/值对、其他(嵌套) JSON 记录,和 JSON 数组(以方括号 “[]” 界定)。这是典型的 JSON,其中已包含大量嵌套的和不同的数据结构。有关 JSON 的更多详细信息,请参阅 参考资料 部分。

在本示例中,您可以看到,JSON 记录的初始部分包含名称/值对,以及有关 Twitter 搜索的背景信息。例如,搜索在 0.021 秒内完成,并且查询涉及到了 IBM Watson。记录中嵌套的 “results” 数组包含本文最关注的数据:包含有关 IBM Watson 的推文的其他 JSON 记录。

在结果数组中嵌套的每一个 JSON 记录都指示了用来让大家了解用户的名称 ("from_user_name")、创建消息的时间戳 ("created_at")、消息文本 ("text")、文本的语言 “iso_language_code”,等等。事实上,您会使用用户的 ID ("from_user_id_str") 来联接社交媒体数据与从关系型 DBMS 中提取的数据。本文的联接场景很简单,甚至有点取巧。它假定:雇主维护一个关系表,跟踪其员工用于公务的社交媒体 ID,该企业希望分析最近的文章。不过,可以将使用 Jaql 联接来自不同数据源的数据的基本方法应用于更广泛的场景。


执行 Jaql 语句

BigInsights Enterprise Edition 提供了若干个执行 Jaql 语句的选项,包括以下选项:

  • Jaql shell,一个命令行界面。
  • Jaql 临时查询应用程序,可通过 BigInsights Web 控制台访问。在启动该应用程序之前,管理员必须将它部署到您的 BigInsights 集群上,并授予您访问该应用程序的权限。请参阅 参考资料 部分,获得有关 BigInsights Web 控制台和示例应用程序的独立文章的链接。
  • 随用于 BigInsights 的 Eclipse 工具一起提供的 Jaql 测试环境。
  • Jaql Web 服务器,允许通过 REST API 调用来执行 Jaql 脚本。
  • Jaql API,将 Jaql 嵌入 Java 程序。

本文使用 Jaql shell。为了启动 Jaql shell,打开一个 Unix / Linux 命令窗口,然后执行下面的命令。 $BIGINSIGHTS_HOME/jaql/bin/jaqlshell

注意:$BIGINSIGHTS_HOME 是一个环境变量,被设置为安装 BigInsights 的目录,通常为 /opt/ibm/biginsights。在启动​​ Jaql shell 后,屏幕显示应该类似于图 1。

图 1. Jaql shell
Jaql shell

Jaql 和 MapReduce

Jaql 旨在透明地利用与基于 Hadoop 的环境有关联的 MapReduce 编程模型。利用 Jaql,您可以专注于自己试图解决的问题,而不是工作的底层实现。换句话说,您的查询指定了您想做的事情,而 Jaql 引擎会透明地重写查询,以确定这项工作将如何执行。查询重写技术在关系数据库管理系统 (RDBMS) 环境中很常见,Jaql 利用这一基本理念来简化您的工作。

Jaql 的查询重写技术的一部分往往涉及到将您的逻辑拆分成多个 Map 和 Reduce 任务,让您的工作可以在 BigInsights 集群上并行处理。能够以这种方式重写的查询被称为可拆分或可分区查询。在熟悉 Jaql 之后,本文稍后会讨论 Jaql 可以转换成一系列 MapReduce 作业的各种查询。

收集示例数据

通过 Jaql 查询、操作和分析数据,首先要从源读取数据,源可以是由 BigInsights 管理的 Hadoop 分布式文件系统 (HDFS)、本地文件系统、关系型 DBMS、提供基于 REST 的搜索 API 的社交媒体站点和其他源。在本节中,您将学习如何使用 Jaql 将从 Twitter 中检索到的 JSON数据以及从 DB2 Express-C 中检索到的关系数据来填充 HDFS。虽然 BigInsights 也支持其他数据收集和加载机制,但这里描述的技术适合于本文的简单应用程序场景,并支持向您介绍核心 Jaql 功能的目标。

使用社交媒体数据

BigInsights 提供了一些适用于 Jaql 的 I/O 适配器。每个适配器处理一个特定数据源(如 HDFS、Web 服务器或数据库)的访问,并在其原生格式和 JSON 值数组之间执行数据转换。正如您可以想象的那样,Jaql I/O 适配器和语言提供的 read()write() 函数有密切的关联。

现在,我们探讨一个检索有关 “IBM Watson” 的 15 个最新的 Twitter 消息的简单方法。如 清单 2 所示,首先要为 Twitter URL 定义一个 url 变量,您要使用它来检索关注的数据。然后,需要定义一个 tweets 变量,给它分配一个表达式,使用 Jaql 的 read() 函数和 HTTP 适配器从您刚才指定的 URL 中获取数据。

清单 2. 使用 Jaql 的 HTTP 适配器从 URL 中获取流数据
url = "http://search.twitter.com/search.json?q=IBM+Watson"; 
                
tweets = read(http(url));
                
tweets;

因为 Jaql 以一个懒惰的方式物化变量赋值,在执行最后的语句之前,可能不会发生读取操作。在 Jaql shell 输入一个变量,指示 Jaql 显示变量的内容,从而迫使对定义该内容的操作进行评估。

虽然使用变量是可选的,但这些变量往往很便利,因为后续的查询可以引用这些变量来简化它们的代码。事实上,本文稍后演示的几个清单都会引用先前定义的变量来说明这一点。

之前在 清单 2 中所示的语句的执行结果将类似于 清单 1 中的内容。与本文中所有的练习一样,具体的结果可能会有所不同,因为有关 IBM Watson 的最新推文将随着时间的推移而变化。但是,两个清单返回的数据之间有一个区别。Jaql 将从 Web 服务返回的数据嵌入到顶层的数组,例如,[ twitter-json-data-content ]。因此,推文表示一个数组,其中包含一条 JSON 记录和之前在 清单 1 中所示的结构。

使用关系型 DBMS 数据

BigInsights Enterprise Edition 包括数据库导入和导出应用程序,您可以从 Web 控制台启动这些应用程序,从关系 DBMS 读取数据,或者将数据写入关系 DBMS。前面的文章(参考 参考资料 部分)介绍了这些示例应用程序,并提供了一个示例,使用数据库导入应用程序,以便检索 DB2 Express-C 中的数据。

在本文中,您将使用 Jaql shell 从关系型 DBMS 动态检索数据。Jaql 提供了对 IBM 和非 IBM DBMS 的 JDBC 连接,包括 Netezza、DB2、Oracle、Teradata,等等。您将使用通用 JDBC 连接模块访问 DB2 Express-C 数据库,其中包含一个名为 IBM.TWEETERS 的表,该表跟踪将消息发布到这个社交媒体网站的 IBM 员工的电子邮件地址、Twitter ID、姓名和职位。本文中的表包含出于测试目的而创建的示例数据。

现在探讨一下清单 3 中所示的 Jaql 代码。

清单 3. 从 Jaql shell 动态查询关系型 DBMS
// Block 1: Import Jaql JDBC module
import dbms::jdbc; 
                
// Block 2: Set class path for JDBC drivers 
addRelativeClassPath(getSystemSearchPath(), '/home/hdpadmin/myDrivers/db2jcc4.jar');   
                
addRelativeClassPath(getSystemSearchPath(),  
'/home/hdpadmin/myDrivers/db2jcc_license_cu.jar');
                
// Block 3: Establish a database connection 
db := jdbc::connect(
   driver = 'com.ibm.db2.jcc.DB2Driver',
   url = 'jdbc:db2://myserver.ibm.com:50000/sample',
   properties = { user: "myID", password: "myPassword" }
);
                
// Block 4: Prepare and execute the query
desc := jdbc::prepare(db, query =
   "SELECT EMAIL, ID, NAME, TITLE FROM IBM.TWEETERS");
                
ibm = read(desc);
                
ibm; 
                
// Sample output 
[
    {
        "EMAIL": "john.doe@us.ibm.com",
        "ID": "1111111",
        "NAME": "John Doe",
        "TITLE": "Researcher"
    },
. . . 
    {
        "EMAIL": "mary.johnson@us.ibm.com",
        "ID": "71717171",
        "NAME": "Mary Johnson",
        "TITLE": "IT Architect"
    }
]

Block 1 导入了关注的 Jaql 模块。在本例中,dbms 是一个 Jaql 包的名称,其中包含被实现为单独模块的多个 DBMS 连接器。import 语句使得 JDBC 模块中的所有函数和变量都可以供您的会话使用。JDBC 执行了 JDBC Jaql 结构的范围界定,参见清单 3 的 Blocks 3 和 4 中所示的调用。

Block 2 指定了所需 JDBC Driver jar 文件的位置,并将这些位置添加到 Jaql 类路径。由于它访问了 DB2 Express-C,所以会将 db2jcc4.jar 和 db2cc_license_cu.jar 文件复制到本地文件系统。接着,Block 3 会调用 Jaql JDBC 模块的 connect() 函数,传递所需的 JDBC 连接参数,包括 JDBC 驱动程序类、JDBC URL、用户名和用户密码。请注意该语句中声明的 db 变量的赋值运算符,它与您之前在 清单 2 中看到的赋值运算符看起来是不同的。具体来说,此处所示的 := 运算符执行等式右侧的工作,DBMS 连接工作,会立刻被物化。在本例中,它强制 Jaql 尝试立即获得 DB2 连接句柄,并将该句柄分配给 db 变量。

成功建立数据库连接之后,就可以准备并执行一个查询,如 Block 4 所示。在本例中,SQL SELECT 语句仅仅从表中检索四列。注意,在将查询传递到目标数据库之前,Jaql JDBC 模块不会解析查询,也不会检查其语法。Jaql 的 read() 函数会执行准备好的查询,让结果显示为包含多个记录的 JSON 数组,每个记录有四个字段,分别映射到 SQL 查询中指定的四个列。

从 BigInsights 1.4 开始,Jaql 的 JDBC 连接器允许您选择引用您的 BigInsights 凭据存储中的某个属性文件,其中包括 DBMS 连接信息(比如一个有效的用户 ID 和密码),而不是直接传递这些数据,如前面的清单 3 中所示。有关的详细信息,请参阅 参考资料 部分中引用的 BigInsights InfoCenter。

现在您知道了如何检索社交媒体数据和关系数据,本文稍后会用到该知识,使用 Jaql 联接来自这些不同来源的数据。但是,在这之前,让我们来回顾 Jaql 的一些基本查询功能。

查询和操作数据

常见的查询操作包括从输入数据中提取特定字段。如果您熟悉 SQL,您可以认为这是从表中投影或检索列的子集。现在,让我们来了解如何从在上一节收集的示例 Twitter 数据中检索特定字段。然后,您会看到如何操作或转换您的输出为不同的 JSON 结构,以实现应用程序特定的需求。

检索单一字段

从非常简单的示例开始。当执行之前在 清单 2 中所示的 Jaql 代码时,您会获得了一个顶层的 JSON 数组,其中包含一个 JSON 记录以及由 Twitter 返回的有关 IBM Watson 的最新文章的数据。(清单 1 包含由 Twitter 返回的这种 JSON 记录的示例数据。)JSON 记录中包含的是由一些代表推文的 JSON 记录组成的结果数组。每个记录都包含多个字段。如果您只想从每个记录中获得一个字段,如 id_str 字段,又该如何做呢?您如何在 Jaql 中实现这一目的呢?

清单 4 显示了执行该查询的一种方法,后面是示例输出。

清单 4. 使用 Jaql 变换表达式检索单个字段
tweets -> transform $.results.id_str;
                
// Sample Jaql output 
[
    [
        "999992200059387904",
        "999992003644329985",
        "999991910044229633",
        "999991880671531008",
        "999991865702064128",
        "999991853391769601",
        "999991708440817664",
        "999991692309524480",
        "999991655370293248",
        "999991582779469826",
        "999991442597437442",
        "999991361437655041",
        "999991343142100992",
        "999991269276213249",
        "999991175747436544"
    ]
]

让我们简要地介绍一下该查询。作为执行 清单 2 中的代码的结果,tweets 变量表示从 Web 上收集的数据。管道操作符 (->) 将该数据提供给 Jaql 的 transform 表达式,表达式按指示修改输出。在本例中,会对 Jaql 进行定向,以检索包含在当前记录内的结果数组中的 id_str 字段。清单 4 中所示的美元符号是 Jaql 对当前记录的缩写。

如果您检查输出,就会看到它包含一个在另一个 JSON 数组内返回的 JSON 数组。Jaql transform 表达式会遍历输入数组的每一个元素并转换它们,然后返回一个输出数组。在本例中,所提供的输入数组包含一个带有一条 JSON 记录的数组,但该记录包含了一个嵌套在它内部的数组。因此,Jaql 返回一个数组,其中包含 id_str 值的一个(嵌套)数组。

现在想象一下,您选择使用这些值的一个简单的扁平数组 (flat array)。Jaql 的 expand 表达式使您能够实现这一目标。expand 表达式使用嵌套数组 [ [ T ] ] 作为输入数组,并通过每个嵌套数组的元素提升到顶级输出数组,从而产生一个输出数组 [ T ]清单 5 显示如何使用 expand 返回 id_str 值的单个数组。

清单 5. 利用 expand 表达式扁平化嵌套数组
tweets -> transform $.results.id_str -> expand;
                
// Sample Jaql output
[
    "999992200059387904",
    "999992003644329985",
    "999991910044229633",
    "999991880671531008",
    "999991865702064128",
    "999991853391769601",
    "999991708440817664",
    "999991692309524480",
    "999991655370293248",
    "999991582779469826",
    "999991442597437442",
    "999991361437655041",
    "999991343142100992",
    "999991269276213249",
    "999991175747436544"
]

将数据写入 HDFS 和从 HDFS 读取数据

此时,您已查询从 Web 源中动态检索的查询数据。虽然您一直在这样做,但更实用的方法可能是将关注的数据写入 HDFS,在您关闭您的 Jaql 会话之后,这些数据仍然是可用的。由于您只对查询由 Twitter 返回的嵌套结果数组中包含的数据感兴趣,所以您只需将这些数据写入 HDFS。

清单 6 使用 Jaql write() 函数将一些 Twitter 数据写入特定 HDFS 目录中一个名为 recentTweets.seq 的 SequenceFile 中。SequenceFile 是 Jaql 支持的众多格式中的一种。

清单 6. 将数据写入 SequenceFile
// Write the "results" data of the first element of tweets to HDFS 
// This data contains Twitter posts and related info  
tweets[0].results -> 
write(seq("/user/idcuser/sampleData/twitter/recentTweets.seq"));
                
// Jaql output
{
    "inoptions": {
        "adapter": "com.ibm.jaql.io.hadoop.DefaultHadoopInputAdapter",
        "configurator": "com.ibm.jaql.io.hadoop.FileInputConfigurator",
        "format": "org.apache.hadoop.mapred.SequenceFileInputFormat"
    },
    "location": "/user/idcuser/sampleData/twitter/recentTweets.seq",
    "outoptions": {
        "adapter": "com.ibm.jaql.io.hadoop.DefaultHadoopOutputAdapter",
        "configurator": "com.ibm.jaql.io.hadoop.FileOutputConfigurator",
        "format": "org.apache.hadoop.mapred.SequenceFileOutputFormat"
    }
}

清单 6 中的第一行指定了您想写入的数据:我们的 Twitter 搜索返回的 tweets 数组中第一个元素的结果部分。使用典型的数组索引标注来访问由估计的 tweets 变量返回的顶层数组的第一个(也是惟一一个)元素。因此,tweets[0] 会返回第一个元素,它是包含 Twitter 数据的大型 JSON 记录。在该元素中,您可以轻松地隔离某个字段。在本例中,您希望结果数组包含所有 Twitter 提要记录。第一行的 -> 是一个简单的管道,将 tweets 变量的内容定向到 write() 函数。

清单 6 中显示的第二部分的第一个语句返回 Jaql 输出。输出是一个 Jaql 文件描述符记录,其中包括有关 I/O 适配器的信息和用于处理该语句的格式。

您可能好奇为什么在本例中会选择使用 SequenceFile 格式。您稍后会了解到,Jaql 可以并行读取和写入这样的数据,自动利用了 MapReduce 框架的这一关键方面。直接从 Web 服务读取数据(如 清单 2 中的做法)是不可能实现这种并行性的。此外,为了鼓励获得高效的运行时查询处理,需要转换数据,以隔离出您关注的信息,并结构化这些数据,这样您就可以拥有一个小对象集合,在本例中,这些小对象是表示 tweet 的 JSON 记录。与 Twitter 返回的原始 JSON 数据相比,该结构可实现并行程度更高的查询,原始 JSON 数据由包含一个 JSON 记录的顶层数组组成,这种结构不能进行拆分,因此不能利用并行性。虽然该示例数据非常小,但在处理大量数据时,刚刚讨论的基础思路相当重要。

一旦将数据存储到了 HDFS 中,就可以使用 Jaql 的 read() 函数来检索它,如 清单 7 所示。

清单 7. 从 HDFS 读取一个 SequenceFile
tweetsHDFS = read(seq("/user/idcuser/sampleData/twitter/recentTweets.seq")); 
                
tweetsHDFS; 
                
// Sample Jaql output
[
    {
        "created_at": "Mon, 30 Apr 2012 18:42:37 +0000",
        "from_user": "SomeSampleUser",
        "from_user_id": 444455555,
        "from_user_id_str": "444455555",
        "from_user_name": "Some Sample User",
        "geo": null,
        "id": 000000000000000001,
        "id_str": "000000000000000001",
        "iso_language_code": "en",
        "metadata": {
        "result_type": "recent"
        },
        "profile_image_url": 
        "http://a0.twimg.com/profile_images/222222/TwitterPic2_normal.jpg",
        "profile_image_url_https": 
        "https://si0.twimg.com/profile_images/222222/TwitterPic2_normal.jpg",
        "source": "<a href="http://news.myUniv.edu/" rel="nofollow">MyUnivNewsApp</a>",
        "text": "RT @MyUnivNews: IBM's Watson Inventor will present 
        at a conference April 12   http://confURL.co/xrr5rBeJG",
        "to_user": null,
        "to_user_id": null,
        "to_user_id_str": null,
        "to_user_name": null
    }, 
    . . . 
    {
        "created_at": "Mon, 30 Apr 2012 17:31:13 +0000",
        "from_user": "anotheruser",
        "from_user_id": 76666993,
        "from_user_id_str": "76666993",
        "from_user_name": "Chris",
        "geo": null,
        "id": 66666536505281,
        "id_str": "66666536505281",
        "iso_language_code": "en",
        "metadata": {
        "result_type": "recent"
        },
        "profile_image_url": 
        "http://a0.twimg.com/profile_images/3331788339/Mug_Shot.jpg",
        "profile_image_url_https": 
        "https://si0.twimg.com/profile_images/3331788339/Mug_Shot.jpg",
        "source": "<a href="http://www.somesuite.com" rel="nofollow">SomeSuite</a>",
        "text": "IBM's Watson training to help diagnose and treat cancer 
        http://someURL.co/fBJNaQE6",
        "to_user": null,
        "to_user_id": null,
        "to_user_id_str": null,
        "to_user_name": null
    } 
]

现在您已经隔离出了您关注的数据,并从 HDFS 检索到它,您可以探讨其他一些查询场景。

检索多个字段

您可以探索如何从已写入 HDFS 的数据中检索多个字段。特别是,假设您想获得一个记录集合,其中每条记录都包含与返回的推文相关的创建日期 (created_at)、地理位置 (geo),发帖人的用户 ID (from_user_id_str),语言代码 (iso_language_code) 和文本 (text) 字段。如果您熟悉SQL,那么您可能想编写一些与 清单 8 中的代码类似的东西,该代码调用了 Jaql 的包含多个字段的 transform 表达式。但是,这将导致一个错误。

清单 8. 不正确地使用 Jaql transform 表达式从记录中提取多个字段
                // Incorrect syntax for extracting multiple fields 
                tweetsHDFS -> transform $.created_at, $.geo, 
                $.from_user_id_str, $.iso_language_code, $.text;

现在看看可以实现您的目标的适当方式,如 清单 9 所示。

清单 9. 检索选定的字段,并将它们作为一个数组中的 JSON 记录返回
tweetsHDFS -> transform { 
              created_at: $.created_at, 
              geo: $.geo, 
              id: $.from_user_id_str, 
              iso_language_code: $.iso_language_code, 
              text: $.text };             
                
// Sample Jaql output
[
    {
        "created_at": "Mon, 30 Apr 2012 17:30:09 +0000",
        "geo": null,
        "id": "888888888",
        "iso_language_code": "en",
        "text": "#someUser: IBM\'s Watson Has An Answer 
           About The Future of Health Care | http://someURL.co/ZZ1XX via #mynews"
    },
. . . 
    {
        "created_at": "Mon, 30 Apr 2012 17:24:43 +0000",
        "geo": null,
        "id": "77777777",
        "iso_language_code": "en",
        "text": "Great news! \"RT @SomePlace: 
           Can Watson, IBM\'s Supercomputer, Cure Cancer? http://someURL.co/DDk1a \""
    }
]

由于 tweetsHDFS 变量包含一个 JSON 记录数组,所以可以使用 transform 表达式通过一个特殊的美元符号变量来访问此数组中包含的每条记录。此外,指定您想要将每个输入记录转换为一个新的 JSON 记录(由花括号界定)。每一条新记录包含您想要的五个字段,表示为一个名称/值对。例如,新记录中的第一个字段是 created_at(一个任意的名称),该字段的值是从输入数组的 $.created_at 字段获得的。

顺便说一句,当定义由 Jaql transform 表达式生成的新 JSON 记录时,如果您想让 Jaql 根据输入数组中的字段名称来推断字段名称,那么您可以省略字段名称。如之前的 清单 9 中所示,为新 JSON 记录定义的五个字段中的四个字段都与其在输入数组中的相应字段具有相同的名称。所以,您可以制止显式指定这些字段名称。清单 10 显示了这种短形式表示法 (short form notation)。

清单 10. 指定新的 JSON 记录结构时使用默认的字段名称
// short-hand version of prior query 
// Jaql will infer field names unless explicitly stated  
tweetsHDFS -> transform { 
                $.created_at, 
                $.geo, 
                id: $.from_user_id_str, 
                $.iso_language_code, 
                $.text };

筛选数据

另一种常见的查询要求包括根据用户指定的标准筛选数据。例如,假设您想检索基于英语的 tweet 记录。清单 11 显示了一个实现这目标的简单方法。

清单 11. 检索选定字段,并基于单个查询谓词筛选数据
// Query 1:  transform data, retrieving select fields 
tweetRecords = tweetsHDFS 
            -> transform { 
            created_at: $.created_at, 
            geo: $.geo, 
            id: $.from_user_id_str, 
            iso_language_code: $.iso_language_code, 
            text: $.text };
                
// Query 2:  filter data, retraining only English records 
tweetRecords -> filter $.iso_language_code == "en"; 
                
// Sample Jaql output
[
    {
        "created_at": "Mon, 30 Apr 2012 17:30:09 +0000",
        "geo": null,
        "id": "888888888",
        "iso_language_code": "en",
        "text": "#someUser: IBM\'s Watson Has An Answer 
           About The Future of Health Care | http://someURL.co/ZZ1XX via #mynews"
    },
. . . 
    {
        "created_at": "Mon, 30 Apr 2012 17:24:43 +0000",
        "geo": null,
        "id": "77777777",
        "iso_language_code": "en",
        "text": "Great news! \"RT @SomePlace: 
           Can Watson, IBM\'s Supercomputer, Cure Cancer? http://someURL.co/DDk1a \""
    }
]

第一个查询和之前 清单 9 中所示的查询几乎是完全相同的,但定义了一个 tweetRecords 变量,用于保存 JSON 记录,其中包含每篇推文中关注的字段。第二个查询将这个变量作为输入提供给 Jaql filter 表达式,该表达式会检查 iso_language_code 字段,以获得 en(即英语的 ISO 语言代码)的值。同样,美元符号是 filter 表达式引入的一个特殊变量,它将每一个输入元素与其绑定。然后,使用清单中所示的点符号就可以轻松访问单个字段。输出中会包括合资格的记录,如清单的末尾所示。

如果您想根据多个条件筛选查询结果,只需使用 Jaql 的 AND / OR 语法。例如,清单 12 中的查询将返回基于英语的推文记录,其中并没有指定地理位置。这些记录在地理字段中会有一个 null 值。为了测试 null 值,Jaql 提供了 isnullnot isnull 表达式。

清单 12. 指定多个查询谓词(筛选条件)
tweetRecords -> filter $.iso_language_code == "en" and isnull $.geo;

排序数据

对数据进行排序是另一种常见的查询要求,并且是在 Jaql 中执行的一个简单操作。试想一下,您要根据用户 ID 按照升序顺序对推文记录进行排序。清单 13 显示了适当的语法。

清单 13. 排序查询结果
tweetRecords -> sort by [$.id asc]

tweetRecords 变量表示的数组被提供为 Jaql 的 sort by 表达式的输入,这就要求至少按照它的一个字段进行排序。$.id 字段(表示用户 ID)被提供并指定为 asc,表示升序。如需按降序排列,则可以指定 desc。

您也可以根据多个条件进行排序,如清单 14 中所示。

清单 14. 指定多个排序条件
// Sort by id (ascending), then geo (descending) 
tweetRecords -> sort by [$.id asc, $.geo desc];

汇总数据

某些应用程序需要将数据进行分组,并对所有组进行汇总计算。Jaql 支持常见的聚合函数,包括 min(最小值)、max(最大值)和 count 等。探索以下简单的示例。

试想一下您想为每个 ISO 语言代码的推文记录进行计数的情况。清单 15 显示了适当的语法。

清单 15. 汇总数据
tweetRecords -> 
group by key = $.iso_language_code 
 into { groupingKey: key, num: count($) };
                
// Sample Jaql output 
[
    {
        "groupingKey": "de",
        "num": 1
    },
    {
        "groupingKey": "en",
        "num": 12
    },
    {
        "groupingKey": "fr",
        "num": 2
    }
]

将推文记录提供给 Jaql 的 group by 表达式。在执行此操作时,可以使用 $.iso_language_code 作为分组的标准,并定义一个称为 key 的变量,用它指您作为分组依据的值。虽然本例是按单个字段进行分组的,但是,如果您需要根据多个值进行分组,Jaql 也支持您按记录分组。

此外,您还可以指示,将结果写入新的 JSON 记录,每一条 JSON 记录将由名称/值对组成。groupingKey 将包含在记录中发现的 ISO 语言代码的值,而 num 将包含每个值的计数。为每一个独特的分组值调用一次 into 子句,它允许您为分组构建输出值。在 into 子句中的表达式里,您可以使用所提供的组名称 (key) 来引用当前分组值,变量 $ 将是一个数组,其中的所有记录都被分配给该组。因此,count($) 会提供所有记录的计数。

之前的 清单 15 中所示的输出示例包括返回的数组中所包含的三条 JSON 记录。您可以看到,有一条推文记录是用德语(ISO 代码 “de”)编写的,有 12 个推文是用英语(ISO 代码 “en”)编写的,还有两个推文是用法语(ISO 代码为 “fr”)编写的。

使用 tee 函数拆分查询输出

如果您熟悉 Unix 命令,您可能已经使用 tee 将一个程序的输出拆分至两个目的地,比如终端显示和文件。Jaql 也包含类似的结构,使您能够根据函数调用的结果拆分查询的输出。典型用法包括将输出编写到两个不同的文件。

如清单 16 所示,Jaql 的 tee 函数与 filter 表达式配合使用,将数据写入两个不同的本地文件,这对于诊断或针对特定应用程序的需求定制文件输入可能很有用。

清单 16. 使用 Jaql 的 tee 函数将查询输出拆分到两个文件
tweetRecords -> tee( -> filter $.iso_language_code == "en" -> 
                        write(jsonTextFile("file:///home/hdpadmin/en.json"))
                     -> filter $.iso_language_code != "en" -> 
                        write(jsonTextFile("file:///home/hdpadmin/non-en.json"))
                   );

本例中采用了一个简单的方法,将基于英语的推文记录写入 en.json 文件,并将其他推文记录保存在本地的 non-en.json 文件中。因此,如果您想对基于英语的消息应用文本分析函数,那么您已经将它们隔离在一个特定文件中。

需要注意的是,file:/// 在这里被用作架构的路径 URI;这表示引用了本地文件系统(而不是 HDFS)。

联合数据

与 SQL 和其他查询语言一样,Jaql 使您能够联合来自多个来源(多个 JSON 数组)的数据。Jaql 的 union 表达式不删除重复的数据,所以它的功能类似于 SQL 的 UNION ALL 表达式。

清单 17 显示了一个简单的场景,使用 union 表达式从存储在 HDFS 中的两个 SequenceFiles 中读取数据。

清单 17. 来自两个文件的数据的联合
union(read(seq('/user/idcuser/sampleData/twitter/tweet1.seq')), 
      read(seq('/user/idcuser/sampleData/twitter/tweet2.seq'))
      );

这些文件是使用基于 REST 的 API(参见 清单 2)根据不同时间检索的 Twitter 数据创建的,并使用 Jaql 的 write() 函数将这些文件写入 HDFS(参见 清单 6)。使用收集到的这些数据,通过将两个读取操指定为 union 表达式的参数,就可以联合这些文件,这非常简单。

您可以选择将该联合操作的结果写入 HDFS。

联接数据

为了完成对 Jaql 的简单了解,您还需要探索如何联接数据。之前在 清单 3 中定义的 ibm 变量包含从关系型 DBMS 中检索的数据,该 DBMS 中的 IBM 员工以发布社交媒体消息作为其工作的一部分。试想一下以下情形:您希望联接该企业数据和 清单 11 的 Query 1 中定义的 tweetRecords 变量所表示的推文。使用 ibm.ID 和 tweetRecords.id 字段作为联接键,如 清单 18 的第二行上的 where 子句所示。请注意,字段名称在 Jaql 中是区分大小写的。

清单 18. 联接从社交媒体站点提取的数据和从关系型 DBMS 中提取的数据
join tweetRecords, ibm
where tweetRecords.id == ibm.ID
into {
ibm.ID, ibm.NAME, ibm.TITLE, tweetRecords.text, tweetRecords.created_at
} 
-> sort by [$.ID asc];
                
// Sample output 
[
    {
        "ID": "159088234",
        "NAME": "James Smith",
        "TITLE": "Consultant",
        "text": "Great news! \"RT @OwnPrivateCloud: Can Watson, 
            IBM\'s Supercomputer, Cure Cancer? http://sampleURL.co/dgttTra \"",
        "created_at": "Mon, 30 Apr 2012 17:28:43 +0000"
    },
    {
        "ID": "370988953",
        "NAME": "John Taylor",
        "TITLE": "IT Specialist",
        "text": "http://someURL.co/45x044 Can Watson, 
            IBM\'s Supercomputer, Cure Cancer? - CIO",
        "created_at": "Mon, 30 Apr 2012 17:11:58 +0000"
    }
]

在清单 18 第三行开始的 into 子句定义了查询结果的一个新的 JSON 记录。在本例中,每条记录包含五个字段。前三个字段基于从关系型 DBMS 中检索的数据,而剩下的两个字段则基于社交媒体数据。最后,您需要对输出进行排序。


并行性和 Jaql

现在,您应该已经熟悉了 Jaql 的基本知识,您可以重新阅读前面介绍过的主题:Jaql 通过 MapReduce 框架使用并行性。虽然可以在 BigInsights 集群中跨多个节点拆分(并行执行)许多查询任务,但某些查询,或者,更常见的是 Jaql 查询的某些部分,可能无法利用 MapReduce 并行性。对于 Jaql,可以制定一个查询执行计划,跨多个 Map 和 Reduce 任务对工作进行拆分,以下两个基本的查询属性必须存在。

  1. 查询的输入/输出数据必须适用于分区。 驻留在某种类型的分布式存储系统(如 HDFS、HBase,甚至某些 DBMS)中的数据通常是适用的数据。不合适的数据包括从 Web 服务中读取的数据、存储在本地文件系统中的 JSON 数据,以及包含跨越多行的记录的 JSON 数据。
  2. 在 Jaql 中使用的运算符必须适合于分区,此外,Jaql 必须知道如何通过 MapReduce 并行化它们。 所有 Jaql 的大数组运算符都满足这些标准,这些运算符包括 transform、expand、filter、sort、group by、join、tee 和 union。然而,有些函数调用可能不会满足这些标准。

此外,Jaql 非常适合用来处理包含许多小对象的 JSON 数组,但它们对于包含少量大型对象的数据结构则不太理想,因为无法对单个大型对象进行 “拆分”,因而无法利用 MapReduce 的并行性。

要检查会并行执行哪些部分的 Jaql 查询,串行执行哪些部分,最好的方法或许是使用 explain 语句来显示查询的执行计划。只需使用 explain 作为所有查询的前缀,并检查 MapReduce 和 mrAggregate 运算符的输出,这些运算符表明使用了一个 MapReduce 作业来执行部分或全部查询。

虽然对 Jaql 的 explain 特性的详细讨论已超出了本教程的范围,但您可以快速回顾一个简单的示例。在本文前面的部分,在 清单 2 中定义了 tweets 变量,以包含从 Web 服务中读取 Twitter 数据的结果。稍后,在转换该数据子集并将它写入 HDFS 中的某个 SequenceFile 之后,清单 7 会将该文件读入 tweetHDFS 变量。

查看清单 19 中所示的两个 Jaql 查询,它们都返回相同的结果,即一个 id_str 值的数组。(Twitter 最初返回该字段作为更大型 JSON 记录中的结果数组的一部分。tweetsHDFS 数据以结果数组的投射为基础,所以 Query 2 不需要引用结果数组。

清单 19. 要解释的两个查询
// Query 1:  get the "id_str" field from Web source
// and return data in as a "flat" array 
tweets -> expand $.results.id_str;
                
                
// Query 2:  get the "id_str" field from HDFS file 
// Data structure was already "flattened" when written to HDFS  
tweetsHDFS -> transform $.id_str;

如前所述,Jaql 可以并行化(分区工作) expandtransform 运算符。因此,两个查询都符合在本节开始部分提到的第二个标准。然而,第一个标准则不然。可以使用 MapReduce 作业对 HDFS 中的 SequenceFile 进行分区并处理它们,但 Web 服务的流式传输连接不支持数据分区。因此,可以使用多个 MapReduce 任何来处理前面的 清单 19 中所示的 Query 2,因为您必须为您的 SequenceFile 定义一个包含多个较小的对象(许多 JSON 记录)的结构。另一方面,无法并行化 Query 1,因为它的数据是从一个基于 REST 的服务动态检索到的。

查看 Jaql 的 explain 输出,您可以看到这些查询的两个不同的数据访问计划。清单 20 包含 Query 1 的数据访问计划。

清单 20. 为没有利用并行性的查询解释结果
// Explain for Query 1 
explain tweets -> expand $.results;
                
system::read(system::const({
       "location": "http://search.twitter.com/search.json?q=IBM+Watson",
       "inoptions": {
    "adapter": "com.ibm.jaql.io.stream.StreamInputAdapter",
    "format": "com.ibm.jaql.io.stream.converter.JsonTextInputStream",
    "asArray": false
}
    })) -> expand each $ ( ($).("results") )
;

如您所见,计划缺乏 mapReduce 或 mrAggregate 运算符的任何指示。这意味着,该查询将在一个 Java 虚拟机 (JVM) 内读取和处理数据。

相比之下,清单 21 中所示的 Query 2 的 explain 结果显示,数据作为一个 MapReduce 作业被读取和处理,因为您可以看到,在 explain 输出开始包含 MapReduce 运算符。

清单 21. 为利用并行性的查询解释结果
// Explain for Query 2 
explain tweetsHDFS -> transform $.id_str;
                
(
    $fd_0 = system::mapReduce({ ("input"):(system::const({
       "location": "/user/idcuser/sampleData/twitter/recentTweets.seq",
       "inoptions": {
    "adapter": "com.ibm.jaql.io.hadoop.DefaultHadoopInputAdapter",
    "format": "org.apache.hadoop.mapred.SequenceFileInputFormat",
    "configurator": "com.ibm.jaql.io.hadoop.FileInputConfigurator"
},
        "outoptions": {
    "adapter": "com.ibm.jaql.io.hadoop.DefaultHadoopOutputAdapter",
    "format": "org.apache.hadoop.mapred.SequenceFileOutputFormat",
    "configurator": "com.ibm.jaql.io.hadoop.FileOutputConfigurator"
}
    })), ("map"):(fn(schema [ * ] $mapIn) ($mapIn
-> transform each $ (($).("id_str"))
-> transform each $fv ([null, $fv]))), ("schema"):(system::const({
        "key": schema null,
        "value": schema any
    })), ("output"):(system::HadoopTemp()) }),
    system::read($fd_0)
)
;

您会看到,Jaql transform 运算符被推送到 map 任务中,每个 Mapper 在其数据分区上并行执行所需的操作。这个查询生成一个 Map-only 作业,所以在解释输出中没有包含 reduce 任务。


使用 Jaql 查询输出与 BigSheets

到现在为止,您已经在 HDFS 中将您的查询输出存储为一个 SequenceFile。这些文件对于程序员通常很方便,但业务分析师和其他非技术人员可能认为其他文件格式更容易使用一些。

试想一下以下情形:您希望将一个或多个 Jaql 查询的输出存储在 HDFS 中,并且可以很容易地在随 BigInsights 提供的电子表格风格的工具 BigSheets 中显示其格式。如果您不熟悉 BigSheets,请参阅 参考资料 部分,获得有关适用于业务分析师的此类工具的一篇文章和若干个视频的链接。由于 BigInsights 包括一些不同的 Jaql I/O 适配器,所以很容易调整前面介绍的某个查询示例,将输出存储为一个以字符分隔的文件(.del 文件),这是 BigSheets 可以轻松使用的一种格式。而且,正如您想知道的那样,这个 I/O 工作是 Jaql 可以并行化的工作。

清单 22 显示了如何将一个查询的结果编写为 HDFS 中的分隔符文件。请注意,已经指定了输出文件的架构,它控制了指定字段在文件内的记录中的出现顺序。

清单 22. 操作数据和并将数据存储为分隔符文件
// the tweets variable was defined earlier, in Listing 2 
sheetsData = tweets -> expand $.results
           -> transform { created_at: $.created_at,
                    geo: $.geo,
                    id: $.from_user_id_str,
                    iso_language_code: $.iso_language_code,
                    text: $.text };
                
// write that data as a delimited file in the distributed file system
sheetsData -> write(del('/user/idcuser/sampleData/twitter/tweetRecords.del', 
                schema = schema {created_at, geo, id, iso_language_code, text}));

将文件写入 HDFS 后,就可以启动 BigInsights Web 控制台,并访问它的 Files 选项卡,找到输出,如 图 2 所示。

图 2. 根据写入 HDFS 的 Jaql 输出来定义 BigSheets 集合
Jaql 查询输出的 BigSheets 集合

按照标准程序,定义此数据的 BigSheets 集合。在图 2 的右窗格中,将文件的显示设置从 Text 更改为 Sheets,然后将读者类型重设为以逗号分隔的值,没有标头。有关创建和使用 BigSheets 集合的详细信息,请参阅 参考资料 部分中引用的文章。

顺便说一句,您也可以将 tweetRecords 变量(之前已在 清单 11 中进行定义)内容作为分隔符文件写入 HDFS,如 清单 23 所示。

清单 23. 写入 tweetRecords 表示为分隔符文件的数据
// write records as a delimited file in the distributed file system
tweetRecords -> write(del('/user/idcuser/sampleData/twitter/tweetRecords', 
schema = schema {created_at, geo, id, iso_language_code, text}));

这将在 HDFS 产生一个目录,而不是单一的文件。要查看 BigSheets 中的内容,则需要为整个目录创建一个新集合,而不是为包含在目录中的某个输出文件创建一个新集合。


结束语

本文向您介绍了 Jaql,它是处理大数据的一种查询和脚本语言。本文详细探讨了如何使用 Jaql 与 InfoSphere BigInsights,并利用该语言所提供的各种表达式和函数来读取、写入、筛选、操作社交媒体数据和结构化数据。本文还探讨了 Jaql 如何利用 MapReduce 框架中固有的并行处理,并介绍了如何使用 Jaql 的 explain 特性来确定 BigInsights 为您的查询执行的数据访问路径。最后,了解了一种格式化您的 Jaql 查询结果并将它存储为 BigSheets 很容易使用的格式的方法,BigInsights 还为业务分析师提供了一个电子表格风格的工具。

当然,关于 Jaql 的更多要学习的内容超出了这篇介绍性文章的讨论范围。例如,Jaql 支持用户定义的函数和模块以及许多 SQL 构造。它还支持 BigInsights 中的文本分析,使程序员可以执行由 IBM 提供的文本提取器和自定义文本提取器。有关 Jaql 的更多详细信息,请参阅 参考资料 部分。

致谢

感谢为本文做过贡献或审阅本文的人。按字母顺序排列,他们是:Rafael Coss、Vuk Ercegovac、Scott Gray、 Manoj Kumar 和 Nicolas Morales。

参考资料

学习

获得产品和技术

讨论

条评论

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
ArticleID=839511
ArticleTitle=使用 InfoSphere BigInsights 查询社交媒体和结构化数据
publish-date=10082012