如何将多个计数字段检索到单个用户报告中?

时间:2012-11-23 16:36:36

标签: ravendb

说我有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个实例的列表。

UserService

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();
}

正如您所看到的,我正在努力找出检索MentorCountMenteeCount值的最佳方法。我写了一些我认为应该做的Map / Reduce Indexes,但我不确定如何使用它们来实现我想要的结果。

问题

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


编辑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;
}

MultiMap索引

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)
        });
    }
}

1 个答案:

答案 0 :(得分:2)

对于已经与用户保持关系数据的模型,您可能会得到更好的服务。这可能类似于:

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

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

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)
                };
  }
}

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

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();