1using System.Diagnostics;
2using System.Linq.Expressions;
3using System.Runtime.CompilerServices;
12public partial class QuerySource<T>
16 private LookupTable Lookup {
get => Ctx.LookupTable; }
23 return Chain<T>(q:
QH.MethodCall(
Query,
"distinct"));
29 return Chain<T>(q:
QH.MethodCall(
Query,
"order"));
35 return Chain<T>(q:
QH.MethodCall(
Query,
"order", SubQuery(keySelector)));
41 return Chain<T>(q:
QH.MethodCall(
Query,
"order",
QH.FnCall(
"desc", SubQuery(keySelector))));
47 return Chain<T>(q:
QH.MethodCall(
Query,
"order",
QH.Expr(
"desc(x => x)")));
51 Chain<T>(q:
QH.MethodCall(
Query,
"reverse"));
55 var pl = SelectCall(
Query, selector);
60 Chain<T>(q:
QH.MethodCall(
Query,
"drop",
QH.Const(count)));
63 Chain<T>(q:
QH.MethodCall(
Query,
"take",
QH.Const(count)));
66 Chain<T>(q: WhereCall(
Query, predicate));
70 public bool All(Expression<Func<T, bool>> predicate) => Execute<bool>(AllImpl(predicate));
71 public Task<bool>
AllAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
72 ExecuteAsync<bool>(AllImpl(predicate), cancel);
73 private Pipeline AllImpl(Expression<Func<T, bool>> predicate)
75 RequireQueryMode(
"All");
78 q:
QH.MethodCall(
Query,
"every", SubQuery(predicate)),
82 public bool Any() => Execute<bool>(AnyImpl(
null));
83 public Task<bool>
AnyAsync(CancellationToken cancel =
default) =>
84 ExecuteAsync<bool>(AnyImpl(
null), cancel);
85 public bool Any(Expression<Func<T, bool>> predicate) => Execute<bool>(AnyImpl(predicate));
86 public Task<bool>
AnyAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
87 ExecuteAsync<bool>(AnyImpl(predicate), cancel);
88 private Pipeline AnyImpl(Expression<Func<T, bool>>? predicate) =>
91 q:
QH.MethodCall(MaybeWhereCall(
Query, predicate),
"nonEmpty"),
94 public int Count() => Execute<int>(CountImpl(
null));
95 public Task<int>
CountAsync(CancellationToken cancel =
default) =>
96 ExecuteAsync<int>(CountImpl(
null), cancel);
97 public int Count(Expression<Func<T, bool>> predicate) => Execute<int>(CountImpl(predicate));
98 public Task<int>
CountAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
99 ExecuteAsync<int>(CountImpl(predicate), cancel);
100 private Pipeline CountImpl(Expression<Func<T, bool>>? predicate) =>
103 q:
QH.MethodCall(MaybeWhereCall(
Query, predicate),
"count"),
106 public T
First() => Execute<T>(FirstImpl(
null));
107 public Task<T>
FirstAsync(CancellationToken cancel =
default) =>
108 ExecuteAsync<T>(FirstImpl(
null), cancel);
109 public T
First(Expression<Func<T, bool>> predicate) => Execute<T>(FirstImpl(predicate));
110 public Task<T>
FirstAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
111 ExecuteAsync<T>(FirstImpl(predicate), cancel);
112 private Pipeline FirstImpl(Expression<Func<T, bool>>? predicate) =>
115 q:
QH.MethodCall(AbortIfEmpty(MaybeWhereCall(
Query, predicate)),
"first"));
119 ExecuteAsync<T?>(FirstOrDefaultImpl(
null), cancel);
120 public T?
FirstOrDefault(Expression<Func<T, bool>> predicate) => Execute<T?>(FirstOrDefaultImpl(predicate));
121 public Task<T?>
FirstOrDefaultAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
122 ExecuteAsync<T?>(FirstOrDefaultImpl(predicate), cancel);
123 private Pipeline FirstOrDefaultImpl(Expression<Func<T, bool>>? predicate) =>
126 q:
QH.MethodCall(MaybeWhereCall(
Query, predicate),
"first"),
130 public T
Last() => Execute<T>(LastImpl(
null));
131 public Task<T>
LastAsync(CancellationToken cancel =
default) =>
132 ExecuteAsync<T>(LastImpl(
null), cancel);
133 public T
Last(Expression<Func<T, bool>> predicate) => Execute<T>(LastImpl(predicate));
134 public Task<T>
LastAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
135 ExecuteAsync<T>(LastImpl(predicate), cancel);
136 private Pipeline LastImpl(Expression<Func<T, bool>>? predicate) =>
139 q:
QH.MethodCall(AbortIfEmpty(MaybeWhereCall(
Query, predicate)),
"last"));
143 ExecuteAsync<T?>(LastOrDefaultImpl(
null), cancel);
144 public T?
LastOrDefault(Expression<Func<T, bool>> predicate) => Execute<T?>(LastOrDefaultImpl(predicate));
145 public Task<T?>
LastOrDefaultAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
146 ExecuteAsync<T?>(LastOrDefaultImpl(predicate), cancel);
147 private Pipeline LastOrDefaultImpl(Expression<Func<T, bool>>? predicate) =>
150 q:
QH.MethodCall(MaybeWhereCall(
Query, predicate),
"last"),
154 public long LongCount() => Execute<long>(LongCountImpl(
null));
156 ExecuteAsync<long>(LongCountImpl(
null), cancel);
157 public long LongCount(Expression<Func<T, bool>> predicate) => Execute<long>(LongCountImpl(predicate));
158 public Task<long>
LongCountAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
159 ExecuteAsync<long>(LongCountImpl(predicate), cancel);
160 private Pipeline LongCountImpl(Expression<Func<T, bool>>? predicate) =>
163 q:
QH.MethodCall(MaybeWhereCall(
Query, predicate),
"count"),
166 private static readonly
Query _maxReducer =
QH.Expr(
"(a, b) => if (a >= b) a else b");
168 public T
Max() => Execute<T>(MaxImpl<T>(
null));
169 public Task<T>
MaxAsync(CancellationToken cancel =
default) =>
170 ExecuteAsync<T>(MaxImpl<T>(
null), cancel);
171 public R
Max<R>(Expression<Func<T, R>> selector) => Execute<R>(MaxImpl(selector));
172 public Task<R>
MaxAsync<R>(Expression<Func<T, R>> selector, CancellationToken cancel =
default) =>
173 ExecuteAsync<R>(MaxImpl(selector), cancel);
174 private Pipeline MaxImpl<R>(Expression<Func<T, R>>? selector, [CallerMemberName]
string callerName =
"")
176 RequireQueryMode(callerName);
179 q:
QH.MethodCall(MaybeMap(AbortIfEmpty(
Query), selector),
"reduce", _maxReducer),
183 private static readonly
Query _minReducer =
QH.Expr(
"(a, b) => if (a <= b) a else b");
185 public T
Min() => Execute<T>(MinImpl<T>(
null));
186 public Task<T>
MinAsync(CancellationToken cancel =
default) => ExecuteAsync<T>(MinImpl<T>(
null), cancel);
187 public R
Min<R>(Expression<Func<T, R>> selector) => Execute<R>(MinImpl(selector));
188 public Task<R>
MinAsync<R>(Expression<Func<T, R>> selector, CancellationToken cancel =
default) =>
189 ExecuteAsync<R>(MinImpl(selector), cancel);
190 private Pipeline MinImpl<R>(Expression<Func<T, R>>? selector, [CallerMemberName]
string callerName =
"")
192 RequireQueryMode(callerName);
195 q:
QH.MethodCall(MaybeMap(AbortIfEmpty(
Query), selector),
"reduce", _minReducer),
199 public double Average(Expression<Func<T, double>> selector) => Execute<double>(AverageImpl(selector));
200 public Task<double>
AverageAsync(Expression<Func<T, double>> selector, CancellationToken cancel =
default) =>
201 ExecuteAsync<double>(AverageImpl(selector), cancel);
203 private Pipeline AverageImpl<R>(Expression<Func<T, R>> selector)
205 RequireQueryMode(
"Average");
209 q:
QH.FnCall(
"Math.mean",
QH.MethodCall(
QH.MethodCall(AbortIfEmpty(
Query),
"map", SubQuery(selector)),
"toArray")),
213 public T
Single() => Execute<T>(SingleImpl(
null));
214 public Task<T>
SingleAsync(CancellationToken cancel =
default) => ExecuteAsync<T>(SingleImpl(
null), cancel);
215 public T
Single(Expression<Func<T, bool>> predicate) => Execute<T>(SingleImpl(predicate));
216 public Task<T>
SingleAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
217 ExecuteAsync<T>(SingleImpl(predicate), cancel);
218 private Pipeline SingleImpl(Expression<Func<T, bool>>? predicate) =>
221 q:
QH.MethodCall(AbortIfEmpty(Singularize(MaybeWhereCall(
Query, predicate))),
"first"));
224 public Task<T>
SingleOrDefaultAsync(CancellationToken cancel =
default) => ExecuteAsync<T>(SingleOrDefaultImpl(
null), cancel);
225 public T
SingleOrDefault(Expression<Func<T, bool>> predicate) => Execute<T>(SingleOrDefaultImpl(predicate));
226 public Task<T>
SingleOrDefaultAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
227 ExecuteAsync<T>(SingleOrDefaultImpl(predicate), cancel);
228 private Pipeline SingleOrDefaultImpl(Expression<Func<T, bool>>? predicate) =>
231 q:
QH.MethodCall(Singularize(MaybeWhereCall(
Query, predicate)),
"first"),
235 private static readonly
Query _sumReducer =
QH.Expr(
"(a, b) => a + b");
237 public int Sum(Expression<Func<T, int>> selector) => Execute<int>(SumImpl<int>(selector));
238 public Task<int>
SumAsync(Expression<Func<T, int>> selector, CancellationToken cancel =
default) =>
239 ExecuteAsync<int>(SumImpl<int>(selector), cancel);
240 public long Sum(Expression<Func<T, long>> selector) => Execute<long>(SumImpl<long>(selector));
241 public Task<long>
SumAsync(Expression<Func<T, long>> selector, CancellationToken cancel =
default) =>
242 ExecuteAsync<long>(SumImpl<long>(selector), cancel);
243 public double Sum(Expression<Func<T, double>> selector) => Execute<double>(SumImpl<double>(selector));
244 public Task<double>
SumAsync(Expression<Func<T, double>> selector, CancellationToken cancel =
default) =>
245 ExecuteAsync<double>(SumImpl<double>(selector), cancel);
246 private Pipeline SumImpl<R>(Expression<Func<T, R>> selector)
248 RequireQueryMode(
"Sum");
249 var seed = (typeof(R) == typeof(
int) || typeof(R) == typeof(
long)) ?
252 var mapped =
QH.MethodCall(
Query,
"map", SubQuery(selector));
255 q:
QH.MethodCall(mapped,
"fold", seed, _sumReducer),
261 private void RequireQueryMode([CallerMemberName]
string callerName =
"")
267 $
"Query is not pure: Earlier `Select` could not be translated to pure FQL.");
271 private R Execute<R>(Pipeline pl)
275 var res = ExecuteAsync<R>(pl);
279 catch (AggregateException ex)
281 throw TranslateException(ex.InnerExceptions.First());
285 private async Task<R> ExecuteAsync<R>(Pipeline pl, CancellationToken cancel =
default)
289 return await pl.GetExec(Ctx).Result<R>(queryOptions:
null, cancel: cancel);
291 catch (AggregateException ex)
293 throw TranslateException(ex.InnerExceptions.First());
297 private QuerySource<R> Chain<R>(
303 LambdaExpression? proj =
null) =>
304 new QuerySource<R>(Ctx, CopyPipeline(mode, q, ser, ety, enull, proj));
306 private Pipeline CopyPipeline(
312 LambdaExpression? proj =
null)
314 if (ser is not
null) Debug.Assert(ety is not
null);
316 var mode0 = mode ?? Pipeline.Mode;
317 var q0 = q ?? Pipeline.Query;
320 var (ety0, enull0, ser0, proj0) = ety is not
null ?
321 (ety, enull, ser, proj) :
323 Pipeline.ElemNullable,
324 Pipeline.ElemSerializer,
325 proj ?? Pipeline.ProjectExpr);
327 return new Pipeline(mode0, q0, ety0, enull0, ser0, proj0);
334 QH.Expr(
@"({ let s = (").Concat(setq).Concat(
@")
335 if (s.isEmpty()) abort(['empty'])
341 let s = (").Concat(setq).Concat(
@")
342 let s = if (s isa Set) s.toArray() else s
344 if (s.length > 1) abort(['not single'])
351 private Exception TranslateException(Exception ex) =>
357 "empty" =>
new InvalidOperationException(
"Empty set"),
358 "not single" =>
new InvalidOperationException(
"Set contains more than one element"),
364 private Query MaybeWhereCall(
Query callee, Expression? predicate, [CallerMemberName]
string callerName =
"") =>
365 predicate is
null ? callee : WhereCall(callee, predicate, callerName);
367 private Query MaybeMap(
Query setq, Expression? selector) =>
368 selector is
null ? setq :
QH.MethodCall(setq,
"map", SubQuery(selector));
370 private Query SubQuery(Expression expr) =>
371 new SubQuerySwitch(Ctx.LookupTable).Apply(expr);
373 private Query WhereCall(
Query callee, Expression predicate, [CallerMemberName]
string callerName =
"")
375 RequireQueryMode(callerName);
376 return QH.MethodCall(callee,
"where", SubQuery(predicate));
379 private Pipeline SelectCall(
Query callee, Expression proj, [CallerMemberName]
string callerName =
"")
381 var lambda = Expressions.UnwrapLambda(proj);
382 Debug.Assert(lambda is not
null, $
"lambda is {proj.NodeType}");
383 Debug.Assert(lambda.Parameters.Count() == 1);
388 Debug.Assert(Pipeline.ProjectExpr is not
null);
389 var prev = Pipeline.ProjectExpr;
390 var pbody = Expression.Invoke(lambda,
new Expression[] { prev.Body });
391 var plambda = Expression.Lambda(pbody, prev.Parameters);
393 return CopyPipeline(proj: plambda);
398 var lparam = lambda.Parameters.First()!;
399 var analysis =
new ProjectionAnalysisVisitor(MappingCtx, lparam);
400 analysis.Visit(lambda.Body);
404 if (lambda.Body is MemberExpression mexpr && mexpr.Expression == lparam)
406 Debug.Assert(!analysis.Escapes);
407 var info = MappingCtx.
GetInfo(lparam.Type);
408 var access = analysis.Accesses.First();
409 var field = Lookup.FieldLookup(access, lparam);
410 Debug.Assert(field is not
null);
413 q:
QH.MethodCall(callee,
"map",
QH.Expr($
".{field.Name}")),
414 ser: field.Serializer,
418 if (analysis.Escapes)
420 return CopyPipeline(mode:
PipelineMode.Project, proj: lambda);
424 var accesses = analysis.Accesses.OrderBy(f => f.Name).ToArray();
425 var fields = accesses.Select(a => Lookup.FieldLookup(a, lparam)!);
428 var accs = fields.Select(f =>
QH.Expr($
"x.{f.Name}"));
429 var pquery =
QH.Expr(
"x => ").Concat(
QH.Array(accs));
432 var deser =
new ProjectionDeserializer(fields.Select(f => f.Serializer));
433 var ety = typeof(
object?[]);
436 var pparam = Expression.Parameter(typeof(
object?[]),
"x");
437 var rewriter =
new ProjectionRewriteVisitor(lparam, accesses, pparam);
438 var pbody = rewriter.Visit(lambda.Body);
439 var plambda = Expression.Lambda(pbody, pparam);
442 q:
QH.MethodCall(callee,
"map", pquery),
Fauna.Linq.IntermediateQueryHelpers QH
Represents an exception that occurs when the FQL abort function is called. This exception captures th...
object? GetData()
Retrieves the deserialized data associated with the abort operation as an object.
Task< int > CountAsync(CancellationToken cancel=default)
Task< T?> FirstOrDefaultAsync(Expression< Func< T, bool > > predicate, CancellationToken cancel=default)
double Average(Expression< Func< T, double > > selector)
Task< bool > AllAsync(Expression< Func< T, bool > > predicate, CancellationToken cancel=default)
Task< long > SumAsync(Expression< Func< T, long > > selector, CancellationToken cancel=default)
Task< T > SingleOrDefaultAsync(Expression< Func< T, bool > > predicate, CancellationToken cancel=default)
Task< T > FirstAsync(CancellationToken cancel=default)
Task< T > FirstAsync(Expression< Func< T, bool > > predicate, CancellationToken cancel=default)
double Sum(Expression< Func< T, double > > selector)
T SingleOrDefault(Expression< Func< T, bool > > predicate)
Task< T > MinAsync(CancellationToken cancel=default)
Task< double > AverageAsync(Expression< Func< T, double > > selector, CancellationToken cancel=default)
bool Any(Expression< Func< T, bool > > predicate)
Task< T?> FirstOrDefaultAsync(CancellationToken cancel=default)
Task< int > SumAsync(Expression< Func< T, int > > selector, CancellationToken cancel=default)
Task< int > CountAsync(Expression< Func< T, bool > > predicate, CancellationToken cancel=default)
IQuerySource< T > OrderByDescending< K >(Expression< Func< T, K > > keySelector)
R Min< R >(Expression< Func< T, R > > selector)
Task< T > LastAsync(Expression< Func< T, bool > > predicate, CancellationToken cancel=default)
T? FirstOrDefault(Expression< Func< T, bool > > predicate)
T First(Expression< Func< T, bool > > predicate)
Task< T > LastAsync(CancellationToken cancel=default)
IQuerySource< T > Reverse()
Task< R > MinAsync< R >(Expression< Func< T, R > > selector, CancellationToken cancel=default)
IQuerySource< T > Skip(int count)
Task< T > SingleOrDefaultAsync(CancellationToken cancel=default)
IQuerySource< T > Where(Expression< Func< T, bool > > predicate)
IQuerySource< T > OrderDescending()
bool All(Expression< Func< T, bool > > predicate)
Task< R > MaxAsync< R >(Expression< Func< T, R > > selector, CancellationToken cancel=default)
IQuerySource< T > Order()
long LongCount(Expression< Func< T, bool > > predicate)
Task< T?> LastOrDefaultAsync(Expression< Func< T, bool > > predicate, CancellationToken cancel=default)
int Sum(Expression< Func< T, int > > selector)
IQuerySource< T > Take(int count)
Task< T > SingleAsync(CancellationToken cancel=default)
T Single(Expression< Func< T, bool > > predicate)
IQuerySource< T > Distinct()
Task< long > LongCountAsync(CancellationToken cancel=default)
Task< double > SumAsync(Expression< Func< T, double > > selector, CancellationToken cancel=default)
Task< T?> LastOrDefaultAsync(CancellationToken cancel=default)
Task< T > SingleAsync(Expression< Func< T, bool > > predicate, CancellationToken cancel=default)
T? LastOrDefault(Expression< Func< T, bool > > predicate)
Task< bool > AnyAsync(Expression< Func< T, bool > > predicate, CancellationToken cancel=default)
T Last(Expression< Func< T, bool > > predicate)
IQuerySource< T > OrderBy< K >(Expression< Func< T, K > > keySelector)
Task< T > MaxAsync(CancellationToken cancel=default)
Task< long > LongCountAsync(Expression< Func< T, bool > > predicate, CancellationToken cancel=default)
long Sum(Expression< Func< T, long > > selector)
IQuerySource< R > Select< R >(Expression< Func< T, R > > selector)
Task< bool > AnyAsync(CancellationToken cancel=default)
R Max< R >(Expression< Func< T, R > > selector)
int Count(Expression< Func< T, bool > > predicate)
A class representing the mapping context to be used during serialization and deserialization.
MappingInfo GetInfo(Type ty, string? colName=null)
Gets the MappingInfo for a given Type.
Represents the abstract base class for constructing FQL queries.