CosmosDb 的 SqlQuerySpec QueryBuilder

Ale*_*AIT 6 .net c# azure azure-cosmosdb

我们确实不得不从 Linq-Queries 转移到我们的 DocumentDB/CosmosDB。

原因主要是两个用例:

  • 部分选择 - 文档至少有一个大字段,我只希望在某些情况下返回。直接指定字段可以节省 RU 成本。我无法在 Linq 中实现这一点。
  • 像这样连接(示例有点奇怪)。

        SqlQuerySpec spec = new SqlQuerySpec(@"
            SELECT value(n)
            FROM books b
            join p in b.author.parents
            where b.isbn = @isbnId
            AND lower(p.address.street) = @parentStreet
        ");
    
    Run Code Online (Sandbox Code Playgroud)

所以我们的查询看起来像这样:

IQueryable<Book> queryable = client.CreateDocumentQuery<Book>(
            collectionSelfLink,
            new SqlQuerySpec
    {
                QueryText = "SELECT * FROM books b WHERE (b.Author.Name = @name)", 
                Parameters = new SqlParameterCollection() 
        { 
                      new SqlParameter("@name", "Herman Melville")
                }
    });
Run Code Online (Sandbox Code Playgroud)

但是,随着我们的需求变得越来越复杂,我们需要根据给定的参数使查询看起来不同。我们还有“in”查询,需要我们添加多个参数。

所以现在我们的代码看起来像这样......

        var sqlParameterCollection = new SqlParameterCollection();
        for (int i = 0; i < ids.Length; i++)
        {
            var key = "@myid" + i;
            sqlParameterCollection.Add(new SqlParameter(key, ids[i]));
        }
 [...]
        var query = $@"
            {select}
            FROM collection m
            WHERE m.myid IN ({string.Join(",", sqlParameterCollection.Select(p => p.Name))})
        ";
Run Code Online (Sandbox Code Playgroud)

接下来,需要根据某些参数使用额外的过滤器扩展 where 子句

由于情况越来越糟:是否有可用的查询构建器?我正在考虑一个流畅的 api,它在理想情况下还可以包含 SqlParameters,而不仅仅是查询文本。

伪代码:

queryBuilder
    .from("m")
    .select("field1")
    .select("field2")
    .where("myid", Operators.In, ...)
           .And(...
Run Code Online (Sandbox Code Playgroud)

Ale*_*AIT 2

我们最终不需要构建查询生成器。我们低估了使用的力量SelectMany

我们最终做了这样的事情

var query = client.CreateDocumentQuery<Book>(collectionSelfLink)
    // Join with select many
    .SelectMany(book => book.author.parents
        // Stay within the original SelectMany to do the filtering
        .Where(parents => parents.address.street == "parentstreet")
        // Stay within the original SelectMany to return the root node
//          .Select(x => book)
        // Or do a partial select
//          .Select(x => new {book.title, book.author.name})
    )
    .AsDocumentQuery();
Run Code Online (Sandbox Code Playgroud)

这个答案和其他例子有很大帮助:

documentdb 与 linq 连接

https://github.com/Azure/azure-cosmos-dotnet-v2/blob/58d740dde9474800c62ece42ef2ac67e426c0915/samples/code-samples/Queries/Program.cs

    private static void QueryWithTwoJoins(string collectionLink)
    {
        // SQL
        var familiesChildrenAndPets = client.CreateDocumentQuery<dynamic>(collectionLink,
            "SELECT f.id as family, c.FirstName AS child, p.GivenName AS pet " +
            "FROM Families f " +
            "JOIN c IN f.Children " +
            "JOIN p IN c.Pets ");

        foreach (var item in familiesChildrenAndPets)
        {
            Console.WriteLine(item);
        }

        // LINQ
        familiesChildrenAndPets = client.CreateDocumentQuery<Family>(collectionLink)
                .SelectMany(family => family.Children
                .SelectMany(child => child.Pets
                .Select(pet => new
                {
                    family = family.Id,
                    child = child.FirstName,
                    pet = pet.GivenName
                }
                )));

        foreach (var item in familiesChildrenAndPets)
        {
            Console.WriteLine(item);
        }
    }
Run Code Online (Sandbox Code Playgroud)