自联接:Cosmos DB 中的查询语言

在查询语言中,数据是无架构的,通常非规范化。 与在关系数据库中一样,联接发生在单个项中,而不是跨实体和集联接数据。 具体而言,联接的范围限定为该项,不能跨多个项和容器发生。

小窍门

如果你发现自己需要跨项和容器联接,请考虑重新工作数据模型以避免这种反模式。

使用单个项进行自联接

让我们看看项内自联接的示例。 考虑包含单个项的容器。 此项表示具有各种大小的产品:

[
  {
    "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 结果集中每个产品项以及 s 自联接 sizes 数组。 *如果关键字可以推断输入集,则仅对投影所有字段有效,但现在有两个输入集(pt)。 由于此约束,我们必须将返回的字段name显式定义为产品以及keydescription大小。

最后,可以使用筛选器查找以 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 FinsElecy 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"
]