chriseldredge / Lucene.Net.Linq

LINQ provider to run native queries on a Lucene.Net index
Other
151 stars 66 forks source link

Unable to search within IEnumerable<int> field #80

Closed xumix closed 9 years ago

xumix commented 9 years ago

The model looks like

public IEnumerable<int> AccommodationAges { get; set; }
public decimal TotalPriceMin { get; set; }
public decimal TotalPriceMax { get; set; }

the query:

tourMap.Property(p => p.AccommodationAges).Stored().NotAnalyzed();
tourMap.Property(p => p.TotalPriceMin).Stored().NotAnalyzed();
tourMap.Property(p => p.TotalPriceMax).Stored().NotAnalyzed();

var q = session.Query().Where(t => t.TotalPriceMin >= 10 && t.TotalPriceMax <= 1000000);
                for (int i = 2; i < 10; i++)
                {
                    var age = i;
                    q = q.Where(t => t.AccommodationAges.Contains(age));
                }
var search = q.ToArray();

The error:

System.InvalidOperationException occurred
  HResult=-2146233079
  Message=The binary operator Equal is not defined for the types 'System.Collections.Generic.IEnumerable`1[System.Int32]' and 'System.Int32'.
  Source=System.Core
  StackTrace:
       at System.Linq.Expressions.Expression.GetEqualityComparisonOperator(ExpressionType binaryType, String opName, Expression left, Expression right, Boolean liftToNull)
       at System.Linq.Expressions.Expression.Equal(Expression left, Expression right, Boolean liftToNull, MethodInfo method)
       at System.Linq.Expressions.Expression.MakeBinary(ExpressionType binaryType, Expression left, Expression right, Boolean liftToNull, MethodInfo method, LambdaExpression conversion)
       at System.Linq.Expressions.Expression.MakeBinary(ExpressionType binaryType, Expression left, Expression right)
       at Lucene.Net.Linq.Transformation.TreeVisitors.SubQueryContainsTreeVisitor.VisitSubQueryExpression(SubQueryExpression expression)
       at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitExpression(Expression expression)
       at Remotion.Linq.Clauses.WhereClause.TransformExpressions(Func`2 transformation)
       at Lucene.Net.Linq.Transformation.QueryModelTransformer.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index)
       at Remotion.Linq.Clauses.WhereClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel, Int32 index)
       at Remotion.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection`1 bodyClauses, QueryModel queryModel)
       at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
       at Remotion.Linq.QueryModel.Accept(IQueryModelVisitor visitor)
       at Lucene.Net.Linq.Transformation.QueryModelTransformer.TransformQueryModel(QueryModel queryModel)
       at Lucene.Net.Linq.LuceneQueryExecutorBase`1.PrepareQuery(QueryModel queryModel)
       at Lucene.Net.Linq.LuceneQueryExecutorBase`1.<ExecuteCollection>d__3`1.MoveNext()
       at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
       at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
       at RestChild.Booking.Logic.Indexing.TourIndexer.IndexTours() in c:\Projects\RestChild\RestChild.Booking.Logic\Indexing\TourIndexer.cs:line 211
  InnerException: 
chriseldredge commented 9 years ago

Fixed in 71ffd11fed78a157006f1b67e3cc5c8978930431.

xumix commented 9 years ago

Great, thanks!

chriseldredge commented 9 years ago

Released in v3.5.3

xumix commented 9 years ago

Well, there is opposite situation now:

query = query.Where(q => q.SubjectId.HasValue && parameter.Programs.Contains(q.SubjectId.Value));
System.InvalidCastException occurred
  HResult=-2147467262
  Message=Unable to cast object of type 'System.Linq.Expressions.UnaryExpression' to type 'System.Linq.Expressions.ConstantExpression'.
  Source=Lucene.Net.Linq
  StackTrace:
       at Lucene.Net.Linq.Transformation.TreeVisitors.SubQueryContainsTreeVisitor.VisitSubQueryExpression(SubQueryExpression expression)
       at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitExpression(Expression expression)
       at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression)
       at Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitExpression(Expression expression)
       at Remotion.Linq.Clauses.WhereClause.TransformExpressions(Func`2 transformation)
       at Lucene.Net.Linq.Transformation.QueryModelTransformer.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index)
       at Remotion.Linq.Clauses.WhereClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel, Int32 index)
       at Remotion.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection`1 bodyClauses, QueryModel queryModel)
       at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
       at Remotion.Linq.QueryModel.Accept(IQueryModelVisitor visitor)
       at Lucene.Net.Linq.LuceneQueryExecutorBase`1.PrepareQuery(QueryModel queryModel)
       at Lucene.Net.Linq.LuceneQueryExecutorBase`1.<ExecuteCollection>d__3`1.MoveNext()
       at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
       at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
       at RestChild.Booking.Logic.TourSearchService.SearchTours(SearchToursParams parameter) in c:\Projects\RestChild\RestChild.Booking.Logic\TourSearchService.cs:line 90
  InnerException: 
chriseldredge commented 9 years ago

This would be a separate feature to support this. Supposing that parameter.Programs is a set of int (with example values 3, 7, 10), in effect you are trying to execute a query like:

SubjectId:3 OR SubjectId:7 OR SubjectId:10

Lucene does not provide a way to do something like SubjectId:[3 OR 7 OR 10].

Since there is no native support for this type of query, Lucene.Net.Linq does not try to bolt one on.

As an alternative, you can construct your own BooleanQuery and use the IQueryable<T>.Where(Query) extension method provided by Lucene.Net.Linq.

xumix commented 9 years ago

This is rather strange, why not translate to this exact form? SubjectId:3 OR SubjectId:7 OR SubjectId:10 Looks like a simple and proper solution since the collection is finite

chriseldredge commented 9 years ago

It might be intuitive, but that doesn't make it trivial. Of course pull requests are always welcomed.

xumix commented 9 years ago

I've used this project https://www.nuget.org/packages/PredicateExtensions/1.0.0 to make it work like this:

if (parameter.Programs.NotNullOrEmpty())
{
    Expression<Func<IndexTourDto, bool>> pr = a => true;
    foreach (var program in parameter.Programs)
    {
        var tmp = program;
        pr = pr.Or(p => p.SubjectId == tmp);
    }

    query = query.Where(pr);
}

But it looks ugly :(