JSON 入门 - Cosmos DB 中的查询语言(在 Azure 和 Fabric 中)

使用 JavaScript 对象表示法(JSON)是查询语言的核心。 项存储为 JSON,所有查询、表达式和类型都设计为使用 JSON 数据。 有关 JSON 本身的详细信息,请参阅 正式的 JSON 规范

下面是在此上下文中了解 JSON 的一些重要事项:

  • JSON 对象始终以 { 和结尾开头 }
  • 属性可以相互 嵌套
  • 属性值可以是数组。
  • 属性名称区分大小写。
  • 属性名称可以是任何字符串,即使包含空格或特殊字符。

嵌套属性

可以使用点表示法访问嵌套 JSON 属性。 这就像访问大多数编程语言中的属性一样。

下面是包含嵌套 JSON 的示例文档:

[
  {
    "name": "Heatker Women's Jacket",
    "category": "apparel",
    "slug": "heatker-women-s-jacket",
    "sizes": [
      {
        "key": "s",
        "description": "Small"
      }
    ],
    "metadata": {
      "link": "https://www.adventure-works.com/heatker-women-s-jacket/68719520138.p"
    }
  }
]

然后,可以在查询中投影相同的嵌套属性:

SELECT
  p.name,
  p.category,
  p.metadata.link
FROM
  products p
WHERE
  p.name = "Heatker Women's Jacket"

你将获得此预期输出:

[
  {
    "name": "Heatker Women's Jacket",
    "category": "apparel",
    "link": "https://www.adventure-works.com/heatker-women-s-jacket/68719520138.p"
  }
]

数组和集

JSON 支持数组,可以在查询中使用这些数组。 若要访问特定元素,请使用其在数组中的位置。

使用上一节中的同一个示例,可以使用其索引访问数组中的项。 例如,如果想要访问数组中的第一项,我们将使用索引, 0 因为它是查询语言中数组的 从零 开始的索引系统:

SELECT
  p.name,
  p.sizes[0].description AS defaultSize
FROM
  products p
WHERE
  p.name = "Heatker Women's Jacket"

此查询将生成以下 JSON 对象:

[
  {
    "name": "Heatker Women's Jacket",
    "defaultSize": "Small"
  }
]

现在,让我们考虑一个具有较大数组的示例:

[
  {
    "name": "Vencon Kid's Coat",
    "category": "apparel",
    "slug": "vencon-kid-s-coat",
    "colors": [
      "cardinal",
      "disco"
    ],
    "sizes": [
      {
        "key": "m",
        "description": "Medium"
      },
      {
        "key": "l",
        "description": "Large"
      },
      {
        "key": "xl",
        "description": "Extra Large"
      }
    ]
  }
]

通常,你想要使用子查询或自联接来处理数组中的所有元素。 例如,若要将每个颜色作为单独的行获取:

SELECT
  p.name,
  c AS color
FROM
  products p
JOIN
  c IN p.colors
WHERE
  p.name = "Vencon Kid's Coat"

这会生成如下所示的 JSON 数组:

[
  {
    "name": "Vencon Kid's Coat",
    "color": "cardinal"
  },
  {
    "name": "Vencon Kid's Coat",
    "color": "disco"
  }
]

若要检查数组中是否存在特定值,可以在关键字后 WHERE 在筛选器中使用该数组。 此示例使用 子查询 筛选数组的项:

SELECT VALUE
  p.name
FROM
  products p
WHERE
  EXISTS(SELECT VALUE
    c
  FROM
    c IN p.sizes
  WHERE
    c.description LIKE "%Large")

此查询生成字符串的平面 JSON 数组,该数组将包括示例中的项:

[
  ...,
  "Vencon Kid's Coat"
  ...
]

最后,可以通过组合多个属性来构造数组。 在此示例中,将组合多个属性以形成 metadata 数组:

SELECT
  p.name,
  [
    p.category,
    p.slug,
    p.metadata.link
  ] AS metadata
FROM
  products p
WHERE
  p.name = "Heatker Women's Jacket"
[
  {
    "name": "Heatker Women's Jacket",
    "metadata": [
      "apparel",
      "heatker-women-s-jacket",
      "https://www.adventure-works.com/heatker-women-s-jacket/68719520138.p"
    ]
  }
]

迭代

