1

假设我有这样的User课程:

public class User
{
   public string Id {get; set;}
   public string Name {get; set;}
}

每个用户可以是导师、被指导者或两者兼而有之。这由一个Relationship类表示:

public class Relationship
{
   public string MentorId {get; set;} // This is a User.Id
   public string MenteeId {get; set;} // This is another User.Id
}

现在我想生成一个报告,列出我的所有用户,并包含一个名为的字段Mentor Count和另一个名为Mentee Count. 为了实现这一点,我创建了一个UserReportDTO类来保存我的报告数据。

public class UserReportDTO
{
   public string Name {get; set;}
   public string MentorCount {get; set;}
   public string MenteeCount {get; set;}
}

然后我查询我的 RavenDB 以获取所有列表Users并将其转换为UserReportDTO实例列表。

用户服务

public List<UserReportDTO> GetReportChunk(
    IDocumentSession db,
    int skip = 0,
    int take = 1024)
{
    return db.Query<User>()
            .OrderBy(u => u.Id)
            .Skip(skip)
            .Take(take)
            .ToList()
            .Select(user =>
                new UserReportDTO
                {
                    Name = user.Name,
                    MentorCount = // What goes here?
                    MenteeCount = // What goes here?
                })
            .ToList();
}

如您所见,我正在努力找出检索MentorCountandMenteeCount值的最佳方法。我已经编写了一些我认为应该完成工作的 Map/Reduce 索引,但我不确定如何使用它们来实现我想要的结果。

问题

将多个聚合字段包含到单个查询中的最佳方法是什么?


编辑 1

@Matt Johnson:我已经实现了你的索引(见末尾),现在有一个有效的报告查询,如果有人感兴趣,它看起来像这样:

工作用户报表查询

public List<UserDTO> GetReportChunk(IDocumentSession db, Claim claim, int skip = 0, int take = 1024)
{
    var results = new List<UserDTO>();
    db.Query<RavenIndexes.Users_WithRelationships.Result, RavenIndexes.Users_WithRelationships>()
        .Include(o => o.UserId)
        .Where(x => x.Claims.Any(c => c == claim.ToString()))
        .OrderBy(x => x.UserId)
        .Skip(skip)
        .Take(take)
        .ToList()
        .ForEach(p =>
            {
                var user = db.Load<User>(p.UserId);
                results.Add(new UserDTO
                {
                        UserName = user.UserName,
                        Email = user.Email,
                        // Lots of other User properties
                        MentorCount = p.MentorCount.ToString(),
                        MenteeCount = p.MenteeCount.ToString()
                });
            });

    return results;
}

多图索引

public class Users_WithRelationships :
        AbstractMultiMapIndexCreationTask<Users_WithRelationships.Result>
{
    public class Result
    {
        public string UserId { get; set; }
        public string[] Claims { get; set; }
        public int MentorCount { get; set; }
        public int MenteeCount { get; set; }
    }

    public Users_WithRelationships()
    {
        AddMap<User>(users => users.Select(user => new
        {
                UserId = user.Id,
                user.Claims,
                MentorCount = 0,
                MenteeCount = 0
        }));

        AddMap<Relationship>(relationships => relationships.Select(relationship => new
        {
                UserId = relationship.MentorId,
                Claims = (string[]) null,
                MentorCount = 0,
                MenteeCount = 1
        }));

        AddMap<Relationship>(relationships => relationships.Select(relationship => new
        {
                UserId = relationship.MenteeId,
                Claims = (string[]) null,
                MentorCount = 1,
                MenteeCount = 0
        }));

        Reduce = results => results.GroupBy(result => result.UserId).Select(g => new
        {
                UserId = g.Key,
                Claims = g.Select(x => x.Claims).FirstOrDefault(x => x != null),
                MentorCount = g.Sum(x => x.MentorCount),
                MenteeCount = g.Sum(x => x.MenteeCount)
        });
    }
}
4

1 回答 1

2

使用已经与用户保存关系数据的模型可能会更好地为您服务。这可能看起来像:

public class User
{
  public string Id { get; set; }
  public string Name { get; set; }
  public string[] MentorUserIds { get; set; }
  public string[] MenteeUserIds { get; set; }
}

但是,如果您想坚持使用您描述的模型,解决方案是摆脱多个单独的索引并创建一个包含您需要的数据的单个多地图索引。

public class Users_WithRelationships
           : AbstractMultiMapIndexCreationTask<Users_WithRelationships.Result>
{
  public class Result
  {
    public string UserId { get; set; }
    public string Name { get; set; }
    public int MentorCount { get; set; }
    public int MenteeCount { get; set; }
  }

  public Users_WithRelationships()
  {
    AddMap<User>(users => from user in users
                          select new
                          {
                            UserId = user.Id,
                            Name = user.Name,
                            MentorCount = 0,
                            MenteeCount = 0
                          });

    AddMap<Relationship>(relationships => from relationship in relationships
                                          select new
                                          {
                                            UserId = relationship.MentorId,
                                            Name = (string)null,
                                            MentorCount = 1,
                                            MenteeCount = 0
                                          });

    AddMap<Relationship>(relationships => from relationship in relationships
                                          select new
                                          {
                                            UserId = relationship.MenteeId,
                                            Name = (string)null,
                                            MentorCount = 0,
                                            MenteeCount = 1
                                          });

    Reduce = results => 
                from result in results
                group result by result.UserId
                into g
                select new
                {
                  UserId = g.Key,
                  Name = g.Select(x => x.Name).FirstOrDefault(x => x != null),
                  MentorCount = g.Sum(x => x.MentorCount),
                  MenteeCount = g.Sum(x => x.MenteeCount)
                };
  }
}

GetReportChunk然后,如果您仍想投影自定义 DTO,则可以更新您的方法以查询一个索引。

return db.Query<Users_WithRelationships.Result, Users_WithRelationships>()
                 .OrderBy(x => x.UserId)
                 .Skip(skip)
                 .Take(take)
                 .Select(x =>
                         new UserReportDTO
                         {
                             Name = x.Name,
                             MentorCount = x.MentorCount,
                             MenteeCount = x.MenteeCount,
                         })
                 .ToList();
于 2012-11-23T18:29:29.130 回答