LINQ 翻译 - Cosmos DB 中的查询语言

Cosmos DB 查询提供程序将语言集成查询 (LINQ) 转换为 NoSQL 查询,以便高效检索数据。 本文介绍哪些 LINQ 运算符受支持,以及 LINQ 查询如何映射到 Cosmos DB 语法,帮助 .NET 开发人员优化其数据库查询。 若要查看 LINQ 代码中转换的 NoSQL 查询,请在 ToString() 生成的 IQueryable 对象上使用该方法。 以下说明假定你基本熟悉 LINQ。 除了 LINQ,Cosmos DB 还支持 Entity Framework Core,该 Core 适用于 NoSQL 的 API。

注释

建议使用最新的 .NET SDK (Microsoft.Azure.Cosmos) 版本

查询提供程序类型系统仅支持 JSON 基元类型: numericBooleanstringnull

查询提供程序支持以下标量表达式:

  • 常量值,包括查询计算时基元数据类型的常量值。

  • 引用对象或数组元素的属性的属性/数组索引表达式。 例如:

    family.Id;
    family.children[0].familyName;
    family.children[0].grade;
    
    int n = 1;
    
    family.children[n].grade;
    
  • 算术表达式,包括数值和布尔值的常见算术表达式。

    2 * family.children[0].grade;
    x + y;
    
  • 字符串比较表达式,包括将字符串值与某些常量字符串值进行比较。

    mother.familyName.StringEquals("Wakefield");
    
    string s = "Rob";
    string e = "in";
    string c = "obi";
    
    child.givenName.StartsWith(s);
    child.givenName.EndsWith(e);
    child.givenName.Contains(c);
    
  • 对象/数组创建表达式,返回复合值类型或匿名类型的对象,或此类对象的数组。 可以嵌套这些值。

    new Parent { familyName = "Wakefield", givenName = "Robin" };
    new { first = 1, second = 2 }; //an anonymous type with two fields  
    new int[] { 3, child.grade, 5 };
    

使用 LINQ

可以使用 创建 LINQ 查询 GetItemLinqQueryable。 本示例演示 LINQ 查询生成和异步执行,其中包含:FeedIterator

using FeedIterator<Book> setIterator = container.GetItemLinqQueryable<Book>()
    .Where(b => b.Title == "War and Peace")
    .ToFeedIterator<Book>());

