Documentdb Sql 简明教程

DocumentDB SQL - Quick Guide

DocumentDB SQL - Overview

DocumentDB 是 Microsoft 最新 NoSQL 文档数据库平台,它在 Azure 上运行。在本教程中,我们将了解使用 DocumentDB 支持的特殊版本的 SQL 查询文档的全部内容。

NoSQL Document Database

DocumentDB 是 Microsoft 最新 NoSQL 文档数据库,但是,当我们说 NoSQL 文档数据库时,NoSQL 和文档数据库具体是什么意思?

  1. SQL 表示结构化查询语言,这是关系数据库的传统查询语言。SQL 通常等同于关系数据库。

  2. 实际上,将 NoSQL 数据库视为非关系数据库更有帮助,因此 NoSQL 实际上指非关系。

有不同类型的 NoSQL 数据库,包括键值存储,例如:

  1. Azure Table Storage

  2. Column-based stores, like Cassandra

  3. Graph databases, like NEO4

  4. 文档数据库,如 MongoDB 和 Azure DocumentDB

Why SQL Syntax?

这乍听起来可能很奇怪,但在 DocumentDB 中将其称为 NoSQL 数据库时,我们使用 SQL 进行查询。如上所述,这是植根于 JSON 和 JavaScript 语义的特殊版本的 SQL。

  1. SQL 仅仅是一种语言,但它也是一种非常流行且丰富且富有表现力的语言。因此,使用 SQL 的某种方言似乎肯定是个好主意,而不是想出一种全新的查询表达方式,如果你想要从你的数据库中获取文档话,就需要学习这种新的表达方式。

  2. SQL 专为关系数据库设计,而 DocumentDB 是一个非关系文档数据库。DocumentDB 团队实际上已经针对文档数据库的非关系世界改编了 SQL 语法,这就是通过将 SQL 植根于 JSON 和 JavaScript 中的含义。

  3. 此语言仍然能直接读出熟悉的 SQL,但其语义全部基于无模式 JSON 文档,而不是关系表。在 DocumentDB 中,我们将处理 JavaScript 数据类型,而不是 SQL 数据类型。我们将熟悉 SELECT、FROM、WHERE 等,但带有 JavaScript 类型,这些类型仅限于数字和字符串,对象、数组、布尔值和 null 值远少于各种各样的 SQL 数据类型。

  4. 类似地,表达式被计算为 JavaScript 表达式,而不是 T-SQL 的某种形式。例如,在反范式化数据的领域中,我们不打交道的是行和列,而是包含嵌套数组和对象的层次结构的无模式文档。

How does SQL Work?

DocumentDB 团队已经通过多种创新方式回答了这个问题。如下列出了其中的少数几种 −

  1. 首先,假设您尚未更改默认行为以自动为文档中的每个属性建立索引,您可以在查询中使用点号标记来导航路径到任何属性,无论它在文档中嵌套的有多深。

  2. 您还可以执行文档内联接,在这种连接中,嵌套数组元素及其父元素在文档内以非常类似于在关系世界中两张表之间执行连接的方式进行连接。

  3. 您的查询可以按原样从数据库返回文档,或者您可以根据您想要的多或少文档数据来投影您想要的任何自定义 JSON 形状。

  4. DocumentDB 中的 SQL 支持许多常见运算符,包括 − 算术和按位运算,AND 和 OR 逻辑,相等性和范围比较,字符串连接。

  5. 查询语言还支持许多内置函数。

DocumentDB SQL - Select Clause

Azure 门户有一个查询浏览器,它让我们针对我们的 DocumentDB 数据库运行任何 SQL 查询。我们将使用查询浏览器演示查询语言的许多不同功能和特性,从最简单的查询开始。

Step 1 - 打开 Azure 门户并在数据库边栏中单击查询浏览器边栏。

query explorer blade

请记住,查询在集合范围内运行,所以查询浏览器允许我们在该下拉菜单中选择集合。我们将让它设置为我们的包含三个文档的 Families 集合。让我们在该示例中考虑三个文档。

以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]

      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]

      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

查询浏览器打开并带有这样简单的查询 SELECT * FROM c,它简单地从集合检索所有文档。尽管简单,但它仍然与关系数据库中的等效查询有很大的不同。

Step 2 - 在关系数据库中,SELECT * 表示返回所有列,而在 DocumentDB 中,这意味着您希望结果中的每个文档完全按其在数据库中存储的方式返回。

但是,当您选择特定属性和表达式而不是仅仅发出 SELECT * 时,那么您就会针对结果中的每个文档规划您想要的新形状。

Step 3 - 单击“运行”执行查询并打开结果边栏。

open results blade

可以看到检索到 WakefieldFamily、SmithFamily 和 AndersonFamily。

以下三份文档是 SELECT * FROM c 查询结果检索到的。

[
   {
      "id": "WakefieldFamily",
      "parents": [
         {
            "familyName": "Wakefield",
            "givenName": "Robin"
         },

         {
            "familyName": "Miller",
            "givenName": "Ben"
         }
      ],

      "children": [
         {
            "familyName": "Merriam",
            "givenName": "Jesse",
            "gender": "female",
            "grade": 6,

            "pets": [
               {
                  "givenName": "Charlie Brown",
                  "type": "Dog"
               },

               {
                  "givenName": "Tiger",
                  "type": "Cat"
               },

               {
                  "givenName": "Princess",
                  "type": "Cat"
               }
            ]
         },

         {
            "familyName": "Miller",
            "givenName": "Lisa",
            "gender": "female",
            "grade": 3,

            "pets": [
               {
                  "givenName": "Jake",
                  "type": "Snake"
               }
            ]
         }
      ],

      "location": {
         "state": "NY",
         "county": "Manhattan",
         "city": "NY"
      },

      "isRegistered": false,
      "_rid": "Ic8LAJFujgECAAAAAAAAAA==",
      "_ts": 1450541623,
      "_self": "dbs/Ic8LAA==/colls/Ic8LAJFujgE=/docs/Ic8LAJFujgECAAAAAAAAAA==/",
      "_etag": "\"00000500-0000-0000-0000-567582370000\"",
      "_attachments": "attachments/"
   },

   {
      "id": "SmithFamily",
      "parents": [
         {
            "familyName": "Smith",
            "givenName": "James"
         },

         {
            "familyName": "Curtis",
            "givenName": "Helen"
         }
      ],

      "children": [
         {
            "givenName": "Michelle",
            "gender": "female",
            "grade": 1
         },

         {
            "givenName": "John",
            "gender": "male",
            "grade": 7,

            "pets": [
               {
                  "givenName": "Tweetie",
                  "type": "Bird"
               }
            ]
         }
      ],

      "location": {
         "state": "NY",
         "county": "Queens",
         "city": "Forest Hills"
      },

      "isRegistered": true,
      "_rid": "Ic8LAJFujgEDAAAAAAAAAA==",
      "_ts": 1450541623,
      "_self": "dbs/Ic8LAA==/colls/Ic8LAJFujgE=/docs/Ic8LAJFujgEDAAAAAAAAAA==/",
      "_etag": "\"00000600-0000-0000-0000-567582370000\"",
      "_attachments": "attachments/"
   },

   {
      "id": "AndersenFamily",
      "lastName": "Andersen",

      "parents": [
         {
            "firstName": "Thomas",
            "relationship": "father"
         },

         {
            "firstName": "Mary Kay",
            "relationship": "mother"
         }
      ],

      "children": [
         {
            "firstName": "Henriette Thaulow",
            "gender": "female",
            "grade": 5,

            "pets": [
               "givenName": "Fluffy",
               "type": "Rabbit"
            ]
         }
      ],

      "location": {
         "state": "WA",
         "county": "King",
         "city": "Seattle"
      },

      "isRegistered": true,
      "_rid": "Ic8LAJFujgEEAAAAAAAAAA==",
      "_ts": 1450541624,
      "_self": "dbs/Ic8LAA==/colls/Ic8LAJFujgE=/docs/Ic8LAJFujgEEAAAAAAAAAA==/",
      "_etag": "\"00000700-0000-0000-0000-567582380000\"",
      "_attachments": "attachments/"
   }
]

