说我有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();
}
正如您所看到的,我正在努力找出检索MentorCount
和MenteeCount
值的最佳方法。我写了一些我认为应该做的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)
});
}
}
答案 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();