查询语言支持使用 IN 源中的 FROM 关键字对 JSON 数组进行迭代。

请考虑此示例数据集:

[
  {
    "name": "Pila Swimsuit",
    "colors": [
      "regal-blue",
      "rose-bud-cherry"
    ],
    "sizes": [
      {
        "key": "m",
        "description": "Medium"
      },
      {
        "key": "l",
        "description": "Large"
      },
      {
        "key": "xl",
        "description": "Extra Large"
      }
    ]
  },
  {
    "name": "Makay Bikini",
    "colors": [
      "starship"
    ],
    "sizes": [
      {
        "key": "s",
        "description": "Small"
      },
      {
        "key": "m",
        "description": "Medium"
      },
      {
        "key": "l",
        "description": "Large"
      }
    ]
  }
]

第一个示例使用 IN 关键字对每个产品的属性执行迭代 colors

SELECT
  *
FROM
  p IN p.colors
[
  "regal-blue",
  "rose-bud-cherry",
  "starship"
]

还可以使用 WHERE 子句筛选数组中的单个条目。 在此示例中,将 sizes 筛选属性:

SELECT
  p.key
FROM
  p IN p.sizes
WHERE
  p.description LIKE "%Large"
[
  {
    "key": "l"
  },
  {
    "key": "xl"
  },
  {
    "key": "l"
  }
]

使用同一 IN 关键字,可以聚合数组迭代的结果。 在此示例中,查询返回容器中所有项之间求和的标记数:

SELECT VALUE
  COUNT(1)
FROM
  p IN p.sizes

注释

使用 IN 关键字进行迭代时,不能筛选或投影数组之外的任何属性。 而是使用 自联接

Null 值和未定义值

如果文档中不存在属性,则其值为 undefined. 如果属性存在,但设置为 null,则为显式设置的值。 这两者之间的区别 nullundefined 一个重要的区别,可能会导致查询混乱。

例如,此 JSON 对象将具有属性的值 undefinedsku 因为从未定义该属性:

[
  {
    "name": "Witalica helmet",
    "category": "gear",
  }
]

此 JSON 对象将具有相同属性的值 null ,因为尚未使用值定义该属性:

[
  {
    "name": "Witalica helmet",
    "category": "gear",
    "sku": null
  }
]

存在用于检查这些情况的内置函数:

  • IS_NULL 检查属性是否为 null.
  • IS_DEFINED 检查属性是否存在(不是 undefined)。

下面介绍了如何检查这两者:

SELECT
  IS_DEFINED(p.sku) AS isSkuDefined,
  IS_NULL(p.sku) AS isSkuDefinedButNull
FROM
  products p

括号表示法

虽然大多数示例都使用 表示法来指定属性,但始终可以使用 括号 表示法指定相同的属性。

首先,使用嵌套对象作为属性的值 metadata 的简单对象:

[
  {
    "name": "Hikomo Sandals",
    "metadata": {
      "link": "https://www.adventure-works.com/hikomo-sandals/68719519305.p"
    }
  }
]

对于该对象,可以使用括号表示法的组合以三种不同的方式引用metadata.link属性:

SELECT
  p.metadata.link AS metadataLinkDotNotation,
  p["metadata"]["link"] AS metadataLinkBracketNotation,
  p.metadata["link"] AS metadataLinkMixedNotation
FROM
  products p
WHERE
  p.name = "Hikomo Sandals"
[
  {
    "metadataLinkDotNotation": "https://www.adventure-works.com/hikomo-sandals/68719519305.p",
    "metadataLinkBracketNotation": "https://www.adventure-works.com/hikomo-sandals/68719519305.p",
    "metadataLinkMixedNotation": "https://www.adventure-works.com/hikomo-sandals/68719519305.p"
  }
]

小窍门

如果属性名称具有空格、特殊字符或匹配保留字,则必须使用括号表示法来指定属性。

JSON 表达式

可以直接在查询结果中创建 JSON 对象。 让我们以此 JSON 数组为例:

[
  {
    "name": "Diannis Watch",
    "category": "apparel",
    "detailCategory": "apparel-accessories-watches",
    "slug": "diannis-watch",
    "sku": "64801",
    "price": 98,
    "quantity": 159
  },
  {
    "name": "Confira Watch",
    "category": "apparel",
    "detailCategory": "apparel-accessories-watches",
    "slug": "confira-watch",
    "sku": "64800",
    "price": 105,
    "quantity": 193
  }
]