但是,这些结果还包括系统生成的所有以下划线字符为前缀的属性。

DocumentDB SQL - From Clause

在本章中,我们将介绍 FROM 子句,其与常规 SQL 中的标准 FROM 子句的作用完全不同。

查询总是运行在特定集合的上下文中,不能在集合中的文档之间进行联接,这让我们疑惑为什么需要 FROM 子句。事实上,我们不需要,但是如果不包含它,那么我们就不会查询集合中的文档。

此子句的目的是指定查询必须在其上运行的数据源。通常整个集合都是源,但也可以指定集合的子集。 FROM <from_specification> 子句是可选的,除非在查询中对源进行了筛选或投影。

让我们再次看同一个例子。以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}
wakefield family

在上面的查询中,“ SELECT * FROM c ”表示整个 Families 集合是用来枚举的源。

Sub-documents

也可以将源缩小到更小的子集。当我们只想检索每个文档中的子树时,子根就可以成为源,如下面的示例所示。

sub documents

当我们运行以下查询时 −

SELECT * FROM Families.parents

将检索以下子文档。

[
   [
      {
         "familyName": "Wakefield",
         "givenName": "Robin"
      },

      {
         "familyName": "Miller",
         "givenName": "Ben"
      }
   ],

   [
      {
         "familyName": "Smith",
         "givenName": "James"
      },

      {
         "familyName": "Curtis",
         "givenName": "Helen"
      }
   ],

   [
      {
         "firstName": "Thomas",
         "relationship": "father"
      },

      {
         "firstName": "Mary Kay",
         "relationship": "mother"
      }
   ]
]

根据此查询结果,我们可以看到仅检索了 parents 子文档。

DocumentDB SQL - Where Clause

在本章中,我们将介绍 WHERE 子句,它也像 FROM 子句一样是可选的。它用于在从源提供的 JSON 文档形式获取数据时指定条件。任何 JSON 文档都必须评估指定条件为“true”,才能被视为结果。如果给定的条件满足,则仅以 JSON 文档形式返回特定数据。我们可以使用 WHERE 子句过滤记录并仅获取必要的记录。

在该示例中,我们将考虑同一三个文档。以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

让我们看一个简单的示例,其中使用 WHERE 子句。

where clause

此查询中,在 WHERE 子句中指定了 (WHERE f.id = "WakefieldFamily") 条件。

SELECT *
FROM f
WHERE f.id = "WakefieldFamily"

执行上述查询时,它会返回 WakefieldFamily 的完整 JSON 文档,如下面的输出所示。

[
   {
      "id": "WakefieldFamily",

      "parents": [
         {
            "familyName": "Wakefield",
            "givenName": "Robin"
         },

         {
            "familyName": "Miller",
            "givenName": "Ben"
         }
      ],

      "children": [
         {
            "familyName": "Merriam",
            "givenName": "Jesse",
            "gender": "female",
            "grade": 6,

            "pets": [
               {
                  "givenName": "Charlie Brown",
                  "type": "Dog"
               },

               {
                  "givenName": "Tiger",
                  "type": "Cat"
               },

               {
                  "givenName": "Princess",
                  "type": "Cat"
               }
            ]
         },

         {
            "familyName": "Miller",
            "givenName": "Lisa",
            "gender": "female",
            "grade": 3,

            "pets": [
               {
                  "givenName": "Jake",
                  "type": "Snake"
               }
            ]

         }
      ],

      "location": {
         "state": "NY",
         "county": "Manhattan",
         "city": "NY"
      },

      "isRegistered": false,
      "_rid": "Ic8LAJFujgECAAAAAAAAAA==",
      "_ts": 1450541623,
      "_self": "dbs/Ic8LAA==/colls/Ic8LAJFujgE=/docs/Ic8LAJFujgECAAAAAAAAAA==/",
      "_etag": "\"00000500-0000-0000-0000-567582370000\"",
      "_attachments": "attachments/"
   }
]

DocumentDB SQL - Operators

运算符是主要在 SQL WHERE 子句中用来执行运算(如比较和算术运算)的保留字或字符。DocumentDB SQL 还支持各种标量表达式。最常用的表达式是 binary and unary expressions

目前支持以下 SQL 运算符,并且可以在查询中使用它们。

SQL Comparison Operators

以下是 DocumentDB SQL 语法中所有比较运算符的列表。

S.No.

Operators & Description

1

= 检查两个操作数的值是否相等。如果相等,则条件为 true。

2

!= 检查两个操作数的值是否不等。如果不等,则条件为 true。

3

&lt;&gt; 检查两个操作数的值是否不等。如果不等,则条件为 true。

4

&gt; 检查左操作数的值是否大于右操作数的值。如果相等,则条件为 true。

5

&lt; 检查左操作数的值是否小于右操作数的值。如果相等,则条件为 true。

6

&gt;= 检查左操作数的值是否大于或等于右操作数的值。如果相等,则条件为 true。

7

&#8656; 检查左操作数的值是否小于或等于右操作数的值。如果相等,则条件为 true。

SQL Logical Operators

以下是 DocumentDB SQL 语法中所有逻辑运算符的列表。

S.No.

Operators & Description

1

AND AND 运算符允许在 SQL 语句的 WHERE 子句中存在多个条件。

2

BETWEEN BETWEEN 运算符用于搜索给定最小值和最大值的集合值。

3

IN IN 运算符用于将一个值与已指定的一系列文本值进行比较。

4

OR OR 运算符用于组合 SQL 语句 WHERE 子句中的多个条件。

5

NOT NOT 运算符会逆转与之结合使用的逻辑运算符的含义。例如,NOT EXISTS、NOT BETWEEN、NOT IN 等。这是一个否定运算符。

SQL Arithmetic Operators

以下是 DocumentDB SQL 语法中所有算术运算符的列表。

S.No.

Operators & Description

1

+ Addition - 加法:将运算符两侧的值相加。

2

- Subtraction - 减法:用左操作数减去右操作数。

3

Multiplication - 乘法:将运算符两侧的值相乘。

4

/ Division - 除法:用左操作数除以右操作数。

5

% Modulus − 将左操作数除以右操作数并返回余数。

在这个示例中我们也会考虑相同的文档。以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

我们来通过一个简单的示例了解一下 WHERE 子句中比较运算符的使用。

comparison operator

在这个查询中,在 WHERE 子句中指定了 (WHERE f.id = "WakefieldFamily") 条件,它将检索 id 等于 WakefieldFamily 的文档。

SELECT *
FROM f
WHERE f.id = "WakefieldFamily"

执行上述查询时,它会返回 WakefieldFamily 的完整 JSON 文档,如下面的输出所示。

[
   {
      "id": "WakefieldFamily",
      "parents": [
         {
            "familyName": "Wakefield",
            "givenName": "Robin"
         },

         {
            "familyName": "Miller",
            "givenName": "Ben"
         }
      ],

      "children": [
         {
            "familyName": "Merriam",
            "givenName": "Jesse",
            "gender": "female",
            "grade": 6,

            "pets": [
               {
                  "givenName": "Charlie Brown",
                  "type": "Dog"
               },

               {
                  "givenName": "Tiger",
                  "type": "Cat"
               },

               {
                  "givenName": "Princess",
                  "type": "Cat"
               }
            ]

         },

         {
            "familyName": "Miller",
            "givenName": "Lisa",
            "gender": "female",
            "grade": 3,

            "pets": [
               {
                  "givenName": "Jake",
                  "type": "Snake"
               }
            ]
         }
      ],

      "location": {
         "state": "NY",
         "county": "Manhattan",
         "city": "NY"
      },

      "isRegistered": false,
      "_rid": "Ic8LAJFujgECAAAAAAAAAA==",
      "_ts": 1450541623,
      "_self": "dbs/Ic8LAA==/colls/Ic8LAJFujgE=/docs/Ic8LAJFujgECAAAAAAAAAA==/",
      "_etag": "\"00000500-0000-0000-0000-567582370000\"",
      "_attachments": "attachments/"
   }
]

