You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用LINQ在Cosmos NoSQL中展平嵌套结构指定属性?

三级嵌套数据结构的LINQ查询实现(替换SQL)

问题描述

我有一个三级嵌套的数据结构,示例访问路径为top.Middles[y].Inners[z].Secret,需要获取所有最内层Secret等于指定值(如'Forbidden')的Outer、Middle、Inner的ID列表。

已实现的SQL方案

符合需求的SQL语句:

SELECT t.id topId, m.id as middleId, b.id as bottomId
FROM Tops t
join m in t.Middles
join b IN m.Bottoms
where b.Secret = 'Forbidden'

执行后得到的结果示例:

[
{ "topId": "296f9023-f150-45cd-ad38-a7be2ac83f41", "middleId": "101c24bc-71cb-4b6b-a6cf-6979be61be45", "bottomId": "812ac30c-79d4-4c62-94c2-a8cd3eb19bb6" },
{ "topId": "296f9023-f150-45cd-ad38-a7be2ac83f41", "middleId": "101c24bc-71cb-4b6b-a6cf-6979be61be45", "bottomId": "9b298db2-db68-4a0c-82f4-d5bd270aa72b" },
{ "topId": "296f9023-f150-45cd-ad38-a7be2ac83f41", "middleId": "350f63eb-e41a-493f-9e6d-740818242b7e", "bottomId": "56a51a6c-4e6b-4c52-bbc7-0d71b2486c76" },
{ "topId": "1ebdb15f-5864-4e1e-860d-e3a97696da38", "middleId": "ce3bb505-5412-4368-a30e-7f6ce7838a95", "bottomId": "093c7d30-97bb-4fcf-9bd7-0814cbb92571" },
{ "topId": "1ebdb15f-5864-4e1e-860d-e3a97696da38", "middleId": "1b239750-1a91-4f0c-9fb1-b1d9e333349e", "bottomId": "b7c27305-59a3-4af9-93ed-5ca272472d37" },
{ "topId": "1ebdb15f-5864-4e1e-860d-e3a97696da38", "middleId": "a70b3933-3cde-44d0-80a8-fef6d958ce4a", "bottomId": "30c74577-ef59-41ec-8fd2-0ded3f3c30a2" }
]

需求转换

该SQL实现符合需求,但希望改用LINQ实现以获得强类型支持和编译器属性名校验。目标方法为Example.GetTopsContainingTheSecretWord,调用方式如下:

var topsContainingTheSecretWord = new Example().GetTopsContainingTheSecretWord("Forbidden");

实体类定义

public class Outer
{
    public string id { get; set; }
    public string Name { get; set; }
    public List<Middle> Middles { get; set; }
}

public class Middle
{
    public string id { get; set; }
    public string Name { get; set; }
    public List<Inner> Inners { get; set; }
}

public class Inner
{
    public string id { get; set; }
    public string Secret { get; set; }
    public string Name { get; set; }
}

public record ResultRecord(
    Guid TopId,
    Guid MiddleId,
    Guid BottomId
);

修正后的LINQ实现

原代码存在查询类型错误、嵌套集合名称不匹配、上层ID丢失等问题,以下是修正后的可运行代码:

public class Example {
    private readonly Container _plansContainer;

    // 通过构造函数注入Cosmos容器实例
    public Example(Container plansContainer)
    {
        _plansContainer = plansContainer;
    }

    public async Task<List<ResultRecord>> GetTopsContainingTheSecretWord(
        string secretWord,
        CancellationToken cancellationToken = default
    )
    {
        var query = _plansContainer.GetItemLinqQueryable<Outer>(true)
            // 展开Outer的Middles,同时保留Outer的ID
            .SelectMany(outer => outer.Middles.Select(middle => new { outer, middle }))
            // 展开Middle的Inners并过滤Secret匹配项,同时构造ResultRecord
            .SelectMany(
                om => om.middle.Inners.Where(inner => inner.Secret == secretWord),
                (om, inner) => new ResultRecord(
                    Guid.Parse(om.outer.id),
                    Guid.Parse(om.middle.id),
                    Guid.Parse(inner.id)
                )
            )
            .ToFeedIterator();

        var results = new List<ResultRecord>();
        while (query.HasMoreResults)
        {
            var response = await query.ReadNextAsync(cancellationToken);
            results.AddRange(response.Resource);
        }

        return results;
    }
}

修正说明

  1. 初始查询目标改为Outer类型,匹配数据根实体
  2. 使用带结果选择器的SelectMany,在展开嵌套集合时保留上层实体ID,避免信息丢失
  3. 修正集合名称错误:将原代码中的m.Bottoms改为om.middle.Inners,匹配实体类定义
  4. 添加字符串ID到Guid的转换,符合ResultRecord的类型要求
  5. 完善容器注入逻辑,确保_plansContainer正确初始化
  6. 在异步方法中传入取消令牌,符合异步编程最佳实践

内容的提问来源于stack exchange,提问作者DrFloyd5

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 22:19:50