5using System.Diagnostics;
6using System.Runtime.CompilerServices;
7using System.Linq.Expressions;
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 T
Single() => Execute<T>(SingleImpl(
null));
200 public Task<T>
SingleAsync(CancellationToken cancel =
default) => ExecuteAsync<T>(SingleImpl(
null), cancel);
201 public T
Single(Expression<Func<T, bool>> predicate) => Execute<T>(SingleImpl(predicate));
202 public Task<T>
SingleAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
203 ExecuteAsync<T>(SingleImpl(predicate), cancel);
204 private Pipeline SingleImpl(Expression<Func<T, bool>>? predicate) =>
207 q:
QH.MethodCall(AbortIfEmpty(Singularize(MaybeWhereCall(
Query, predicate))),
"first"));
210 public Task<T>
SingleOrDefaultAsync(CancellationToken cancel =
default) => ExecuteAsync<T>(SingleOrDefaultImpl(
null), cancel);
211 public T
SingleOrDefault(Expression<Func<T, bool>> predicate) => Execute<T>(SingleOrDefaultImpl(predicate));
212 public Task<T>
SingleOrDefaultAsync(Expression<Func<T, bool>> predicate, CancellationToken cancel =
default) =>
213 ExecuteAsync<T>(SingleOrDefaultImpl(predicate), cancel);
214 private Pipeline SingleOrDefaultImpl(Expression<Func<T, bool>>? predicate) =>
217 q:
QH.MethodCall(Singularize(MaybeWhereCall(
Query, predicate)),
"first"),
221 private static readonly
Query _sumReducer =
QH.Expr(
"(a, b) => a + b");
223 public int Sum(Expression<Func<T, int>> selector) => Execute<int>(SumImpl<int>(selector));
224 public Task<int>
SumAsync(Expression<Func<T, int>> selector, CancellationToken cancel =
default) =>
225 ExecuteAsync<int>(SumImpl<int>(selector), cancel);
226 public long Sum(Expression<Func<T, long>> selector) => Execute<long>(SumImpl<long>(selector));
227 public Task<long>
SumAsync(Expression<Func<T, long>> selector, CancellationToken cancel =
default) =>
228 ExecuteAsync<long>(SumImpl<long>(selector), cancel);
229 public double Sum(Expression<Func<T, double>> selector) => Execute<double>(SumImpl<double>(selector));
230 public Task<double>
SumAsync(Expression<Func<T, double>> selector, CancellationToken cancel =
default) =>
231 ExecuteAsync<double>(SumImpl<double>(selector), cancel);
232 private Pipeline SumImpl<R>(Expression<Func<T, R>> selector)
234 RequireQueryMode(
"Sum");
235 var seed = (typeof(R) == typeof(
int) || typeof(R) == typeof(
long)) ?
238 var mapped =
QH.MethodCall(
Query,
"map", SubQuery(selector));
241 q:
QH.MethodCall(mapped,
"fold", seed, _sumReducer),
247 private void RequireQueryMode([CallerMemberName]
string callerName =
"")
253 $
"Query is not pure: Earlier `Select` could not be translated to pure FQL.");
257 private R Execute<R>(Pipeline pl)
261 var res = ExecuteAsync<R>(pl);
265 catch (AggregateException ex)
267 throw TranslateException(ex.InnerExceptions.First());
271 private async Task<R> ExecuteAsync<R>(Pipeline pl, CancellationToken cancel =
default)
275 return await pl.GetExec(Ctx).Result<R>(queryOptions:
null, cancel: cancel);
277 catch (AggregateException ex)
279 throw TranslateException(ex.InnerExceptions.First());
283 private QuerySource<R> Chain<R>(
289 LambdaExpression? proj =
null) =>
290 new QuerySource<R>(Ctx, CopyPipeline(mode, q, deser, ety, enull, proj));
292 private Pipeline CopyPipeline(
298 LambdaExpression? proj =
null)
300 if (deser is not
null) Debug.Assert(ety is not
null);
302 var mode0 = mode ?? Pipeline.Mode;
303 var q0 = q ?? Pipeline.Query;
306 var (ety0, enull0, deser0, proj0) = ety is not
null ?
307 (ety, enull, deser, proj) :
309 Pipeline.ElemNullable,
310 Pipeline.ElemDeserializer,
311 proj ?? Pipeline.ProjectExpr);
313 return new Pipeline(mode0, q0, ety0, enull0, deser0, proj0);
320 QH.Expr(
@"({ let s = (").Concat(setq).Concat(
@")
321 if (s.isEmpty()) abort(['empty'])
327 let s = (").Concat(setq).Concat(
@").take(2).toArray()
328 if (s.length > 1) abort(['not single'])
332 private Exception TranslateException(Exception ex) =>
338 "empty" =>
new InvalidOperationException(
"Empty set"),
339 "not single" =>
new InvalidOperationException(
"Set contains more than one element"),
345 private Query MaybeWhereCall(
Query callee, Expression? predicate, [CallerMemberName]
string callerName =
"") =>
346 predicate is
null ? callee : WhereCall(callee, predicate, callerName);
348 private Query MaybeMap(
Query setq, Expression? selector) =>
349 selector is
null ? setq :
QH.MethodCall(setq,
"map", SubQuery(selector));
351 private Query SubQuery(Expression expr) =>
352 new SubQuerySwitch(Ctx.LookupTable).Apply(expr);
354 private Query WhereCall(
Query callee, Expression predicate, [CallerMemberName]
string callerName =
"")
356 RequireQueryMode(callerName);
357 return QH.MethodCall(callee,
"where", SubQuery(predicate));
360 private Pipeline SelectCall(
Query callee, Expression proj, [CallerMemberName]
string callerName =
"")
362 var lambda = Expressions.UnwrapLambda(proj);
363 Debug.Assert(lambda is not
null, $
"lambda is {proj.NodeType}");
364 Debug.Assert(lambda.Parameters.Count() == 1);
369 Debug.Assert(Pipeline.ProjectExpr is not
null);
370 var prev = Pipeline.ProjectExpr;
371 var pbody = Expression.Invoke(lambda,
new Expression[] { prev.Body });
372 var plambda = Expression.Lambda(pbody, prev.Parameters);
374 return CopyPipeline(proj: plambda);
379 var lparam = lambda.Parameters.First()!;
380 var analysis =
new ProjectionAnalysisVisitor(MappingCtx, lparam);
381 analysis.Visit(lambda.Body);
385 if (lambda.Body is MemberExpression mexpr && mexpr.Expression == lparam)
387 Debug.Assert(!analysis.Escapes);
388 var info = MappingCtx.
GetInfo(lparam.Type);
389 var access = analysis.Accesses.First();
390 var field = Lookup.FieldLookup(access, lparam);
391 Debug.Assert(field is not
null);
394 q:
QH.MethodCall(callee,
"map",
QH.Expr($
".{field.Name}")),
395 deser: field.Deserializer,
399 if (analysis.Escapes)
405 var accesses = analysis.Accesses.OrderBy(f => f.Name).ToArray();
406 var fields = accesses.Select(a => Lookup.FieldLookup(a, lparam)!);
409 var accs = fields.Select(f =>
QH.Expr($
"x.{f.Name}"));
410 var pquery =
QH.Expr(
"x => ").Concat(
QH.Array(accs));
413 var deser =
new ProjectionDeserializer(fields.Select(f => f.Deserializer));
414 var ety = typeof(
object?[]);
417 var pparam = Expression.Parameter(typeof(
object?[]),
"x");
418 var rewriter =
new ProjectionRewriteVisitor(lparam, accesses, pparam);
419 var pbody = rewriter.Visit(lambda.Body);
420 var plambda = Expression.Lambda(pbody, pparam);
423 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)
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)
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)
MappingInfo GetInfo(Type ty)
Represents the abstract base class for constructing FQL queries.