使用最直接的语法,可以使用尖括号({/})和 NoSQL 查询中嵌入的 JSON 语法来影响相对平面 JSON 对象的属性名称:

SELECT {
  "brandName": p.name,
  "department": p.category
}
FROM
  products p
WHERE
  p.detailCategory = "apparel-accessories-watches"
[
  {
    "$1": {
      "brandName": "Diannis Watch",
      "department": "apparel"
    }
  },
  {
    "$1": {
      "brandName": "Confira Watch",
      "department": "apparel"
    }
  }
]

在前面的示例中,结果具有推断的名称,因为未定义显式名称 $1 。 在下一个示例中,结果具有使用别名定义的显式名称 product

SELECT {
  "brandName": p.name,
  "department": p.category
} AS product
FROM
  products p
WHERE
  p.detailCategory = "apparel-accessories-watches"
[
  {
    "product": {
      "brandName": "Diannis Watch",
      "department": "apparel"
    }
  },
  {
    "product": {
      "brandName": "Confira Watch",
      "department": "apparel"
    }
  }
]

或者,可以使用语句中的SELECT VALUE关键字平展VALUE结果:

SELECT VALUE {
  "brandName": p.name,
  "department": p.category
}
FROM
  products p
WHERE
  p.detailCategory = "apparel-accessories-watches"
[
  {
    "brandName": "Diannis Watch",
    "department": "apparel"
  },
  {
    "brandName": "Confira Watch",
    "department": "apparel"
  }
]

更进一步的是,可以使用 JSON 语法来“重塑”结果 JSON 对象,以包括数组、子对象和其他可能未在原始项中显式定义的 JSON 构造。 如果客户端应用程序需要特定架构中的数据与基础数据不匹配,则此方法非常有用。

请考虑以下 JSON 架构,例如:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "required": [
    "id",
    "category",
    "financial"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "category": {
      "type": "object",
      "properties": {
        "department": {
          "type": "string"
        },
        "section": {
          "type": "string"
        }
      },
      "required": [
        "department"
      ]
    },
    "inventory": {
      "type": "object",
      "properties": {
        "stock": {
          "type": "number"
        }
      }
    },
    "financial": {
      "type": "object",
      "properties": {
        "listPrice": {
          "type": "number"
        }
      },
      "required": [
        "listPrice"
      ]
    }
  }
}

该架构允许采用以下格式的 JSON 对象:

[
  {
    "id": "[string]",
    "name": "[string]",
    "category": {
      "department": "[string]",
      "section": "[string]"
    },
    "inventory": {
      "stock": [number]
    },
    "financial": {
      "listPrice": [number]
    }
  }
]

此 NoSQL 查询重新映射原始对象[s] 以符合此新架构:

SELECT VALUE {
  "id": p.sku,
  "name": p.name,
  "category": {
    "department": p.category,
    "section": p.detailCategory
  },
  "inventory": {
    "stock": p.quantity
  },
  "financial": {
    "listPrice": p.price
  }
}
FROM
  products p
WHERE
  p.detailCategory = "apparel-accessories-watches"
[
  {
    "id": "64801",
    "name": "Diannis Watch",
    "category": {
      "department": "apparel",
      "section": "apparel-accessories-watches"
    },
    "inventory": {
      "stock": 159
    },
    "financial": {
      "listPrice": 98
    }
  },
  {
    "id": "64800",
    "name": "Confira Watch",
    "category": {
      "department": "apparel",
      "section": "apparel-accessories-watches"
    },
    "inventory": {
      "stock": 193
    },
    "financial": {
      "listPrice": 105
    }
  }
]

容器别名

默认情况下,关键字FROM后使用的术语引用查询目标容器。 术语本身 不需要 与容器的名称匹配。

例如,如果命名products了容器,则只要该容器是查询的目标,这些查询中的任何一个查询都可以正常运行,并且所有引用products容器:

SELECT
  products.id
FROM
  products
SELECT
  p.id
FROM
  p
SELECT
  items.id
FROM
  items
SELECT
  targetContainer.id
FROM
  targetContainer

