You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
LINQ: VisitWhere folds a Where into the root WHERE after GroupBy/OfType/SkipWhile/Take, and from a Join inner sequence #186
BTreeExpressionVisitor.VisitWhere (~L57-88) merges every Queryable.Where it reaches into QueryModel.WhereClause, whatever operators come before it and whichever sequence it belongs to. The provider then pre-filters with FetchAsync(WhereClause) before EnumerableRewriter replays the full expression. That breaks in two ways.
1. Where after an operator that changes the element type or depends on order
OfType<X>, Cast<X>, SelectMany, Join and Zip followed by Where go down the same path.
2. Where on another sequence (e.g. a Join inner)
For operators the visitor doesn't model, VisitMethodCall falls through to base.VisitMethodCall, which visits every argument. A Where on the inner sequence of a Join is therefore ANDed into the outer collection's predicate:
db.VectorItems.AsQueryable().Where(x =>x.Title=="B").Join(db.VectorItems.AsQueryable().Where(y =>y.Title=="A"),
x =>1, y =>1,(x,y)=>x.Title+"/"+y.Title).ToList();
With items A, A, B, the expected result is ["B/A", "B/A"]. The actual result is [], because the outer fetch runs Title == "B" && Title == "A". The VectorSearch variant (.Join(VectorItems.Where(y => y.Embedding.VectorSearch(q, 1)), ...)) also returns [] instead of ["B/A"].
All of the above behaves the same on main.
Suggested fix
Merge into WhereClause only the Where calls on the root chain (reached through Arguments[0]) where every operator below them is Where/OrderBy*/ThenBy* and the lambda takes the entity type. That is the CanFoldPredicate check from #180. Leave every other Where to EnumerableRewriter.
Activity
fix(linq): fold only root-chain Where calls into the fetch predicate (#…