我们来看另一个示例,其中该查询将检索成绩大于 5 的儿童数据。

SELECT *
FROM Families.children[0] c
WHERE (c.grade > 5)

执行上述查询后,将按照输出中所示检索以下子文档。

[
   {
      "familyName": "Merriam",
      "givenName": "Jesse",
      "gender": "female",
      "grade": 6,

      "pets": [
         {
            "givenName": "Charlie Brown",
            "type": "Dog"
         },

         {
            "givenName": "Tiger",
            "type": "Cat"
         },

         {
            "givenName": "Princess",
            "type": "Cat"
         }
      ]
   }
]

DocumentDB SQL - Between Keyword

BETWEEN 关键字用于针对 SQL 中的如同值范围的查询进行表达。BETWEEN 可用于字符串或数字。在 DocumentDB 和 ANSI SQL 中使用 BETWEEN 的主要区别在于,您可以在混合类型的属性上表达范围查询。

例如,在某些文档中,您可能将“grade”作为数字,而在其他文档中它可能是字符串。在这些情况下,“未定义”两种不同类型的结果之间的比较,并且该文档将被跳过。

我们来考虑先前示例中的三个文档。以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

我们来通过一个示例了解一下,其中该查询返回所有第一个孩子的成绩介于 1-5(包括边界)的家庭文档。

returns family documents

以下是使用 BETWEEN 关键字并随后使用 AND 逻辑运算符的查询。

SELECT *
FROM Families.children[0] c
WHERE c.grade BETWEEN 1 AND 5

执行以上查询后,将产生以下输出。

[
   {
      "givenName": "Michelle",
      "gender": "female",
      "grade": 1
   },

   {
      "firstName": "Henriette Thaulow",
      "gender": "female",
      "grade": 5,

      "pets": [
         {
            "givenName": "Fluffy",
            "type": "Rabbit"
         }
      ]
   }
]

要显示超出先前示例范围的成绩,请如以下查询所示使用 NOT BETWEEN。

SELECT *
FROM Families.children[0] c
WHERE c.grade NOT BETWEEN 1 AND 5

执行此查询时。它生成以下输出。

[
   {
      "familyName": "Merriam",
      "givenName": "Jesse",
      "gender": "female",
      "grade": 6,

      "pets": [
         {
            "givenName": "Charlie Brown",
            "type": "Dog"
         },

         {
            "givenName": "Tiger",
            "type": "Cat"
         },

         {
            "givenName": "Princess",
            "type": "Cat"
         }
      ]
   }
]

DocumentDB SQL - In Keyword

IN 关键字可用于检查指定值是否与列表中的任何值匹配。IN 运算符允许您在 WHERE 子句中指定多个值。IN 等同于链接多个 OR 子句。

与先前示例中所做一样,考虑相同的三个文档。以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

我们来看一个简单的例子。

in keyword

以下查询将检索 familyName 为 “Smith”或 Wakefield 的数据。

SELECT *
FROM Families.parents[0] f
WHERE f.familyName IN ('Smith', 'Wakefield')

执行以上查询后,将产生以下输出。

[
   {
      "familyName": "Wakefield",
      "givenName": "Robin"
   },

   {
      "familyName": "Smith",
      "givenName": "James"
   }
]

我们来考虑另一个示例,其中将检索所有 id 为 "SmithFamily" 或 "AndersenFamily" 的家庭文档。以下是查询。

SELECT *
FROM Families
WHERE Families.id IN ('SmithFamily', 'AndersenFamily')

执行以上查询后,将产生以下输出。

[
   {
      "id": "SmithFamily",
      "parents": [
         {
            "familyName": "Smith",
            "givenName": "James"
         },

         {
            "familyName": "Curtis",
            "givenName": "Helen"
         }
      ],

      "children": [
         {
            "givenName": "Michelle",
            "gender": "female",
            "grade": 1
         },

         {
            "givenName": "John",
            "gender": "male",
            "grade": 7,

            "pets": [
               {
                  "givenName": "Tweetie",
                  "type": "Bird"
               }
            ]
         }
      ],

      "location": {
         "state": "NY",
         "county": "Queens",
         "city": "Forest Hills"
      },

      "isRegistered": true,
      "_rid": "Ic8LAJFujgEDAAAAAAAAAA==",
      "_ts": 1450541623,
      "_self": "dbs/Ic8LAA==/colls/Ic8LAJFujgE=/docs/Ic8LAJFujgEDAAAAAAAAAA==/",
      "_etag": "\"00000600-0000-0000-0000-567582370000\"",
      "_attachments": "attachments/"
   },

   {
      "id": "AndersenFamily",
      "lastName": "Andersen",

      "parents": [
         {
            "firstName": "Thomas",
            "relationship": "father"
         },

         {
            "firstName": "Mary Kay",
            "relationship": "mother"
         }
      ],

      "children": [
         {
            "firstName": "Henriette Thaulow",
            "gender": "female",
            "grade": 5,

            "pets": [
               {
                  "givenName": "Fluffy",
                  "type": "Rabbit"
               }
            ]
         }
      ],

      "location": {
         "state": "WA",
         "county": "King",
         "city": "Seattle"
      },

      "isRegistered": true,
      "_rid": "Ic8LAJFujgEEAAAAAAAAAA==",
      "_ts": 1450541624,
      "_self": "dbs/Ic8LAA==/colls/Ic8LAJFujgE=/docs/Ic8LAJFujgEEAAAAAAAAAA==/",
      "_etag": "\"00000700-0000-0000-0000-567582380000\"",
      "_attachments": "attachments/"
   }
]

DocumentDB SQL - Value Keyword

当您知道您仅返回单个值时,VALUE 关键字可以通过避免创建完整对象开销来帮助生成精简的结果集。VALUE 关键字提供了一种返回 JSON 值的方法。

我们来看一个简单的例子。

value keyword

以下是包含 VALUE 关键字的查询。

SELECT VALUE "Hello World, this is DocumentDB SQL Tutorial"

当执行此查询时,他返回标量“Hello World,这是 DocumentDB SQL 教程”。

[
   "Hello World, this is DocumentDB SQL Tutorial"
]

在另一个例子中,我们来看看上面例子中的三个文档。

以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

以下是查询。

SELECT VALUE f.location
FROM Families f

当执行此查询时,他返回地址而不带位置标签。

