couchbase-n1ql中的嵌套和不嵌套

在像Couchbase这样的JSON数据库中建模数据时,开发人员和架构师有两种表示分层数据的选择。 第一个选项是在父对象中嵌入相关对象,如下所示:


另一种方法是分别表示相关对象,并通过标识来引用它们,如下所示:


具体何时使用一种形式或另一种形式超出了本文的范围。 但是无论选择哪一种,N1QL语言都允许开发人员在运行中转换到另一种语言。 这些转换是用两个操作符完成的,NEST和UNNEST。

假设我们有一个订单跟踪应用程序,我们设计了我们的数据库对象模型,让行项目存储在订单文档本身(嵌套)中,如下所示:


现在我们要计算每个订单的应付税款。 我们该怎么做? 订单层没有税务信息;我们只知道每一行项目的税。

我们必须做两件事。 首先,我们必须从订单中提取行项目,然后我们必须按订单汇总税收。 提取将通过一个UNNEST操作完成,聚合将通过一个聚合函数和一个分组依据完成。

我们可以用这个查询提取行项目:


这就产生了这个结果:


这是一个很大的结果。 让我们仔细看看。 结果有五个对象,每个对象有两个字段:“demo”包含整个原始(父)对象,“lineitems”包含原始对象中“lineitems”数组的一个元素。 所以我们提取了每个行项目,并保留了每个原始(父)对象,以备不时之需。

然后我们可以通过演示进行分组。order_id和sum line items。税,使用此查询:


这会产生这种微调结果:


让我们考虑一下基本查询是如何执行的。


计划是这样的:


该计划将扫描演示桶的主索引,获取它引用的syncnavigator 每条记录,并将其输入到扫描操作器中。

如果我们想对订单更有选择性,用指数就足够容易了。 我们向查询添加一个谓词,


我们还在新字段上添加了一个索引,


现在这个计划包含了一个闪亮的新索引扫描操作符,它使用新的索引。


但是,如果我们想对行项目有选择性呢? 假设我们只需要item_id=567的行项目,如下所示:


为此,我们需要一个数组索引,如下所示


这是每个订单中lineitems数组的item_id字段的索引。 然后我们试着解释,但什么也没发生。 没有选择索引。

问题是最初的查询。 Couchbase的当前版本非常注重如何使用数组索引,并且UNNEST子句必须采用特定的格式来匹配索引。 特别是,看看“l。索引定义中的item_id”。 它必须与查询中的字段名和前缀完全匹配。 如果我们像这样更改查询,事情就会正常工作:


然后解释向我们展示了一个不同的操作符,指示索引正在使用:


此处提供了有关阵列索引的更多信息:

现在,假设我们向演示桶中添加第三个文档,如下所示:


如果我们重新运行查询。


。我们看到了一个结果,但是新的订单不包括在内。 这是因为新订单没有行项目,因此不参与连接。

我们可以通过切换到左外部无纸通道来在结果中包含新的顺序,这包括文档,即使它们没有子对象。

查询。


。产生与前一个相同的结果,但这次包含了新的订单:


现在让我们假设我们已经将订单分解为主对象和从属对象,可能是因为主对象变得太大,并且产生了太多的网络流量,如下所示:


这如何迫使我们修改我们的查询? 我们可以这样连接对象和从属对象:


这就产生了这个结果:


然后,我们可以在子数据上进行隧道。要拆分各个行项目,请执行以下操作:


这会产生五个结果(每个行项目一个结果),结构如下:


从那里,我们可以像以前一样聚集。

现在让我们把问题翻过来。 如果数据以订单和行项目作为数据库中的单独条目开始(不需要),并且我们希望将它们与文档下的行项目组合在一起,会怎么样。 例如,我们可能想要每个订单,包括的项目和每个项目的数量。

在这种情况下,数据可以这样表示,使用七个独立的对象:


从这些文档中,我们可以使用NEST操作符,使用以下查询将单个对象转换为嵌套结构:


该查询产生以下结果:


同样,这是一个相当大的结果。 但是让我们仔细看看。 结果中有两个对象,每个订单一个。 每个对象有两个字段。 订单字段位于“ordr”下,行项目位于“li”下的数组中。

但是我们可以进一步简化。 我们只需要每个订单的“订单标识”,我们只需要每个行项目的“项目标识”和“数量”。 我们可以从ordr获得“order_id”。order_id,我们可以使用数组理解从“li”数组中提取“item_id”和“quantity ”,如下所示:


该查询生成以下修剪结果:


让我们回到最初的NEST查询,并检查Couchbase是否会执行它。


这让我们有了这个计划:


这里没有什么大的秘密。 Couchbase将扫描演示桶上的主索引,并探测主索引以获取每个行项目。

如果主对象上有一个条件可以由索引提供服务,Couchbase将使用它。 通过添加一个谓词和一个索引,您可以看到不同之处,如下所示:


现在该计划包含一个索引扫描操作符,它显示Couchbase将使用新的索引:


行项目的条件怎么样? 假设我们有一个谓词项id=555?

在这里,事实证明索引没有帮助。 NEST运算符右侧的谓词在NEST操作后应用。 这可能会对数据设计产生影响。 使用NEST操作时,任何需要选择性的字段都应该放在主体对象中,而不是下推到辅助对象中。

在前面的部分中,我们使用嵌套将键引用的外部对象分组到它们所属的顶级对象中。 但是没有子对象的对象呢?

我们可以通过向数据集添加第三个对象来测试这一点,如下所示:


然后我们重新运行查询,得到与之前完全相同的结果。 订单“6”不存在。 为什么? 问题是订单“6”没有lineitems数组,所以它不包含在连接中。

我们怎么能把它加到结果里呢? 首先,我们需要在查询中切换到左外嵌套操作符。 但这还不够。 仅这一更改就将包括演示桶中的行项目文档。 我们只需要订单文件,即使他们没有行项目。 这给了我们最后一个问题。


。产生这个结果,包括订单“6”:


有时对象变得太大而无法保存在单个文档中,将原始的单个对象分成子对象是有意义的。 例如,这里我们已经从原始对象中分离出原始行项目和价格字段。 请注意“1A”和“5A”文件。


那么我们怎样才能改造原来的对象呢?

我们首先加入“子”字段来重新创建原始对象。


。产生以下结果:


然后,我们也可以添加一个NEST子句来连接行项目。


这就产生了这个结果:


使用文档数据库时,我们可能需要从顶层文档中提取嵌套对象。 在Couchbase N1QL中,这是由UNNEST操作员完成的。

相反,我们可能需要将单个对象分组到它们的顶层文档下。 在Couchbase N1QL中,这是通过NEST操作符完成的。

您可以通过转到您的Couchbase管理控制台的“数据存储桶”选项卡并创建一个“演示”存储桶来创建在UNNEST部分中使用的数据集。 然后转到查询选项卡,并执行以下两个语句:


然后,您可以运行查询:


然后清空演示桶:


为NEST部分设置数据:


并运行查询:


猜你喜欢

转载自blog.csdn.net/weixin_49470452/article/details/107506651