//Asynchronous query execution
while (setIterator.HasMoreResults)
{
    foreach(var item in await setIterator.ReadNextAsync()){
    {
        Console.WriteLine(item.cost);
    }
}

支持的 LINQ 运算符

NoSQL .NET SDK 附带的 LINQ 提供程序支持以下运算符:

  • 选择:投影转换为 SELECT,包括对象构造。
  • 其中:筛选器转换为WHERE,并支持在 NoSQL 运算符和 && NoSQL 运算符之间进行||!转换
  • SelectMany:允许将数组展开到 JOIN 子句。 用于链接或嵌套表达式以筛选数组元素。
  • OrderByOrderByDescending:使用ORDER BYASCDESC转换为 。
  • 聚合的计数Sum、Min、MaxAverage 运算符及其异步等效项 CountAsyncSumAsyncMinAsyncMaxAsyncAverageAsync
  • CompareTo:转换为范围比较。 此运算符通常用于字符串,因为它们在 .NET 中不可比较。
  • 跳过获取:转换为 OFFSETLIMIT 限制查询的结果并执行分页。
  • 数学函数:支持从 .NETAbs、、AcosAsinAtanCeilingCosExpFloorLogLog10PowRoundSignSin、、、,SqrtTan以及Truncate等效的内置数学函数进行转换。
  • 字符串函数:支持从 .NETConcat、、ContainsCountEndsWithIndexOfReplaceReverseStartsWithSubStringToLower、、 ToUpperTrimEndTrimStart等效的内置字符串函数进行转换。
  • 数组函数:支持从 .NET ConcatContains转换为Count等效的内置数组函数
  • 地理空间扩展函数:支持从存根方法DistanceIsValidIsValidDetailedWithin转换为等效的内置地理空间函数
  • User-Defined 函数扩展函数:支持从存根方法 CosmosLinq.InvokeUserDefinedFunction 转换为相应的用户定义的函数。
  • 杂项:支持转换 Coalesce条件运算符。 可以转换为字符串ContainsCONTAINS也可以ARRAY_CONTAINS根据上下文进行转换IN

例子

以下示例演示了某些标准 LINQ 查询运算符如何转换为 Cosmos DB 中的查询。

Select 运算符

语法是 input.Select(x => f(x))f 量表达式。 input在本例中,该对象为对象IQueryable

Select 运算符,示例 1:

  • LINQ lambda 表达式

    input.Select(family => family.parents[0].familyName);
    
  • NoSQL

    SELECT VALUE f.parents[0].familyName
    FROM Families f
    

Select 运算符,示例 2:

  • LINQ lambda 表达式

    input.Select(family => family.children[0].grade + c); // c is an int variable
    
  • NoSQL

    SELECT VALUE f.children[0].grade + c
    FROM Families f
    

Select 运算符,示例 3:

  • LINQ lambda 表达式

    input.Select(family => new
    {
        name = family.children[0].familyName,
        grade = family.children[0].grade + 3
    });
    
  • NoSQL

    SELECT VALUE {
        "name":f.children[0].familyName,
        "grade": f.children[0].grade + 3 
    }
    FROM Families f
    

SelectMany 运算符

语法是 input.SelectMany(x => f(x)),其中 f 是返回容器类型的标量表达式。

  • LINQ lambda 表达式

    input.SelectMany(family => family.children);
    
  • NoSQL

    SELECT VALUE child
    FROM child IN Families.children
    

Where 运算符

语法是 input.Where(x => f(x))f 个标量表达式,它返回布尔值。

其中运算符,示例 1:

  • LINQ lambda 表达式

    input.Where(family=> family.parents[0].familyName == "Wakefield");
    
  • NoSQL

    SELECT *
    FROM Families f
    WHERE f.parents[0].familyName = "Wakefield"
    

其中运算符,示例 2:

  • LINQ lambda 表达式

    input.Where(
        family => family.parents[0].familyName == "Wakefield" &&
        family.children[0].grade < 3);
    
  • NoSQL

    SELECT *
    FROM Families f
    WHERE f.parents[0].familyName = "Wakefield"
    AND f.children[0].grade < 3
    

复合 NoSQL 查询

可以编写上述运算符以形成更强大的查询。 由于 Cosmos DB 支持嵌套容器,因此可以连接或嵌套合成。

串联

语法为 input(.|.SelectMany())(.Select()|.Where())*。 串联的查询可以从可选 SelectMany 查询开始,后跟多个 SelectWhere 运算符。

串联,示例 1:

  • LINQ lambda 表达式

    input.Select(family => family.parents[0])
        .Where(parent => parent.familyName == "Wakefield");
    
  • NoSQL

    SELECT *
    FROM Families f
    WHERE f.parents[0].familyName = "Wakefield"
    

串联,示例 2:

  • LINQ lambda 表达式

    input.Where(family => family.children[0].grade > 3)
        .Select(family => family.parents[0].familyName);
    
  • NoSQL

    SELECT VALUE f.parents[0].familyName
    FROM Families f
    WHERE f.children[0].grade > 3
    

串联,示例 3:

  • LINQ lambda 表达式

    input.Select(family => new { grade=family.children[0].grade}).
        Where(anon=> anon.grade < 3);
    
  • NoSQL

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

串联,示例 4:

  • LINQ lambda 表达式

    input.SelectMany(family => family.parents)
        .Where(parent => parents.familyName == "Wakefield");
    
  • NoSQL

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

嵌 套

语法是input.SelectMany(x=>x.Q())QSelectSelectManyWhere运算符。

嵌套查询将内部查询应用于外部容器的每个元素。 一个重要功能是内部查询可以引用外部容器中元素的字段,例如自联接。

嵌套,示例 1:

  • LINQ lambda 表达式

    input.SelectMany(family=>
        family.parents.Select(p => p.familyName));
    
  • NoSQL

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

嵌套,示例 2:

  • LINQ lambda 表达式

    input.SelectMany(family =>
        family.children.Where(child => child.familyName == "Jeff"));
    
  • NoSQL

    SELECT *
    FROM Families f
    JOIN c IN f.children
    WHERE c.familyName = "Jeff"
    

嵌套,示例 3:

  • LINQ lambda 表达式

    input.SelectMany(family => family.children.Where(
        child => child.familyName == family.parents[0].familyName));
    
  • NoSQL

    SELECT *
    FROM Families f
    JOIN c IN f.children
    WHERE c.familyName = f.parents[0].familyName