[
   {
      "state": "NY",
      "county": "Manhattan",
      "city": "NY"
   },

   {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   {
      "state": "WA",
      "county": "King",
      "city": "Seattle"
   }
]

如果我们现在指定相同的查询而无 VALUE 关键字,那么将会返回带有位置标签的地址。以下是查询。

SELECT f.location
FROM Families f

执行此查询时,将得到以下输出。

[
   {
      "location": {
         "state": "NY",
         "county": "Manhattan",
         "city": "NY"
      }
   },

   {
      "location": {
         "state": "NY",
         "county": "Queens",
         "city": "Forest Hills"
      }
   },

   {
      "location": {
         "state": "WA",
         "county": "King",
         "city": "Seattle"
      }
   }
]

DocumentDB SQL - Order By Clause

Microsoft Azure DocumentDB 支持使用 JSON 文档中的 SQL 查询文档。您可以使用查询中的 ORDER BY 子句按照数字和字符串对集合中的文档进行排序。该子句可以包含一个可选的 ASC/DESC 参数,以指定必须检索结果的顺序。

我们将考虑与前例中相同的文档。

以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

我们来看一个简单的例子。

order by clause

以下是包含 ORDER BY 关键字的查询。

SELECT  f.id, f.children[0].givenName,f.children[0].grade
FROM Families f
ORDER BY f.children[0].grade

执行以上查询后,将产生以下输出。

[
   {
      "id": "SmithFamily",
      "givenName": "Michelle",
      "grade": 1
   },

   {
      "id": "AndersenFamily",
      "grade": 5
   },

   {
      "id": "WakefieldFamily",
      "givenName": "Jesse",
      "grade": 6
   }
]

我们再考虑另一个简单的例子。

order by clauses

以下是对包含可选 ORDER BY 关键字和 DESC 关键字的查询,在其后加上注释。

SELECT f.id, f.parents[0].familyName
FROM Families f
ORDER BY f.parents[0].familyName DESC

执行上述查询时,将会产生以下输出。

[
   {
      "id": "WakefieldFamily",
      "familyName": "Wakefield"
   },

   {
      "id": "SmithFamily",
      "familyName": "Smith"
   },

   {
      "id": "AndersenFamily"
   }
]

DocumentDB SQL - Iteration

在 DocumentDB SQL 中,Microsoft 添加了一种新构造,可与 IN 关键字一起使用,为遍历 JSON 数组提供支持。FROM 子句中提供了对迭代的支持。

我们将再次考虑前面例子里类似的三个文档。

以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

我们来看一个没有 FROM 子句中的 IN 关键字的简单示例。

iteration

以下是将返回 Families 集合中所有父母的查询。

SELECT *
FROM Families.parents

执行以上查询后,将产生以下输出。

[
   [
      {
         "familyName": "Wakefield",
         "givenName": "Robin"
      },

      {
         "familyName": "Miller",
         "givenName": "Ben"
      }
   ],

   [
      {
         "familyName": "Smith",
         "givenName": "James"
      },

      {
         "familyName": "Curtis",
         "givenName": "Helen"
      }
   ],

   [
      {
         "firstName": "Thomas",
         "relationship": "father"
      },

      {
         "firstName": "Mary Kay",
         "relationship": "mother"
      }
   ]
]

如上面输出所示,每个家庭的父母以单独的 JSON 数组显示。

我们来看相同的例子,但这一次,我们将在 FROM 子句中使用 IN 关键字。

iterations

以下是包含 IN 关键字的查询。

SELECT *
FROM c IN Families.parents

执行以上查询后,将产生以下输出。

[
   {
      "familyName": "Wakefield",
      "givenName": "Robin"
   },

   {
      "familyName": "Miller",
      "givenName": "Ben"
   },

   {
      "familyName": "Smith",
      "givenName": "James"
   },

   {
      "familyName": "Curtis",
      "givenName": "Helen"
   },

   {
      "firstName": "Thomas",
      "relationship": "father"
   },

   {
      "firstName": "Mary Kay",
      "relationship": "mother"
   }

   {
      "id": "WakefieldFamily",
      "givenName": "Jesse",
      "grade": 6
   }
]

在上面例子中,可以看出,通过迭代,在集合中的父母上执行迭代的查询有一个不同的输出数组。因此,每个家庭的所有父母都添加到单个数组中。

DocumentDB SQL - Joins

在关系型数据库中,Join 子句用于合并数据库中来自两个或更多表的记录,并且在设计归一化架构时,非常需要连接跨表。由于 DocumentDB 处理无架构文档的非规范化数据模型,因此 DocumentDB SQL 中的 JOIN 是“selfjoin”的逻辑等效。

让我们考虑一下前一个示例中的三个文档。

以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

我们来看一个示例,以了解 JOIN 子句的工作原理。

sql join

以下是将根连接到子文档的查询。

SELECT f.id
FROM Families f
JOIN c IN f.children

执行上述查询时,将会产生以下输出。

[
   {
      "id": "WakefieldFamily"
   },

   {
      "id": "WakefieldFamily"
   },

   {
      "id": "SmithFamily"
   },

   {
      "id": "SmithFamily"
   },

   {
      "id": "AndersenFamily"
   }
]

在上述示例中,连接在文档根和子根之间,在两个 JSON 对象之间生成笛卡尔积。以下是需要注意的几个要点 -

  1. 在 FROM 子句中,JOIN 子句是一个迭代器。

  2. 前两个文档 WakefieldFamily 和 SmithFamily 包含两个子文档,因此结果集包含笛卡尔积,为每个子文档生成一个独立的对象。

  3. 第三个文档 AndersenFamily 只包含一个子文档,因此只有一个对应于此文档的对象。

让我们来看相同的示例,但这一次,我们还检索子名称,以便更好地理解 JOIN 子句。

sql joins

以下是将根连接到子文档的查询。

SELECT
   f.id AS familyName,
   c.givenName AS childGivenName,
   c.firstName AS childFirstName
FROM Families f
JOIN c IN f.children

执行以上查询后,将产生以下输出。

[
   {
      "familyName": "WakefieldFamily",
      "childGivenName": "Jesse"
   },

   {
      "familyName": "WakefieldFamily",
      "childGivenName": "Lisa"
   },

   {
      "familyName": "SmithFamily",
      "childGivenName": "Michelle"
   },

   {
      "familyName": "SmithFamily",
      "childGivenName": "John"
   },

   {
      "familyName": "AndersenFamily",
      "childFirstName": "Henriette Thaulow"
   }
]

DocumentDB SQL - Aliasing

在关系数据库中,SQL 别名用于临时重命名表或列标题。类似地,在 DocumentDB 中,别名用于临时重命名 JSON 文档、子文档、对象或任何字段。

重命名是一个临时更改,实际文档并不会更改。基本上,别名是为了使字段/文档名称更具可读性。对于别名,使用可选的 AS 关键字。

我们从前一个例子中考虑三个类似的文档。

以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

我们来看看一个讨论别名的例子。

aliase

以下是将根与子文档连接的查询。我们有别名,例如 f.id AS familyName、c.givenName AS childGivenName 和 c.firstName AS childFirstName。

SELECT
   f.id AS familyName,
   c.givenName AS childGivenName,
   c.firstName AS childFirstName
FROM Families f
JOIN c IN f.children

执行以上查询后,将产生以下输出。

[
   {
      "familyName": "WakefieldFamily",
      "childGivenName": "Jesse"
   },

   {
      "familyName": "WakefieldFamily",
	  "childGivenName": "Lisa"
   },

   {
      "familyName": "SmithFamily",
      "childGivenName": "Michelle"
   },

   {
      "familyName": "SmithFamily",
      "childGivenName": "John"
   },

   {
      "familyName": "AndersenFamily",
      "childFirstName": "Henriette Thaulow"
   }
]

上面的输出显示文件名称已更改,但这是临时更改,原始文档未修改。

DocumentDB SQL - Array Creation

在 DocumentDB SQL 中,Microsoft 添加了一个关键功能,借助此功能我们可以轻松创建数组。这意味着当我们运行查询时,它将创建类似于 JSON 对象的集合数组作为查询的结果。

让我们考虑与前例中相同的文档。

以下是 AndersenFamily 文档。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

以下是 SmithFamily 文档。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

以下是 WakefieldFamily 文档。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

我们来看一个例子。

array creation

以下是将返回每个家庭的姓氏和地址的查询。

SELECT f.id AS FamilyName,
[f.location.city, f.location.county, f.location.state] AS Address
FROM Families f

可以看到 city、county 和 state 字段用方括号括起来,这将创建一个数组,此数组名为 Address。当执行上述查询时,它会生成以下输出。

[
   {
      "FamilyName": "WakefieldFamily",
      "Address": [
         "NY",
         "Manhattan",
         "NY"
      ]
   },

   {
      "FamilyName": "SmithFamily",
      "Address": [
         "Forest Hills",
         "Queens",
         "NY"
      ]
   },

   {
      "FamilyName": "AndersenFamily",
      "Address": [
         "Seattle",
         "King",
         "WA"
      ]
   }
]

在上述输出中,城市、县和州信息被添加到 Address 数组中。

DocumentDB SQL - Scalar Expressions

在 DocumentDB SQL 中,SELECT 子句还支持标量表达式,如常量、算术表达式、逻辑表达式等。通常,标量查询很少使用,因为它们不会实际查询集合中的文档,只是评估表达式。但使用标量表达式查询来学习基础知识、如何在查询中使用表达式和塑造 JSON,仍然很有帮助,并且这些概念直接适用于在集合中的文档中所运行的实际查询。

让我们来看一个包含多个标量查询的示例。

scalar queries

在查询浏览器中,只选择要执行的文本并单击“运行”。我们先运行第一个。

SELECT "Hello"

执行以上查询后,将产生以下输出。

[
   {
      "$1": "Hello"
   }
]

此输出可能看起来有些令人困惑,因此我们来将其分解一下。

  1. 首先,正如我们在上一个演示中看到的,查询结果始终包含在方括号中,因为它们作为 JSON 数组返回,甚至像此查询那样的标量表达式查询也只返回一个文档。

  2. 我们有一个包含一个文档的数组,该文档中有一个属性以用于 SELECT 语句中的单个表达式。

  3. SELECT 语句未为此属性提供名称,因此 DocumentDB 使用 $1 自动生成一个名称。

  4. 这通常不是我们想要的,这就是为什么我们可以在查询中使用 AS 为表达式设置别名,这会将生成的文档中的属性名称设置为所需的方式,本示例中为 word。

SELECT "Hello" AS word

执行以上查询后,将产生以下输出。

[
   {
      "word": "Hello"
   }
]

同样,以下是一个简单的查询。

SELECT ((2 + 11 % 7)-2)/3

查询检索以下输出。

[
   {
      "$1": 1.3333333333333333
   }
]

让我们再看另一个嵌套数组和嵌入对象的塑造示例。

SELECT
   {
      "words1":
         ["Hello", "World"],
      "words2":
         ["How", "Are", "You?"]
   } AS allWords

执行以上查询后,将产生以下输出。

[
   {
      "allWords": {
         "words1": [
            "Hello",
            "World"
         ],

         "words2": [
            "How",
            "Are",
            "You?"
         ]
      }
   }
]

DocumentDB SQL - Parameterized

在关系数据库中,参数化查询是一个在其中为参数使用占位符,并在执行时提供参数值的查询。DocumentDB 也支持参数化查询,并且参数化查询中的参数可以使用熟悉的 @ 符号表示。使用参数化查询的最重要原因是避免 SQL 注入攻击。它还可以提供对用户输入的可靠处理和转义。

让我们来看一个将使用 .Net SDK 的示例。以下是将删除集合的代码。

private async static Task DeleteCollection(DocumentClient client, string collectionId) {
   Console.WriteLine();
   Console.WriteLine(">>> Delete Collection {0} in {1} <<<",
   collectionId, _database.Id);
   var query = new SqlQuerySpec {
      QueryText = "SELECT * FROM c WHERE c.id = @id",
      Parameters = new SqlParameterCollection { new SqlParameter { Name =
         "@id", Value = collectionId } }
   };

   DocumentCollection collection = client.CreateDocumentCollectionQuery(database.SelfLink,
      query).AsEnumerable().First();

   await client.DeleteDocumentCollectionAsync(collection.SelfLink);

   Console.WriteLine("Deleted collection {0} from database {1}",
      collectionId, _database.Id);
}

参数化查询的构造如下。

var query = new SqlQuerySpec {
   QueryText = "SELECT * FROM c WHERE c.id = @id",
   Parameters = new SqlParameterCollection { new SqlParameter { Name =
      "@id", Value = collectionId } }
};

我们不会对 collectionId 进行硬编码,因此此方法可用于删除任何集合。我们可以使用“@”符号为参数名称添加前缀,类似于 SQL Server。

在上述示例中,我们通过 Id 查询一个特定集合,其中 Id 参数在此 SqlParameterCollection 中定义,该 SqlParameterCollection 已分配给此 SqlQuerySpec 的参数属性。然后,SDK 负责为 DocumentDB 构造最终查询字符串,其中嵌入了 collectionId。我们运行查询,然后使用其 SelfLink 删除集合。

以下是 CreateDocumentClient 任务实现。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM
         c WHERE c.id = 'earthquake'").AsEnumerable().First();

      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'myfirstdb'").AsEnumerable().First();

      await DeleteCollection(client, "MyCollection1");
      await DeleteCollection(client, "MyCollection2");
   }
}

