【问题标题】:Finding objects which contains at least all elements from subset using RavenDB and LINQ使用 RavenDB 和 LINQ 查找至少包含子集中所有元素的对象
【发布时间】:2012-04-12 09:38:19
【问题描述】:

我有简单的类型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>() {"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[] {"aa", "bb"};

我用来获得所需结果的查询是:

var result = questions.Where(x => !tags.Except(x.Tags).Any()).ToList();

结果是 2 个问题的列表:q1q3,在我使用 linq-to-objects 时可以正常工作。

不幸的是,当我尝试查询这些问题(这些问题现在保留在 RavenDB 中)时,我遇到了一个异常:

var result = DocumentSession.Query<Question>()
                     .Where(x => !tags.Except(x.Tags).Any()).ToList();

结果:

System.InvalidOperationException: Cannot understand how to translate value(Core.Commands.GetQuestions+<>c__DisplayClass0).tags.Except(x.Tags)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.GetPath(Expression expression, String& path, Type& memberType, Boolean& isNestedPath)
   at Raven.Client.Linq.DynamicQueryProviderProcessor`1.GetMember(Expression expression)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.VisitAny(MethodCallExpression expression)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.VisitEnumerableMethodCall(MethodCallExpression expression)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.VisitMethodCall(MethodCallExpression expression)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.VisitExpression(Expression expression)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.VisitExpression(Expression expression)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.VisitQueryableMethodCall(MethodCallExpression expression)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.VisitMethodCall(MethodCallExpression expression)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.VisitExpression(Expression expression)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.GetLuceneQueryFor(Expression expression)
   at Raven.Client.Linq.RavenQueryProviderProcessor`1.Execute(Expression expression)
   at Raven.Client.Linq.DynamicRavenQueryProvider`1.Execute(Expression expression)
   at Raven.Client.Linq.DynamicRavenQueryProvider`1.System.Linq.IQueryProvider.Execute(Expression expression)
   at Raven.Client.Linq.RavenQueryInspector`1.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
...

如何使用 RavenDB 执行我想要的操作?

【问题讨论】:

  • 不熟悉 Raven 驱动,但尝试不同的场景:示例包含、相交...

标签: c# .net linq ravendb


【解决方案1】:

贾雷克, 你想做的是:

var q = session.Query<Question>();
foreach(var tag in tags)
{
    var currentTag = tag;
    q = q.Where(x=>x.Tags.Any(xTag=>xTag == currentTag));
}

这将为您提供至少具有所有这些标签的所有问题。

【讨论】:

  • 在 foreach 迭代之后,我在调试器中检查了 q 变量,它被定义为:{(Tags:bb) AND (Tags:bb)},所以这里有些不正确。我正在遍历列表标签 = ["aa","bb"]。第一个标签被埋在某个地方。
  • 这可能与被捕获的变量有关,因为您有一个 lambda。试试这里的代码gist.github.com/8016f3c8f9a6d8cd485f
  • 谢谢,类似的问题解释如下:confluence.jetbrains.net/display/ReSharper/…
【解决方案2】:

看起来 LINQ 提供程序没有将 except 实现为查询模式的一部分。如果我足够了解您的要求,您也许可以使用 SequenceEquals。

var result = questions.Where(q => q.Tags.SequenceEqual(tags));

使用您提供的代码,这恰好返回了一个结果{"aa","bb"}。如果 RavenDB Provider 没有为您提供足够多的查询模式实现,那么干脆不使用 LINQ。

【讨论】:

  • 谢谢,但是使用 linq-to-objects 只会返回与提供的子集具有相同标签的问题。所以这不是我想要实现的。此外,使用 RavenDB,此语法会引发另一个异常:“System.NotSupportedException: Method not supported: SequenceEqual”。
  • 然后将结果提取到一个对象中,并且不对需要查询模式转换的 RavenDB 执行查询。
【解决方案3】:

RavenDB LINQ 提供程序不支持这种语法,因为底层索引机制 (Lucene) 不允许这种类型的查询。

但是,RavenDB 中有一个新功能可以让您执行此操作,请参阅this thread 了解更多信息。 注意您需要获得最新版本才能使用此功能。

您应该能够将查询编写为:

var result = session.Query<Question>("IndexName")
        .Where(x => x.Tags.Any(t => t == "aa"))
        .Intersect()
        .Where(x => x.Tags.Any(t => t == "bb")
        .ToList();

并且索引需要看起来像这样:

from qu in docs.Questions
from tag in qu.Tags
select new { tag }

谢谢,我一直在寻找这个新功能的另一个场景,所以当我有机会时,我会创建一个显示完整示例的要点。

【讨论】:

  • 我已经构建了#888(服务器和客户端),但是在尝试使用您的提示时出现异常:System.NotSupportedException:不支持的方法:相交。有没有其他方法可以做到这一点?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-24
  • 2021-09-16
相关资源
最近更新 更多