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

如何实现多映射索引的连接与投影以关联多集合数据?

解决方案:基于RavenDB多Map-Reduce索引实现系统通知关联查询

针对你的需求,我们可以通过统一中间结构的多Map-Reduce索引解决映射与输出结构不一致的问题,同时支持分页查询,无需多步操作或Include。

1. 定义索引输出结构

先创建索引的中间输出类,统一承载实体和通知的聚合信息:

public class SystemNotificationsIndexResult
{
    public string SystemId { get; set; }
    public string EntityId { get; set; }
    public string EntityName { get; set; }
    public List<NotificationEntry> Notifications { get; set; } = new();

    public class NotificationEntry
    {
        public string NotificationId { get; set; }
        public string Message { get; set; }
    }
}

2. 实现多Map-Reduce索引

通过AbstractMultiMapIndexCreationTask分别映射三个集合,再通过Reduce聚合数据:

public class SystemNotificationsIndex : AbstractMultiMapIndexCreationTask<SystemNotificationsIndexResult>
{
    public SystemNotificationsIndex()
    {
        // 映射Components集合:输出实体基本信息,通知列表为空
        AddMap<Component>(components =>
            from component in components
            select new SystemNotificationsIndexResult
            {
                SystemId = component.SystemId,
                EntityId = component.Id,
                EntityName = component.Name,
                Notifications = new List<NotificationEntry>()
            });

        // 映射Resources集合:同Components逻辑
        AddMap<Resource>(resources =>
            from resource in resources
            select new SystemNotificationsIndexResult
            {
                SystemId = resource.SystemId,
                EntityId = resource.Id,
                EntityName = resource.Name,
                Notifications = new List<NotificationEntry>()
            });

        // 映射Notifications集合:通过RecipientID加载关联实体的SystemId,输出通知信息
        AddMap<Notification>(notifications =>
            from notification in notifications
            let recipient = LoadDocument<dynamic>(notification.RecipientID)
            where recipient != null // 过滤无效的RecipientID
            select new SystemNotificationsIndexResult
            {
                SystemId = recipient.SystemId,
                EntityId = notification.RecipientID,
                EntityName = null, // 留空,Reduce阶段合并实体名称
                Notifications = new List<NotificationEntry>
                {
                    new NotificationEntry
                    {
                        NotificationId = notification.Id,
                        Message = notification.Message
                    }
                }
            });

        // Reduce逻辑:按SystemId+EntityId分组,合并实体名称和通知列表
        Reduce = results =>
            from result in results
            group result by new { result.SystemId, result.EntityId } into g
            select new SystemNotificationsIndexResult
            {
                SystemId = g.Key.SystemId,
                EntityId = g.Key.EntityId,
                // 取来自Component/Resource的非空实体名称
                EntityName = g.FirstOrDefault(x => !string.IsNullOrEmpty(x.EntityName))?.EntityName,
                // 合并所有关联的通知
                Notifications = g.SelectMany(x => x.Notifications).ToList()
            };

        // 为SystemId创建索引,加速查询过滤
        Index(x => x.SystemId, FieldIndexing.Analyzed);
    }
}

3. 查询索引并投影结果

直接查询索引,按SystemId过滤并支持分页,最后投影成你需要的返回结构:

// 定义返回结构的DTO
public class SystemNotificationsResponse
{
    public string System { get; set; }
    public List<EntityNotificationGroup> Notifications { get; set; } = new();
}

public class EntityNotificationGroup
{
    public string Entity { get; set; }
    public string EntityName { get; set; }
    public List<NotificationDto> Notifications { get; set; } = new();
}

public class NotificationDto
{
    public string Id { get; set; }
    public string Message { get; set; }
}

// 查询方法
public async Task<SystemNotificationsResponse> GetSystemNotificationsAsync(string systemId, int pageSize, int pageNumber)
{
    using var session = DocumentStoreHolder.Store.OpenAsyncSession();

    var indexedResults = await session.Query<SystemNotificationsIndexResult, SystemNotificationsIndex>()
        .Where(x => x.SystemId == systemId)
        .Skip((pageNumber - 1) * pageSize)
        .Take(pageSize)
        .ToListAsync();

    return new SystemNotificationsResponse
    {
        System = systemId,
        Notifications = indexedResults.Select(r => new EntityNotificationGroup
        {
            Entity = r.EntityId,
            EntityName = r.EntityName,
            Notifications = r.Notifications.Select(n => new NotificationDto
            {
                Id = n.NotificationId,
                Message = n.Message
            }).ToList()
        }).ToList()
    };
}

关键说明

  • Map阶段的关联加载:在Notifications的Map中使用LoadDocument获取关联实体的SystemId,这是RavenDB支持的索引内关联,不会产生额外查询开销。
  • Reduce的聚合逻辑:通过SystemId+EntityId分组,确保每个实体只聚合一次,同时合并来自Component/Resource的名称和所有关联通知。
  • 分页支持:直接在索引查询中使用Skip/Take,一次查询即可拿到分页后的聚合数据,无需多步操作。
  • 数据一致性:索引会自动监听三个集合的文档变化,实时更新聚合结果,保证数据最新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 14:45:57