当代码执行时,它会产生以下输出。

**** Delete Collection MyCollection1 in mydb ****
Deleted collection MyCollection1 from database myfirstdb

**** Delete Collection MyCollection2 in mydb ****
Deleted collection MyCollection2 from database myfirstdb

让我们来看另一个示例。我们可以编写一个将姓氏和地址州作为参数的查询,然后根据用户输入为 lastname 和 location.state 的各种值执行该查询。

SELECT *
FROM Families f
WHERE f.lastName = @lastName AND f.location.state = @addressState

然后可以将此请求作为一个参数化 JSON 查询发送给 DocumentDB,如下面的代码所示。

{
   "query": "SELECT * FROM Families f WHERE f.lastName = @lastName AND
      f.location.state = @addressState",

   "parameters": [
      {"name": "@lastName", "value": "Wakefield"},
      {"name": "@addressState", "value": "NY"},
   ]
}

DocumentDB SQL - Built-in Function

DocumentDB 支持各种内置函数,用于可以在查询内使用的常见操作。有一系列函数可用于执行数学计算,还有在使用各种模式时非常有用的类型检查函数。这些函数可以测试某个属性是否存在,以及如果存在,它是否为数字、字符串、布尔值或对象。

我们还获得了用于解析和操作字符串的这些便捷函数,以及用于处理数组的几个函数,这些函数允许您执行诸如连接数组和测试数组是否包含特定元素之类的操作。

以下是内置函数不同类型 -

S.No.

Built-in Functions & Description

1

Mathematical Functions 数学函数执行计算,通常基于输入的值作为参数提供,并返回一个数字值。

2

Type Checking Functions 类型检查函数允许您检查 SQL 查询中的表达式的类型。

3

String Functions 字符串函数对字符串输入值执行操作,并返回字符串、数字或布尔值。

4

Array Functions 数组函数对数组输入值执行操作,并以数字、布尔或数组值形式返回。

5

Spatial Functions DocumentDB 还支持开放地理空间联盟 (OGC) 的用于地理空间查询的内置函数。

DocumentDB SQL - Linq to SQL Translation

在 DocumentDB 中,我们实际上使用 SQL 查询文档。如果我们进行 .NET 开发,还可以使用一个 LINQ 提供程序,它可以从 LINQ 查询中生成适当的 SQL。

Supported Data Types

在 DocumentDB 中,随 DocumentDB .NET SDK 包含的 LINQ 提供程序支持所有 JSON 原始类型,它们如下所示 −

  1. Numeric

  2. Boolean

  3. String

  4. Null

Supported Expression

以下标量表达式由随 DocumentDB .NET SDK 包含的 LINQ 提供程序支持。

  1. Constant Values − 包含原始数据类型的常数值。

  2. Property/Array Index Expressions - 表达式引用对象的属性或数组元素。

  3. Arithmetic Expressions - 包含数值和布尔值的常见算术表达式。

  4. String Comparison Expression - 包含将字符串值与某个常量字符串值进行比较。

  5. Object/Array Creation Expression − 返回复合值类型或匿名类型对象或此类对象的数组。这些值可以嵌套。

