Azure Cosmos DB for NoSQL 中的 LINQ 到 NoSQL 转换

适用范围: NoSQL

Azure Cosmos DB 查询提供程序可执行从 LINQ 查询到 Azure Cosmos DB for NoSQL 查询的尽可能有效的映射。 如果要获取从 LINQ 转换而来的 NoSQL 查询,请对生成的 IQueryable 对象使用 ToString() 方法。 以下内容假设你对 LINQ 有一个基本的了解。 除 LINQ 外,Azure Cosmos DB 还支持适用于 API for NoSQL 的 Entity Framework Core

注意

建议使用最新的 .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

可以使用 GetItemLinqQueryable 创建 LINQ 查询。 此示例演示如何使用 FeedIterator 生成 LINQ 查询并进行异步执行:

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:投影转换为 SELECT(包括对象构造)。
  • Where:筛选器转换为 WHERE,并且支持从 &&||! 到 NoSQL 运算符的转换
  • SelectMany:允许将数组展开到 JOIN 子句。 用于将表达式链接或嵌套到对数组元素应用的筛选器。
  • OrderByOrderByDescending:使用 ASC 或 DESC 转换为 ORDER BY
  • 用于聚合的 CountSumMinMaxAverage 运算符及其异步等效项 CountAsyncSumAsyncMinAsyncMaxAsyncAverageAsync
  • CompareTo:转换为范围比较。 此运算符通常用于字符串,因为它们在 .NET 中不可进行比较。
  • SkipTake:转换为 OFFSET 和 LIMIT,用于限制查询的结果和进行分页。
  • 数学函数:支持从 .NET AbsAcosAsinAtanCeilingCosExpFloorLogLog10PowRoundSignSinSqrtTanTruncate 转换为等效的内置数学函数
  • 字符串函数:支持从 .NET ConcatContainsCountEndsWithIndexOfReplaceReverseStartsWithSubStringToLowerToUpperTrimEndTrimStart 转换为等效的内置字符串函数
  • 数组函数:支持从 .NET ConcatContainsCount 转换为等效的内置数组函数
  • 地理空间扩展函数:支持从存根方法 DistanceIsValidIsValidDetailedWithin 转换为等效的内置地理空间函数
  • 用户定义的函数扩展函数:支持从存根方法 CosmosLinq.InvokeUserDefinedFunction 转换为相应的用户定义的函数。
  • 其他:支持 Coalesce 和条件运算符的转换。 可以根据上下文将 Contains 转换为字符串 CONTAINS、ARRAY_CONTAINS 或 IN。

示例

以下示例演示了一些标准 LINQ 查询运算符如何转换为 Azure 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 是返回布尔值的标量表达式。

Where 运算符,示例 1:

  • LINQ Lambda 表达式

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

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

Where 运算符,示例 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 查询

将上述运算符组合到一起可以构成更强大的查询。 由于 Azure 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