【问题标题】:Finding objects which contains at least one element from subset using RavenDB and LINQ使用 RavenDB 和 LINQ 从子集中查找至少包含一个元素的对象
【发布时间】:2012-11-18 23:33:20
【问题描述】:

我有简单的类型Question:

public class Question
{
    public Question(string id)
    {
        Id = id;
        Tags = new List<string>();
    }

    public string Id { get; private set; }
    public IList<string> Tags { get; set; }            
}

我已经定义了此类问题的样本集合:

var q1 = new Question("q1") { Tags = new List<string>() {"a"} };
var q2 = new Question("q2") { Tags = new List<string>() {"b"} };
var q3 = new Question("q3") { Tags = new List<string>() {"a", "b", "c"} };
var q4 = new Question("q4") { Tags = new List<string>() {"a", "b"} };
var q5 = new Question("q5") { Tags = new List<string>() {"z"} };
var q6 = new Question("q6");
var questions = new List<Question>() {q1, q2, q3, q4, q5, q6};

现在我需要从给定的子集中找到包含至少一个标签的所有问题。子集定义如下:

string[] tags = new[] {"a", "b"};

我希望返回 q1q2q3q4。我用来获得所需结果的查询是:

var questions = DocumentSession.Query<Question>().AsQueryable();
questions = GetQuestionsToContainingAtLeastOneTagFromSubset(questions, tags)
// some other query restrictions
var result = questions.ToList(); // (**)

假设对我的收藏施加限制的功能如下:

private IQueryable<Question> GetQuestionsToContainingAtLeastOneTagFromSubset(IQueryable<Question> questions, IEnumerable<string> tags)
{
    var result = new List<Question>();
    foreach (var tag in tags)
    {
        var currentTag = tag;
        var resultForTag = questions.Where(x => x.Tags.Any(xTag => xTag == currentTag));
        result = result.Concat(resultForTag).ToList();
     }
     return result.GroupBy(x => x.Id).Select(grp => grp.First()).AsQueryable();
}

我认为这是非常低效的。我想避免在提供的函数中使用.ToList()。据我了解,.ToList() 表达式查询 RavenDB 并返回部分结果(顺便说一句:我说得对吗?)。这效率不高。我只想在提供的函数中施加限制,并在施加所有限制后执行查询。 (**) 的地方很适合我将批处理发送到 RavenDB 检索查询结果。

如何改进?

【问题讨论】:

    标签: c# .net linq ravendb


    【解决方案1】:

    您可以查询 lucene 以获取与您的标签数组匹配的标签的问题,如下所示:

    string[] tags = new[] { "a", "b" };
    string queryRange = "(" + string.Join(" OR ", tags) + ")";
    
    var res = session.Advanced.LuceneQuery<Question>()
                   .Where("Tags:" + queryRange).ToList();
    

    请注意,这会查询整个索引问题,而不是子集。但我认为您可以将 - I GUESS - 附加到查询字符串表达式中。 有关此类查询的更多信息,请参阅 Lucene 文档: http://lucene.apache.org/core/old_versioned_docs/versions/3_0_0/queryparsersyntax.html

    【讨论】:

    • 感谢您的提示。我更改了我的问题中的数据只是因为我想向您展示这没有按预期工作。调试器中的 Lucene 查询转换为 {Tags: a OR b},并且由于某种原因它只返回 q1、q3 和 q4。它假设也返回 q2,但事实并非如此。此外,我想在这个特定示例中避免使用 LuceneQuery。
    • 试试这个 queryRange: string queryRange = "(" + string.Join(" OR ", tags) + ")";我更新了我的答案。至于不使用显式 lucene 查询...我确实更喜欢 linq 查询,但是如果 lucene 提供并且 ravendb 公开,为什么不使用它呢? :) 这很简单
    • 谢谢,现在可以使用了。看起来确实很简单。我想避免 Lucene 查询,因为我考虑过建立连续的限制。虽然我已经添加了一些限制的 IQueryable 集合,但我无法进一步应用 Lucene 限制。可以从 Raven.Client.IDocumentSession 访问构建 Lucene 查询,我很想应用同样的限制,但使用 IQueryable 和 Linq。
    【解决方案2】:

    试试这个代码:

     var q1 = new Question("q1") { Tags = new List<string>() {"aa", "bb"} };
     var q2 = new Question("q2") { Tags = new List<string>() {"aa"} };
     var q3 = new Question("q3") { Tags = new List<string>() {"aa", "bb", "cc"} };
     var q4 = new Question("q4");
     var questions = new List<Question>() {q1, q2, q3, q4};
     string[] tags = new[] { "bb", "cc" };
    
     var res = (from p in questions where 
               (from q in tags where p.Tags.Contains(q) == true select p).ToArray().Count() >= 1 
                select p).Distinct(); 
     //If you want more speed you can try to add .AsParallel() after .Distinct()
    

    【讨论】:

    • 您的第一个 where 子句可以简化为(from q in tags where p.Tags.Contains(q) select p).Any() select p),但无论如何,RavenDB 会抛出异常:System .InvalidOperationException: 无法理解如何翻译值....tags.Where(q => p.Tags.Contains(q)).Select(q => p)
    • 使用 linq-to-objects 可行,但 Raven 驱动程序在处理该问题时会遇到一些问题并引发相同类型的异常。
    • 请注意,RavenDB 不支持(至少目前)一些重要的可查询操作(例如 .Selectmany),因此尽管 API 通过某种可查询的方式公开,但预计不会工作。 ..IQueryable 在合约中定义了如此多的操作,几乎不可能以这种方式实现它们,以至于它应该与外部源(不是 clr 对象)一起使用。
    • 我知道,我只是在问,因为也许我可以用任何更好的方式来完成它,而我现在不知道该怎么做。如果没有,我就用 Lucene。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-01
    • 1970-01-01
    • 2011-01-30
    • 1970-01-01
    相关资源
    最近更新 更多