Supported LINQ Operators

以下是随 DocumentDB .NET SDK 包含的 LINQ 提供程序中支持的 LINQ 运算符列表。

  1. Select − 投影转换为 SQL SELECT,包括对象构造。

  2. Where − 筛选器转换为 SQL WHERE,并支持在 &&、|| 和 ! 与 SQL 运算符之间的转换。

  3. SelectMany − 允许将数组解压缩到 SQL JOIN 子句。可用于链/嵌套表达式以筛选数组元素。

  4. OrderBy and OrderByDescending − 转换为升序/降序 ORDER BY。

  5. CompareTo − 转换为范围比较。通常用于字符串,因为在 .NET 中它们不可比较。

  6. Take − 转换为 SQL TOP,用于限制查询结果。

  7. Math Functions − 支持从 .NET 的 Abs、Acos、Asin、Atan、Ceiling、Cos、Exp、Floor、Log、Log10、Pow、Round、Sign、Sin、Sqrt、Tan、Truncate 转换为等效的 SQL 内置函数。

  8. String Functions − 支持从 .NET 的 Concat、Contains、EndsWith、IndexOf、Count、ToLower、TrimStart、Replace、Reverse、TrimEnd、StartsWith、SubString、ToUpper 到等效的 SQL 内置函数的转换。

  9. Array Functions − 支持从 .NET 的 Concat、Contains 和 Count 转换为等效的 SQL 内置函数。

  10. Geospatial Extension Functions − 支持将 Distance、Within、IsValid 和 IsValidDetailed 存根方法转换为等效的 SQL 内置函数。

  11. User-Defined Extension Function − 支持将 UserDefinedFunctionProvider.Invoke 存根方法转换为对应的用户定义函数。

  12. Miscellaneous − 支持转换合并和条件运算符。可根据上下文将 Contains 转换成 String CONTAINS、ARRAY_CONTAINS 或 SQL IN。

我们来看一个使用 .Net SDK 的示例。以下三个文档我们将在本示例中考虑。

New Customer 1

{
   "name": "New Customer 1",
   "address": {
      "addressType": "Main Office",
      "addressLine1": "123 Main Street",

      "location": {
         "city": "Brooklyn",
         "stateProvinceName": "New York"
      },

      "postalCode": "11229",
      "countryRegionName": "United States"
   },
}

New Customer 2

{
   "name": "New Customer 2",

   "address": {
      "addressType": "Main Office",
      "addressLine1": "678 Main Street",

      "location": {
         "city": "London",
         "stateProvinceName": " London "
      },

      "postalCode": "11229",
      "countryRegionName": "United Kingdom"
   },
}

New Customer 3

{
   "name": "New Customer 3",

   "address": {
      "addressType": "Main Office",
      "addressLine1": "12 Main Street",

      "location": {
         "city": "Brooklyn",
         "stateProvinceName": "New York"
      },

      "postalCode": "11229",
      "countryRegionName": "United States"
   },
}

以下是在 q 中使用 LINQ 进行查询的代码,但除非针对它运行 .ToList,否则它不会执行。

private static void QueryDocumentsWithLinq(DocumentClient client) {
   Console.WriteLine();
   Console.WriteLine("**** Query Documents (LINQ) ****");
   Console.WriteLine();
   Console.WriteLine("Quering for US customers (LINQ)");
   var q =
      from d in client.CreateDocumentQuery<Customer>(collection.DocumentsLink)
      where d.Address.CountryRegionName == "United States"

   select new {
      Id = d.Id,
      Name = d.Name,
      City = d.Address.Location.City
   };

   var documents = q.ToList();
   Console.WriteLine("Found {0} US customers", documents.Count);

   foreach (var document in documents) {
      var d = document as dynamic;
      Console.WriteLine(" Id: {0}; Name: {1}; City: {2}", d.Id, d.Name, d.City);
   }

   Console.WriteLine();
}

SDK 会将我们的 LINQ 查询转换成适用于 DocumentDB 的 SQL 语法,根据我们的 LINQ 语法生成 SELECT 和 WHERE 子句。

让我们从 CreateDocumentClient 任务调用以上查询。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'myfirstdb'").AsEnumerable().First();
      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'MyCollection'").AsEnumerable().First();
      QueryDocumentsWithLinq(client);
   }
}

执行以上代码时,会产生以下输出。

**** Query Documents (LINQ) ****

Quering for US customers (LINQ)
Found 2 US customers
   Id: 7e9ad4fa-c432-4d1a-b120-58fd7113609f; Name: New Customer 1; City: Brooklyn
   Id: 34e9873a-94c8-4720-9146-d63fb7840fad; Name: New Customer 1; City: Brooklyn

DocumentDB SQL - JavaScript Integration

如今,JavaScript 无处不在,而不仅仅在浏览器中。DocumentDB 作为一种现代版 T-SQL 接受 JavaScript,支持原生地对 JavaScript 逻辑进行事务执行,就在数据库引擎内部。DocumentDB 提供了一种编程模型,用于根据存储过程和触发器直接对集合中的基于 JavaScript 的应用程序逻辑进行执行。

我们来看一个创建简单存储过程的示例。以下步骤 −

Step 1 − 创建一个新的控制台应用程序。

Step 2 − 从 NuGet 添加 .NET SDK。我们此处使用 .NET SDK,这意味着我们将编写一些 C# 代码来创建、执行,然后删除我们的存储过程,但存储过程本身是用 JavaScript 编写的。

Step 3 − 右键单击解决方案资源管理器中的项目。

Step 4 − 添加用于存储过程的新 JavaScript 文件并将其称为 HelloWorldStoreProce.js

js stored procedure

每个存储过程都只是一个 JavaScript 函数,因此我们将创建一个新函数,并自然地也将此函数称为 HelloWorldStoreProce 。无论是否为函数命名都没有关系。DocumentDB 只会通过我们在创建时提供的 Id 来引用这个存储过程。

function HelloWorldStoreProce() {
   var context = getContext();
   var response = context.getResponse();
   response.setBody('Hello, and welcome to DocumentDB!');
}

存储过程的所有操作就是从上下文中获取 response 对象并调用其 setBody 方法,向调用方返回一个字符串。在 C# 代码中,我们将创建存储过程,执行它,然后删除它。

存储过程的范围是每个集合,因此我们将需要集合的 SelfLink 来创建存储过程。

Step 5 − 首先查询 myfirstdb 数据库,然后查询 MyCollection 集合。

创建存储过程就像创建 DocumentDB 中的任何其他资源一样。

