关于N1QL的一切(一):N1QL是如何与开发者熟悉的SQL&JSON完美结合的?

1,576 阅读11分钟
原文链接: rdc.hundsun.com
 N1QL是由Couchbase开发者提供的,目前唯一一个支持在NoSQL数据库上执行SQL查询的实现。
虽然Couchbase社区中关于N1QL的讨论和使用已经相当繁荣,但国内暂无任何公开介绍和指导资料。鉴于在多次技术支持中,遇到N1QL使用的相关问题,以及之前发文中关于N1QL提问的评论(相关内容请戳:千万级海量测试数据运算下,Redis VS Couchbase性能大揭秘!),所以发表该系列文章,本篇作为N1QL入门,将N1QL概念,结构,使用做了较为详细介绍,对高级特性也做略有提及,之后文章会参考阅读反响对后续文章内容进行调整。
【N1QL简介】 N1QL(发音"nickel")是第一个完整融合了SQL和JSON的声明式查询语言,它继承了SQL的丰富表达力和JSON的灵活性,为分布式文档型数据库提供了通用的查询语言和基于JSON的数据模型。N1QL=SQL for JSON,简而言之即使用JSON存储,使用SQL查询。
为了更好的推广,Couchbase开发团队对现有的SQL和数据模型知识进行扩展,帮助开发人员在Couchbase Server上复用传统关系型数据库的知识积累,从而促进开发人员向NoSQL数据库的转型。JSON的灵活性打破了传统表结构的限制,开发人员可以在Couchbase上创建结构丰富的数据模型以应对复杂的查询操作。与此同时N1QL的完整性也避免了在应用程序中维护复杂的关联数据组合。
一个基础的N1QL查询包含以下三部分:
▪ select:指定每个document的返回字段。
▪ from:指定在哪个bucket上执行查询。
▪ where:指定document的筛选条件。
下面列举一个简单的N1QL语句,它从bucket——beer-sample中查询所有由Mishawaka Brewing Company生产的beer的名字: ▪ Query:
▪ Result: 如上图所示,返回的结果集是一个JSON对象,其中最重要的字段是results,它对应一个数组,每个数据元素对应一条查询记录(每一个数组元素对应SDK中返回的每一个“row”)。因为上例的N1QL查询中只挑选了document中的name属性,所以这里的results数组的每个元素对应一条name属性。
【N1QL试用】 一、准备
当一个N1QL语句执行时,query服务首先要和index服务进行交互,寻找与当前查询条件相匹配的index,如果没有对应的index可用,query服务最终会与primary index进行交互并筛选document。因此执行N1QL查询前要求对应的bucket上至少有匹配的index或primary index可用(自定义bucket无默认索引,但示例travel-sample自带了一些预定义index)。
这里先简单示范primary index的创建:
CREATE PRIMARY INDEX ON `travel-sample`;
注意:上面的查询语句中,包裹bucket的是重音符(`)不是单引号(‘)。如果primary index已存在,这里会创建失败。
二、执行N1QL 开发者可以通过以下四种主流方式执行N1QL语句:cbq shell、Web 控制台 、Query REST API、Couchbase SDK。
N1QL语句本质上是一个等待被查询服务解析的字符串,而结果集本质上就是一个JSON对象。通过以上四种方式执行同一条N1QL语句,会得到相同的结果(使用SDK得到的结果集格式会和cbq,REST API稍有不同)。
为了更好的验证N1QL的语法和功能,这里推荐在Couchbase集群搭建阶段创建示例bucket——travel-sample(也可以在Web 控制台Setting下的Sample Buckets标签下进行追加),之后我们以上述四种方式为例,展示如何在travel-sample上编写和执行N1QL查询。
▲ cbq
cbq是Couchbase Server内置的用于N1QL查询的交互式shell,不同的操作系统需要在不同bin路径下启动cbq。
• Linux • Mac OS X • Microsoft Windows 下面举例展示从`travel-sample`中查询callsign字段的值并限制结果集返回5条记录: 返回结果如下: 注意:默认情况下cbq会尝试连接本机Query服务,如果当前节点未激活Query服务,cbq会抛出一个connection refused error。如果需要连接远程节点的Query服务,必须显式指定engine参数:
▲Web控制台
Couchbase Web控制台提供了一个Query控制台,用户可以点击顶部栏的Query标签进入,然后编写和执行N1QL语句: Query工作台主要有以下三个区域:
▪ N1QL查询输入框:提示有“Enter a query here”,用户在此输入需要执行的N1QL语句。
▪ Bucket分析报告框:位于左下角,展示了当前集群维护的bucket相关信息。
▪ 结果集:位于右下角,可以以三种格式展示查询结果集。
操作示例如下:
1、输入N1QL语句: 2、执行N1QL语句: 3、查看执行结果: 通过点击不同结果集展示标签,可以以不同格式的格式查看结果集。
▲REST API
默认情况下,用户可以通过http://ip:8093/query/service访问N1QL查询服务,并通过参数statement传递需要执行的N1QL语句,示例如下:
▲SDK发起N1QL请求
这里以Java SDK为例,用户可以通过bucket.query()执行指定的N1QL语句,下图示范了通过简单字符串和DSL(The N1QL domain-specific language,N1QL定制语言)两种方式构造statement:

三、view
view简单说是一种遵循用户指定脚本筛选数据进行冗余存储,以满足特定查询需要的功能。
对于不支持N1QL查询的Couchbase Server版本(4.0以下),开发人员可以通过view功能达到某些特定目的,如查询数据ID集合,数据过期时间,特定字段集合等,也可以通过MapReduce进行相关统计工作。
view可以通过Web控制台进行管理,也可以以编程方式在运行时动态管理(Web控制台直接在views标签下创建和发布view,这里只对编程方式管理view进行演示)。每个bucket可以添加多个designDocument,而每个designDocument又可以添加多个view。下图展示了通过bucketManager创建两个普通视图和一个空间视图的过程:
上图中构建view的脚本中对bucket中数据进行了筛选,之后用户便可以通过查询view获取筛选后的信息。下图是对普通view和空间view(支持基于地理坐标的查询,常用于支持定位功能的应用)的查询范例:
关于view查询的细节,如key前后缀匹配,view更新模式,view环境设置,MapReduce等,会在另一篇文章中详细介绍,这里只把它当作某种程度上N1QL替代品,做简单介绍。
【N1QL进阶】 一、N1QL语法
Couchbase Server把数据存储在JSON文档中,而每个JSON文档对应一个由多个预定义字段组成的JSON对象。一个对象可以由一个或多个JSON文档组成,对象的一个属性可以是文档中的字段。你可以以key-value的形式存储数据,其中key可以设置为SQL中的主键。

N1QL作为一个数据定义语言(DDL),数据操纵语言(DML)和查询语言,它支持NoSQL上的诸多数据模型,如key-value存储,多值属性和嵌套对象。N它允许你用类SQL的语言访问上述多种格式的文档。因此只要你了解SQL,就可以很轻松掌握N1QL。
N1QL提供了丰富的特性帮助你检索,操纵,转变和创建JSON文档,其中主要特征如下:
▪ SELECT:N1QL中的SELECT是SQL中的SELECT在JSON文档上的扩展,因此你可以将SQL中的知识直接迁移至N1QL。
▪ 数据操纵语言(DML):N1QL提供了DELETE, INSERT, UPDATE和UPSERT 语句用于 创建, 删除, 和修改存储在JSON文档上的数据。
▪ 索引: N1QL 提供了创建和删除index的语句用于方便地管理索引。
▪ 主键访问: 对于以key-value形式存储于Couchbase中的数据,N1QL提供了主键访问的方式
▪ 聚合:N1QL提供了标准的SQL聚合操作符,如MIN, MAX, COUNT, 同时还提供了分组操作符,如GROUP BY子句和分组过滤器HAVING.
▪ Joins:通过在FROM子句中指定JOIN链接符可以从多个文档中检索数据
▪ 嵌套: 嵌套允许你检索一条记录的子记录,即对一个多重JSON文档进行检索。
▪ 解嵌套:解嵌套功能是从一条记录中提取子记录,多次提取的子记录之间相互隔离,且解嵌套可用于简单JSON文档。
▪ 子查询: N1QL支持多重查询,你可以在一个子查询上使用SELECT语句或嵌套另一个子查询, 从而在该子查询的基础上提炼结果集。
▪ UNION,INTERSECT和EXCEPT: N1QL提供了UNION,INTERSECT和EXCEPT 操作符用于操作多个查询。UNION把多个查询的结果集组合为一个所有查询结果的并集,EXCEPT返回所有出现在左侧结果集且未出▪ 现在右侧结果集中的值, INTERSECT返回同时属于多个结果集的值(自动去重)。


二、DSL-N1QL的定制化语言
DSL全称The N1QL domain-specific language,是N1QL的定制化语言。相比于普通的字符串语句,DSL能提供额外的类型安全和重要方法及子句的自动补齐等功能。


Java SDK2.2以后的DSL主要包含以下五点特色:
1.以Select.select(...)为查询入口
2.com.couchbase.client.java.query.dsl.functions下的辅助类聚合了N1QL的所有功能
3.mini DSL通过Case类处理CASE表达式
4.mini DSL通过Collections类处理集合操作符,如ANY, EVERY, ARRAY and FIRST
5.通过Index类管理索引(Index index和primary index 的创建/删除/声明)


在构造N1QL语句时,辅助类类Expression不仅提供了丰富的工厂方法处理文档变量和字符常量,如使用x()包装一个文档变量,s()包装一个字符常量,i()用重音符对一个文档变量转义(i(“beer-sample”)等价于`beer-sample`,而x(),s(),i()等都依赖于静态导入import static com.couchbase.client.java.query.dsl.Expression.*),还组合并简化了一些运算符(如使用gte()代替“greater than or equal to”)。


此外对于DSL暂不支持的一些特殊语法,你可以切换回简单字符串格式构造statement。


所有的查询语句都由一个statement和一个可选的N1QL参数组成,下面以一个最简格式的查询(仅有字符串statement或DSL statement组成)为例:

如图所示,DSL提供了类型安全,并以直观的方式执行N1QL查询(select()方法依赖于一个静态导入import static com.couchbase.client.java.query.Select.*)。


每个查询最终都会返回一个N1qlQueryResult,其结构如下:
▪ parseSuccess:如果查询可以正确解析,即返回true。这个信息在结果集计算中即Couchbase Server未响应结果集时就可以获取。
▪ finalSuccess:整个查询执行成功,且所有结果集数据检索成功并成功流向客户端后,才返回true。
▪ allRows:查询返回的所有记录行。
▪ requestId:由服务器生成的query的唯一标识,可用于在服务端检索对应日志信息。
▪ clientContextId:由用户提供的query分组标识,可用于内部事务标识和具有关联性的query查找。
▪ info:返回一个N1qlMetrics对象,其中包含了结果集的一些度量信息,如resultCount,resultSize和executionTime等等。
▪ errors:如果N1QL查询出现错误,返回一个JOSN对象集合描述警告和错误。


关于DSL的异步查询,占位符使用,查询优化,RYOW等等特性和细节,限于篇幅,下篇文章会和view的高级特性一起进行阐述。


【附录】 1.N1QL子句概览
这里对N1QL支持的字句一一进行演示,并逐个和对应的SQL字句进行比对。
https://www.couchbase.com/products/n1ql



2.N1QL在线交互式教学
无需安装Couchbase Server,直接通过浏览器登陆上述教学平台即可学习并修改样例N1QL查询。该教学样例覆盖了JOIN,NEST,GOROUP BY等典型语句。
https://query-tutorial.couchbase.com/tutorial/#1


3.N1QL小抄
这事官方提供的N1QL基础语法总结,你可以打印出来随时查阅。
http://docs.couchbase.com/files/Couchbase-N1QL-CheatSheet.pdf


4.Couchbase开发者社区
你可以在这里学习,提问,与其他开发者活着Couchbase开发团队讨论N1QL的一切。
https://forums.couchbase.com/c/n1ql

恒生技术之眼原创文章,未经授权禁止转载。详情见(点击)转载须知