在查询语言中,数据是无架构的,通常非规范化。 不同于关系数据库中跨实体和集合联接数据的方式,联接是在单个项内进行的。 具体而言,联接仅限于该项内,不能跨多个项或容器之间进行。
小窍门
如果你发现自己需要在项和容器之间进行联接查询,请考虑重新设计数据模型,以避免这种反模式。
使用单个项进行自联接
让我们看看项内自联接的示例。 考虑包含单个项的容器。 此项表示具有各种大小的产品:
[
{
"name": "Raiot Jacket",
"sizes": [
{
"key": "s",
"description": "Small"
},
{
"key": "m",
"description": "Medium"
},
{
"key": "l",
"description": "Large"
},
{
"key": "xl",
"description": "Extra Large"
}
]
}
]
如果需要查找特定大小的产品,该怎么办? 通常,需要编写一个查询,该查询具有一个筛选器,该查询检查数组中 sizes 每个潜在索引是否有前缀的值。 在此示例中,该查询查找出所有尺寸以 Large 结尾的产品:
SELECT
*
FROM
products p
WHERE
p.sizes[0].description LIKE "%Large" OR
p.sizes[1].description LIKE "%Large" OR
p.sizes[2].description LIKE "%Large" OR
p.sizes[3].description LIKE "%Large"
此方法可以快速变得不可维持。 查询语法的复杂性或长度会增加数组中潜在项的数量。 此外,此查询不够灵活,无法处理将来可能具有三个以上的尺寸的产品。
在传统的关系型数据库中,尺寸信息通常会单独存放在一张表中,并通过跨表连接后对结果应用过滤条件。 在查询语言中,我们可以使用 JOIN 关键字在项内执行自联接操作:
SELECT
p.name,
s.key,
s.description
FROM
products p
JOIN
s in p.sizes
此查询返回一个简单的数组,其中包含标记数组中每个值的项。
[
{
"name": "Raiot Jacket",
"key": "s",
"description": "Small"
},
{
"name": "Raiot Jacket",
"key": "m",
"description": "Medium"
},
{
"name": "Raiot Jacket",
"key": "l",
"description": "Large"
},
{
"name": "Raiot Jacket",
"key": "xl",
"description": "Extra Large"
}
]
让我们来分析一下这个查询。 该查询现在有两个别名:结果集中每个产品项对应的 p,以及自联接的 sizes 数组对应的 s。
*如果关键字可以推断输入集,则仅对投影所有字段有效,但现在有两个输入集(p和t)。 由于这一限制,我们必须显式地将返回字段定义为来自 product 的 name 和 key,以及来自 sizes 的 description。
最后,可以使用筛选器查找以 Large结尾的大小。 由于我们使用了 JOIN 关键字,因此筛选器足够灵活,可以处理任何可变数量的标记:
SELECT
p.name,
s.key AS size
FROM
products p
JOIN
s in p.sizes
WHERE
s.description LIKE "%Large"
[
{
"name": "Raiot Jacket",
"size": "l"
},
{
"name": "Raiot Jacket",
"size": "xl"
}
]
多个项目自连接
让我们转到一个示例,在此示例中,我们需要在多个项中存在的数组中找到值。 对于此示例,请考虑包含两个产品项的容器。 每个项目都包含与其相关的 colors。
[
{
"name": "Gremon Fins",
"colors": [
"science-blue",
"turbo"
]
},
{
"name": "Elecy Jacket",
"colors": [
"indigo-shark",
"jordy-blue-shark"
]
},
{
"name": "Tresko Pack",
"colors": [
"golden-dream"
]
}
]
如果需要查找名称中包含颜色 blue 的每个项,该怎么办? 可以手动搜索字符串 blue,但需要编写一个复杂查询,该查询包含以下两个特征:
包含
blue子字符串的颜色在各个数组中的索引位置各不相同。Elecy Jacket对于产品,颜色为第二项(索引:1)。 对于Gremon Fins产品,标签是第一项(索引:0)。 该Tresko Pack产品没有任何包含此子字符串的内容。每个项的
colors数组长度不同。Gremon Fins产品和Elecy Jacket产品都有两种颜色,而Tresko Pack产品只有一种颜色。
在这里,关键字 JOIN 是创建项和颜色的交叉积的绝佳工具。 联接对参与联接的各个集合创建一个完整的交叉积。 结果是一组元组,包含该项与目标数组中各个值的所有排列组合。
对我们的示例产品和颜色执行连接操作会生成以下项:
| 产品 | 颜色 |
|---|---|
Gremon Fins |
science-blue |
Gremon Fins |
turbo |
Elecy Jacket |
indigo-shark |
Elecy Jacket |
jordy-blue-shark |
Tresko Pack |
golden-dream |
此示例 NoSQl 查询使用 JOIN 关键字创建跨产品并返回所有排列:
SELECT
p.name,
c AS color
FROM
products p
JOIN
c in p.colors
[
{
"name": "Elecy Jacket",
"color": "indigo-shark"
},
{
"name": "Elecy Jacket",
"color": "jordy-blue-shark"
},
{
"name": "Gremon Fins",
"color": "science-blue"
},
{
"name": "Gremon Fins",
"color": "turbo"
},
{
"name": "Tresko Pack",
"color": "golden-dream"
}
]
与单个项一样,可以在此处应用筛选器以仅查找与特定标记匹配的项目。 例如,此查询查找包含满足本节前面提到的初始要求的所有子字符串 blue 的项目。
SELECT
p.name,
c AS color
FROM
products p
JOIN
c in p.colors
WHERE
c LIKE "%blue%"
[
{
"name": "Elecy Jacket",
"color": "jordy-blue-shark"
},
{
"name": "Gremon Fins",
"color": "science-blue"
}
]
可以进一步优化此查询,以便仅返回满足筛选器的产品的名称。 此示例不投影颜色值,但筛选器仍按预期工作:
SELECT VALUE
p.name
FROM
products p
JOIN
c in p.colors
WHERE
c LIKE "%blue%"
[
"Elecy Jacket",
"Gremon Fins"
]