private async static Task SimpleStoredProcDemo() {
   var endpoint = "https://azuredocdbdemo.documents.azure.com:443/";
   var masterKey =
      "BBhjI0gxdVPdDbS4diTjdloJq7Fp4L5RO/StTt6UtEufDM78qM2CtBZWbyVwFPSJIm8AcfDu2O+AfV T+TYUnBQ==";

   using (var client = new DocumentClient(new Uri(endpoint), masterKey)) {
      // Get database
      Database database = client
         .CreateDatabaseQuery("SELECT * FROM c WHERE c.id = 'myfirstdb'")
         .AsEnumerable()
         .First();

      // Get collection
      DocumentCollection collection = client
         .CreateDocumentCollectionQuery(database.CollectionsLink, "SELECT * FROM
         c WHERE c.id = 'MyCollection'")
         .AsEnumerable()
         .First();

      // Create stored procedure
      var sprocBody = File.ReadAllText(@"..\..\HelloWorldStoreProce.js");

      var sprocDefinition = new StoredProcedure {
         Id = "HelloWorldStoreProce",
         Body = sprocBody
      };

      StoredProcedure sproc = await client.
         CreateStoredProcedureAsync(collection.SelfLink, sprocDefinition);
      Console.WriteLine("Created stored procedure {0} ({1})",
         sproc.Id, sproc.ResourceId);

      // Execute stored procedure
      var result = await client.ExecuteStoredProcedureAsync(sproc.SelfLink);
      Console.WriteLine("Executed stored procedure; response = {0}", result.Response);

      // Delete stored procedure
      await client.DeleteStoredProcedureAsync(sproc.SelfLink);
      Console.WriteLine("Deleted stored procedure {0} ({1})",
         sproc.Id, sproc.ResourceId);
   }
}

Step 6 - 首先使用新资源的 Id 创建一个定义对象,然后在 DocumentClient 对象上调用 Create 方法之一。对于一个存储过程来说,定义包括用于传输到服务器的 Id 和实际 JavaScript 代码。

Step 7 - 调用 File.ReadAllText 以从 JS 文件中提取存储过程代码。

Step 8 - 将存储过程代码分配给定义对象的 body 属性。

对于 DocumentDB 来说,我们在定义中指定的 Id 是存储过程的名称,无关乎我们实际为 JavaScript 函数命名的名称。

然而,在创建存储过程和其他服务器端对象时,建议为 JavaScript 函数命名,而且这些函数名称要与我们在 DocumentDB 定义中设置的 Id 匹配。

Step 9 - 调用 CreateStoredProcedureAsync ,传递给 SelfLinkMyCollection 以及存储过程定义。这将创建 DocumentDB 为其分配的存储过程和 ResourceId

Step 10 - 调用存储过程。 ExecuteStoredProcedureAsync 采用类型参数,将其设置为存储过程返回的值的预期数据类型,如果你想要返回一个动态对象,则可以简单地将其指定为一个对象。那是一个在运行时将绑定其属性的对象。

在本例中,我们知道存储过程只返回一个字符串,因此我们调用 ExecuteStoredProcedureAsync<string>

以下是 Program.cs 文件的完整实现。

using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DocumentDBStoreProce {
   class Program {
      private static void Main(string[] args) {
         Task.Run(async () => {
            await SimpleStoredProcDemo();
         }).Wait();
      }

      private async static Task SimpleStoredProcDemo() {
         var endpoint = "https://azuredocdbdemo.documents.azure.com:443/";
         var masterKey =
            "BBhjI0gxdVPdDbS4diTjdloJq7Fp4L5RO/StTt6UtEufDM78qM2CtBZWbyVwFPSJIm8AcfDu2O+AfV T+TYUnBQ==";

         using (var client = new DocumentClient(new Uri(endpoint), masterKey)) {
            // Get database
            Database database = client
               .CreateDatabaseQuery("SELECT * FROM c WHERE c.id = 'myfirstdb'")
               .AsEnumerable()
               .First();

            // Get collection
            DocumentCollection collection = client
               .CreateDocumentCollectionQuery(database.CollectionsLink,
               "SELECT * FROM c WHERE c.id = 'MyCollection'")
               .AsEnumerable()
               .First();

            // Create stored procedure
            var sprocBody = File.ReadAllText(@"..\..\HelloWorldStoreProce.js");

            var sprocDefinition = new StoredProcedure {
               Id = "HelloWorldStoreProce",
               Body = sprocBody
            };

            StoredProcedure sproc = await client
               .CreateStoredProcedureAsync(collection.SelfLink, sprocDefinition);

            Console.WriteLine("Created stored procedure {0} ({1})", sproc
               .Id, sproc.ResourceId);

            // Execute stored procedure
            var result = await client
               .ExecuteStoredProcedureAsync<string>(sproc.SelfLink);
            Console.WriteLine("Executed stored procedure; response = {0}",
               result.Response);

            // Delete stored procedure
            await client.DeleteStoredProcedureAsync(sproc.SelfLink);
            Console.WriteLine("Deleted stored procedure {0} ({1})",
               sproc.Id, sproc.ResourceId);
         }
      }
   }
}

执行以上代码时,会产生以下输出。

Created stored procedure HelloWorldStoreProce (Ic8LAMEUVgACAAAAAAAAgA==)

Executed stored procedure; response = Hello, and welcome to DocumentDB!

如上面的输出所示,response 属性包含存储过程返回的“Hello, and welcome to DocumentDB!”。

DocumentDB SQL - User-Defined Functions

DocumentDB SQL 提供对用户自定义函数 (UDF) 的支持。UDF 只是另一种你可以编写的 JavaScript 函数,且它们的工作方式几乎和你预期的一样。你可以创建 UDF 以使用可以在查询中引用的自定义业务逻辑来扩展查询语言。

DocumentDB SQL 语法被扩展以使用这些 UDF 来支持自定义应用程序逻辑。UDF 可以向 DocumentDB 注册,然后被引用为 SQL 查询的一部分。

为此示例,我们考虑以下三个文档。

AndersenFamily 文档如下。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

SmithFamily 文档如下。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

WakefieldFamily 文档如下。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

我们来看一个我们将创建一些简单 UDF 的示例。

以下是 CreateUserDefinedFunctions 的实现。

private async static Task CreateUserDefinedFunctions(DocumentClient client) {
   Console.WriteLine();
   Console.WriteLine("**** Create User Defined Functions ****");
   Console.WriteLine();

   await CreateUserDefinedFunction(client, "udfRegEx");
}

我们有一个 udfRegEx,在 CreateUserDefinedFunction 中,我们从本地文件中获取其 JavaScript 代码。我们构造新 UDF 的定义对象,并使用集合的 SelfLink 和如下代码所示的 udfDefinition 对象调用 CreateUserDefinedFunctionAsync。

private async static Task<UserDefinedFunction>
CreateUserDefinedFunction(DocumentClient client, string udfId) {
   var udfBody = File.ReadAllText(@"..\..\Server\" + udfId + ".js");

   var udfDefinition = new UserDefinedFunction {
      Id = udfId,
      Body = udfBody
   };

   var result = await client
      .CreateUserDefinedFunctionAsync(_collection.SelfLink, udfDefinition);
   var udf = result.Resource;

   Console.WriteLine("Created user defined function {0}; RID: {1}",
      udf.Id, udf.ResourceId);

   return udf;
}

我们从结果的 resource 属性中返回新的 UDF,并将其返回给调用者。为了显示现有的 UDF,以下是 ViewUserDefinedFunctions 的实现。我们调用 CreateUserDefinedFunctionQuery 并像往常一样遍历它们。

private static void ViewUserDefinedFunctions(DocumentClient client) {
   Console.WriteLine();
   Console.WriteLine("**** View UDFs ****");
   Console.WriteLine();

   var udfs = client
      .CreateUserDefinedFunctionQuery(_collection.UserDefinedFunctionsLink)
      .ToList();

   foreach (var udf in udfs) {
      Console.WriteLine("User defined function {0}; RID: {1}", udf.Id, udf.ResourceId);
   }
}

DocumentDB SQL 未提供用于搜索子字符串或正则表达式的内置函数,因此以下简洁的一行代码填补了缺口,它是一个 JavaScript 函数。

function udfRegEx(input, regex) {
   return input.match(regex);
}

鉴于第一个参数中的输入字符串,使用 JavaScript 的内置正则表达式支持,将第二个参数中的模式匹配字符串传递到 . match 中。我们可以运行一个子字符串查询,以在 lastName 属性中找到单词 Andersen 的所有商店。

private static void Execute_udfRegEx(DocumentClient client) {
   var sql = "SELECT c.name FROM c WHERE udf.udfRegEx(c.lastName, 'Andersen') != null";

   Console.WriteLine();
   Console.WriteLine("Querying for Andersen");

   var documents = client.CreateDocumentQuery(_collection.SelfLink, sql).ToList();
   Console.WriteLine("Found {0} Andersen:", documents.Count);

   foreach (var document in documents) {
      Console.WriteLine("Id: {0}, Name: {1}", document.id, document.lastName);
   }
}

请注意,我们必须使用前缀 udf 限定每个 UDF 引用。就像对任何普通查询一样,我们刚刚将 SQL 传递给 CreateDocumentQuery 。最后,让我们从 CreateDocumentClient 任务中调用以上查询

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient

   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)){
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE
         c.id = 'myfirstdb'").AsEnumerable().First();
      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'Families'").AsEnumerable().First();

      await CreateUserDefinedFunctions(client);

      ViewUserDefinedFunctions(client);

      Execute_udfRegEx(client);
   }
}

