我在RavenDB中存储了一组简单的对象:
public class Question
{
public string Id { get; set; }
public DateTime CreatedOn { get; set; }
public ICollection<User> Supporters { get; set; }
public ICollection<Answer> Answers { get; set; }
}
public class Answer
{
public string Id { get; set; }
public bool IsOfficial { get; set; }
}
现在我想查询RavenDB给我一组问题,首先按照支持者的数量排序,然后按条件排序 - 如果问题有任何正式答案,并且在问题创建日期。所以我写了一个查询:
var questions = DocumentSession.Query<Question>().AsQueryable();
questions = questions
.OrderByDescending(x => x.Supporters.Count)
.ThenByDescending(x => x.Answers.Any(a => a.IsOfficial)) //EDIT: source of exception
.ThenByDescending(x => x.CreatedOn)
.Take(15);
var result = questions.ToList();
引发异常:
System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'
查询在逻辑上是正确的,当我使用linq-to-objects时,只需将 .ToList()添加到第一行:
var questions = DocumentSession.Query<Question>().Tolist().AsQueryable();
// next lines stay unchanged
由于性能问题,我不想这样做(此更改会强制所有问题在过滤前从数据库加载到内存中)。
如何在不影响性能的情况下使其正常运行?也许shell我定义了一个索引?它应该如何呢?
答案 0 :(得分:3)
用于您的目的的自定义索引基本上将是您的类的重新创建,其中包含额外的字段(以及支持它的一些逻辑)。您似乎不想在当前类中添加更多字段,是否可以在项目中添加更多类?
以下是一个例子:
public class Question_WithAnyOfficial: AbstractIndexCreationTask<Question>
{
public class Question_WithAnyOfficial()
{
Map = questions => from question in questions
// New Anonymous Type
select new
{
Id = question.Id,
CreatedOn = question.CreatedOn,
Supporters = question.Supporters,
Answers = question.Answers,
AnyOfficial = question.Answers.Where(a => a.IsOfficial).Any()
};
}
}
然后你可以查询:
var questions = DocumentSession.Query<Question_WithAnyOfficial>()
.OrderByDescending(x => x.Supporters.Count)
.ThenByDescending(x => x.AnyOfficial)
.ThenByDescending(x => x.CreatedOn)
.Take(15)
.ToList();
不要忘记在应用启动时您必须注册索引。
答案 1 :(得分:2)
Raven不能支持LINQ查询中的计算,所以这应该有效(删除了问题子句):
var questions = DocumentSession.Query<Question>()
.OrderByDescending(x => x.Supporters.Count)
//.ThenByDescending(x => x.Answers.Any(a => a.IsOfficial))
.ThenByDescending(x => x.CreatedOn)
.Take(15);
var result = questions.ToList();
如果要包含该逻辑,则需要在类上使用名为AreAllAnswersOfficial
的字段(或类似内容)。然后你可以把它放在子句中:
var questions = DocumentSession.Query<Question>()
.OrderByDescending(x => x.Supporters.Count)
.ThenByDescending(x => x.AreAllAnswersOfficial)
.ThenByDescending(x => x.CreatedOn)
.Take(15);
var result = questions.ToList();
答案 2 :(得分:0)
根据熊亚历山大的反应,我这样做了:
public class QuestionByAnyOfficial : AbstractIndexCreationTask<Question, QuestionByAnyOfficial.Result>
{
public class Result
{
public string Id;
public bool AnyOfficial;
public int SupportersCount;
public DateTime CreatedOn;
}
public QuestionByAnyOfficial()
{
Map = questions => from question in questions
select new
{
Id = question.Id,
AnyOfficial = question.Answers.Any(a => a.IsOfficial),
SupportersCount = question.Supporters.Count,
CreatedOn = question.CreatedOn
};
}
}
var questionIds = DocumentSession.Query<QuestionByAnyOfficial.Result, QuestionByAnyOfficial>()
.OrderByDescending(x => x.SupportersCount)
.ThenByDescending(x => x.AnyOfficial)
.ThenByDescending(x => x.CreatedOn)
.Take(NumberOfQuestions)
.Select(x => x.Id);
var questions = DocumentSession.Load<Question>(questionIds);
var result = questions.ToList();
它有效,我相信它比我的原始版本更有效。如果它可以以更优雅的方式完成,我会感激任何想法。问候。