Reputation: 17064
I have simple type 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; }
}
I have defined sample collection of such questions:
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};
Now I need to find all questions, which contains at least all tags, from given subset. Subset is defined below:
string[] tags = new[] {"aa", "bb"};
The query which I use to get desired result is:
var result = questions.Where(x => !tags.Except(x.Tags).Any()).ToList();
Result is a list of 2 questions: q1 and q3, which works properly while I'm using linq-to-objects.
Unfortunately, while I'm trying to query such questions, which are now persisted in RavenDB, I get an exception:
var result = DocumentSession.Query<Question>()
.Where(x => !tags.Except(x.Tags).Any()).ToList();
results in:
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)
...
How to perform what I want using RavenDB ?
Upvotes: 4
Views: 2030
Reputation: 22956
Jarek, what you want to do is:
var q = session.Query<Question>();
foreach(var tag in tags)
{
var currentTag = tag;
q = q.Where(x=>x.Tags.Any(xTag=>xTag == currentTag));
}
This will give you all the questions that have at least all those tags.
Upvotes: 6
Reputation: 10291
The RavenDB LINQ provider doesn't support this syntax because the underlying index mechanism (Lucene) doesn't allow this type of query.
However there is a new feature in RavenDB that will allow you to do this, see this thread for more info. Note you'll need to get the latest build to be able to use this feature.
You should be able to write your query as:
var result = session.Query<Question>("IndexName")
.Where(x => x.Tags.Any(t => t == "aa"))
.Intersect()
.Where(x => x.Tags.Any(t => t == "bb")
.ToList();
And the index needs to look something like this:
from qu in docs.Questions
from tag in qu.Tags
select new { tag }
Thanks, I've been looking for another scenario for this new feature, so when I get a chance I'll create a gist showing the full sample.
Upvotes: 0
Reputation: 13670
It looks like the LINQ Provider did not implement Except as part of the Query Pattern. If I understand your requirement enough, you might be able to use SequenceEquals.
var result = questions.Where(q => q.Tags.SequenceEqual(tags));
Using the code you provided, this returned exactly one result {"aa","bb"}
. If the RavenDB Provider doesn't provide enough of the Query Pattern implementation for you, then just do it without using LINQ at all.
Upvotes: 1