执行以上代码时,会产生以下输出。

**** Create User Defined Functions ****
Created user defined function udfRegEx; RID: kV5oANVXnwAlAAAAAAAAYA==
**** View UDFs ****
User defined function udfRegEx; RID: kV5oANVXnwAlAAAAAAAAYA==
Querying for Andersen
Found 1 Andersen:
 Id: AndersenFamily, Name: Andersen

DocumentDB SQL - Composite SQL Queries

Composite Query 使您可以组合来自现有查询的数据,然后再应用筛选器、聚合等,然后才能显示结果数据集中显示的组合数据。复合查询检索现有查询上与多级相关的信息,并将组合的数据作为单个扁平化查询结果呈现出来。

使用复合查询,您还可以选择:

  1. 选择 SQL 修剪选项,以基于用户的属性选择删除不需要的表和字段。

  2. 设置 ORDER BY 和 GROUP BY 子句。

  3. 将 WHERE 子句作为一个筛选器设置在复合查询的结果集中。

可以组合上述运算符以形成功能更强大的查询。由于 DocumentDB 支持嵌套集合,因此可以级联或嵌套组合。

让我们考虑一下演示本文档的以下内容。

AndersenFamily 文档如下。

{
   "id": "AndersenFamily",
   "lastName": "Andersen",

   "parents": [
      { "firstName": "Thomas", "relationship":  "father" },
      { "firstName": "Mary Kay", "relationship":  "mother" }
   ],

   "children": [
      {
         "firstName": "Henriette Thaulow",
         "gender": "female",
         "grade": 5,
         "pets": [ { "givenName": "Fluffy", "type":  "Rabbit" } ]
      }
   ],

   "location": { "state": "WA", "county": "King", "city": "Seattle" },
   "isRegistered": true
}

SmithFamily 文档如下。

{
   "id": "SmithFamily",

   "parents": [
      { "familyName": "Smith", "givenName": "James" },
      { "familyName": "Curtis", "givenName": "Helen" }
   ],

   "children": [
      {
         "givenName": "Michelle",
         "gender": "female",
         "grade": 1
      },

      {
         "givenName": "John",
         "gender": "male",
         "grade": 7,

         "pets": [
            { "givenName": "Tweetie", "type": "Bird" }
         ]
      }
   ],

   "location": {
      "state": "NY",
      "county": "Queens",
      "city": "Forest Hills"
   },

   "isRegistered": true
}

WakefieldFamily 文档如下。

{
   "id": "WakefieldFamily",

   "parents": [
      { "familyName": "Wakefield", "givenName": "Robin" },
      { "familyName": "Miller", "givenName": "Ben" }
   ],

   "children": [
      {
         "familyName": "Merriam",
         "givenName": "Jesse",
         "gender": "female",
         "grade": 6,

         "pets": [
            { "givenName": "Charlie Brown", "type": "Dog" },
            { "givenName": "Tiger", "type": "Cat" },
            { "givenName": "Princess", "type": "Cat" }
         ]
      },

      {
         "familyName": "Miller",
         "givenName": "Lisa",
         "gender": "female",
         "grade": 3,

         "pets": [
            { "givenName": "Jake", "type": "Snake" }
         ]
      }
   ],

   "location": { "state": "NY", "county": "Manhattan", "city": "NY" },
   "isRegistered": false
}

让我们来看一个级联查询的示例。

concatenated query

以下是将检索第一个孩子 givenName 为 Michelle 的家庭的 id 和位置的查询。

SELECT f.id,f.location
FROM Families f
WHERE f.children[0].givenName = "Michelle"

执行以上查询后,将产生以下输出。

[
   {
      "id": "SmithFamily",
      "location": {
         "state": "NY",
         "county": "Queens",
         "city": "Forest Hills"
      }
   }
]

让我们考虑级联查询的另一个示例。

concatenated queries

以下是将返回所有第一个孩子成绩高于 3 的文档的查询。

SELECT *
FROM Families f
WHERE ({grade: f.children[0].grade}.grade > 3)

执行以上查询后,将产生以下输出。

[
   {
      "id": "WakefieldFamily",
      "parents": [
         {
            "familyName": "Wakefield",
            "givenName": "Robin"
         },

         {
            "familyName": "Miller",
            "givenName": "Ben"
         }
      ],

      "children": [
         {
            "familyName": "Merriam",
            "givenName": "Jesse",
            "gender": "female",
            "grade": 6,

            "pets": [
               {
                  "givenName": "Charlie Brown",
                  "type": "Dog"
               },

               {
                  "givenName": "Tiger",
                  "type": "Cat"
               },

               {
                  "givenName": "Princess",
                  "type": "Cat"
               }
            ]
         },

         {
            "familyName": "Miller",
            "givenName": "Lisa",
            "gender": "female",
            "grade": 3,

            "pets": [
               {
                  "givenName": "Jake",
                  "type": "Snake"
               }
            ]
         }
      ],

      "location": {
         "state": "NY",
         "county": "Manhattan",
         "city": "NY"
      },

      "isRegistered": false,
      "_rid": "Ic8LAJFujgECAAAAAAAAAA==",
      "_ts": 1450541623,
      "_self": "dbs/Ic8LAA==/colls/Ic8LAJFujgE=/docs/Ic8LAJFujgECAAAAAAAAAA==/",
      "_etag": "\"00000500-0000-0000-0000-567582370000\"",
      "_attachments": "attachments/"
   },

   {
      "id": "AndersenFamily",
      "lastName": "Andersen",

      "parents": [
         {
            "firstName": "Thomas",
            "relationship": "father"
         },

         {
            "firstName": "Mary Kay",
            "relationship": "mother"
         }
      ],

      "children": [
         {
            "firstName": "Henriette Thaulow",
            "gender": "female",
            "grade": 5,

            "pets": [
               {
                  "givenName": "Fluffy",
                  "type": "Rabbit"
               }
            ]
         }
      ],

      "location": {
         "state": "WA",
         "county": "King",
         "city": "Seattle"
      },

      "isRegistered": true,
      "_rid": "Ic8LAJFujgEEAAAAAAAAAA==",
      "_ts": 1450541624,
      "_self": "dbs/Ic8LAA==/colls/Ic8LAJFujgE=/docs/Ic8LAJFujgEEAAAAAAAAAA==/",
      "_etag": "\"00000700-0000-0000-0000-567582380000\"",
      "_attachments": "attachments/"
   }
]

让我们来看一个 example 嵌套查询。

nested queries

以下是将遍历所有父项然后返回 familyName 为 Smith 的文档的查询。

SELECT *
FROM p IN Families.parents
WHERE p.familyName = "Smith"

执行以上查询后,将产生以下输出。

[
   {
      "familyName": "Smith",
      "givenName": "James"
   }
]

让我们考虑 another example 嵌套查询。

nested query

以下是将返回所有 familyName 的查询。

SELECT VALUE p.familyName
FROM Families f
JOIN p IN f.parents

执行以上查询后,会生成以下输出。

[
   "Wakefield",
   "Miller",
   "Smith",
   "Curtis"
]