若要使 NoSQL 查询更简洁,通常使用较短的名称将容器名称别名化。 可以使用关键字完成 AS 别名:

SELECT
  p.id
FROM
  products AS p

查询语言还具有简写语法,可以在目标容器引用后立即定义别名,而无需关键字 AS 。 此简写功能等效于使用 AS 关键字:

SELECT
  p.id
FROM
  products p

属性别名

还可以使用同一 AS 关键字定义的别名重命名结果中的字段。 对于接下来的几个示例,请考虑此示例数据:

[
  {
    "name": "Oceabelle Scarf",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "metadata": {
      "link": "https://www.adventure-works.com/oceabelle-scarf/68719522190.p"
    }
  },
  {
    "name": "Shinity Socks",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "metadata": {
      "link": "https://www.adventure-works.com/shinity-socks/68719522161.p"
    }
  },
  {
    "name": "Horric Socks",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "metadata": {
      "link": "https://www.adventure-works.com/horric-socks/68719522177.p"
    }
  }
]

在此第一个示例中, metadataLink 别名用于 metadata.link 属性值:

SELECT
  p.name,
  p.metadata.link AS metadataLink
FROM
  products p
[
  {
    "name": "Oceabelle Scarf",
    "metadataLink": "https://www.adventure-works.com/oceabelle-scarf/68719522190.p"
  },
  {
    "name": "Shinity Socks",
    "metadataLink": "https://www.adventure-works.com/shinity-socks/68719522161.p"
  },
  {
    "name": "Horric Socks",
    "metadataLink": "https://www.adventure-works.com/horric-socks/68719522177.p"
  }
]

重要

不能使用别名将值投影为具有空格、特殊字符或保留字的属性名称。 如果要将值的投影更改为具有空格的属性名称,则必须使用 JSON 表达式

例如,

SELECT VALUE {
  "product name": p.name,
  "from": p.metadata.link,
  "detail/category": p.detailCategory
}
FROM
  products p
WHERE
  p.detailCategory = "apparel-accessories-scarfs-and-socks"
[
  {
    "product name": "Oceabelle Scarf",
    "from": "https://www.adventure-works.com/oceabelle-scarf/68719522190.p",
    "detail/category": "apparel-accessories-scarfs-and-socks"
  },
  {
    "product name": "Shinity Socks",
    "from": "https://www.adventure-works.com/shinity-socks/68719522161.p",
    "detail/category": "apparel-accessories-scarfs-and-socks"
  },
  {
    "product name": "Horric Socks",
    "from": "https://www.adventure-works.com/horric-socks/68719522177.p",
    "detail/category": "apparel-accessories-scarfs-and-socks"
  }
]

如果 NoSQL 查询具有两个具有相同名称的属性,请使用别名重命名其中一个或两个属性,以便在投影的结果中消除它们。

请考虑此示例数据:

[
  {
    "name": "Oceabelle Scarf",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "sizes": [
      {
        "key": "s"
      },
      ...
    ],
    "tags": [
      ...
    ]
  },
  {
    "name": "Shinity Socks",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "sizes": [
      ...
      {
        "key": "10"
      },
      ...
    ],
    "tags": [
      ...
      {
        "key": "length"
      }
    ]
  },
  {
    "name": "Horric Socks",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "sizes": [
      ...
      {
        "key": "7"
      },
      ...
    ],
    "tags": [
      {
        "key": "fabric"
      },
      ...
    ]
  }
]

注释

在此示例数据和查询结果中,为了简洁起见,删除了多个属性和值。

此 NoSQL 查询返回 p.sizes[].key 跨产品结果中的属性和 p.tags[].key 属性,但会为每个属性添加别名 key 以避免冲突:

SELECT
  p.name,
  s.key AS sizeKey,
  t.key AS tagKey
FROM
  products p
JOIN
  s IN p.sizes
JOIN
  t in p.tags
WHERE
  p.detailCategory = "apparel-accessories-scarfs-and-socks"
[
  {
    "name": "Oceabelle Scarf",
    "sizeKey": "s",
    "tagKey": "fabric"
  },
  ...
  {
    "name": "Shinity Socks",
    "sizeKey": "10",
    "tagKey": "length"
  },
  ...
  {
    "name": "Horric Socks",
    "sizeKey": "7",
    "tagKey": "fabric"
  }
]