Support filters for all aggregate expressions

This commit is contained in:
Simon Binder 2021-09-16 21:23:07 +02:00
parent f42357c1d9
commit 9fffebbae6
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
4 changed files with 56 additions and 16 deletions

View File

@ -3,9 +3,11 @@ part of '../query_builder.dart';
/// Returns the amount of rows in the current group matching the optional
/// [filter].
///
/// To only count rows matching a predicate, you can set the optional [filter].
/// Note that [filter] is only available from sqlite 3.30, released on
/// {@templace moor_aggregate_filter}
/// To only consider rows matching a predicate, you can set the optional
/// [filter]. Note that [filter] is only available from sqlite 3.30, released on
/// 2019-10-04. Most devices will use an older sqlite version.
/// {@endtemplate}
///
/// This is equivalent to the `COUNT(*) FILTER (WHERE filter)` sql function. The
/// filter will be omitted if null.
@ -21,9 +23,8 @@ extension BaseAggregate<DT> on Expression<DT> {
/// For `COUNT(*)`, which would count all rows, see [countAll].
///
/// If [distinct] is set (defaults to false), duplicate values will not be
/// counted twice. An optional [filter] can be used to only include values
/// matching the filter. Note that [filter] is only available from sqlite
/// 3.30 and most devices will use an older sqlite version.
/// counted twice.
/// {@macro moor_aggregate_filter}
Expression<int> count({bool? distinct, Expression<bool?>? filter}) {
return _AggregateExpression('COUNT', this,
filter: filter, distinct: distinct);
@ -51,17 +52,24 @@ extension BaseAggregate<DT> on Expression<DT> {
/// Provides aggregate functions that are available for numeric expressions.
extension ArithmeticAggregates<DT extends num> on Expression<DT?> {
/// Return the average of all non-null values in this group.
Expression<double?> avg() => _AggregateExpression('AVG', this);
///
/// {@macro moor_aggregate_filter}
Expression<double?> avg({Expression<bool?>? filter}) =>
_AggregateExpression('AVG', this, filter: filter);
/// Return the maximum of all non-null values in this group.
///
/// If there are no non-null values in the group, returns null.
Expression<DT?> max() => _AggregateExpression('MAX', this);
/// {@macro moor_aggregate_filter}
Expression<DT?> max({Expression<bool?>? filter}) =>
_AggregateExpression('MAX', this, filter: filter);
/// Return the minimum of all non-null values in this group.
///
/// If there are no non-null values in the group, returns null.
Expression<DT?> min() => _AggregateExpression('MIN', this);
/// {@macro moor_aggregate_filter}
Expression<DT?> min({Expression<bool?>? filter}) =>
_AggregateExpression('MIN', this, filter: filter);
/// Calculate the sum of all non-null values in the group.
///
@ -71,29 +79,39 @@ extension ArithmeticAggregates<DT extends num> on Expression<DT?> {
///
/// See also [total], which behaves similarly but returns a floating point
/// value and doesn't throw an overflow exception.
Expression<DT?> sum() => _AggregateExpression('SUM', this);
/// {@macro moor_aggregate_filter}
Expression<DT?> sum({Expression<bool?>? filter}) =>
_AggregateExpression('SUM', this, filter: filter);
/// Calculate the sum of all non-null values in the group.
///
/// If all values in the group are null, [total] returns `0.0`. This function
/// uses floating-point values internally.
Expression<double?> total() => _AggregateExpression('TOTAL', this);
/// {@macro moor_aggregate_filter}
Expression<double?> total({Expression<bool?>? filter}) =>
_AggregateExpression('TOTAL', this, filter: filter);
}
/// Provides aggregate functions that are available on date time expressions.
extension DateTimeAggregate on Expression<DateTime?> {
/// Return the average of all non-null values in this group.
Expression<DateTime> avg() => secondsSinceEpoch.avg().roundToInt().dartCast();
/// {@macro moor_aggregate_filter}
Expression<DateTime> avg({Expression<bool?>? filter}) =>
secondsSinceEpoch.avg(filter: filter).roundToInt().dartCast();
/// Return the maximum of all non-null values in this group.
///
/// If there are no non-null values in the group, returns null.
Expression<DateTime> max() => _AggregateExpression('MAX', this);
/// {@macro moor_aggregate_filter}
Expression<DateTime> max({Expression<bool?>? filter}) =>
_AggregateExpression('MAX', this, filter: filter);
/// Return the minimum of all non-null values in this group.
///
/// If there are no non-null values in the group, returns null.
Expression<DateTime> min() => _AggregateExpression('MIN', this);
/// {@macro moor_aggregate_filter}
Expression<DateTime> min({Expression<bool?>? filter}) =>
_AggregateExpression('MIN', this, filter: filter);
}
class _AggregateExpression<D> extends Expression<D> {

View File

@ -43,6 +43,8 @@ void main() {
test('avg', () {
expect(foo.avg(), generates('AVG(foo)'));
expect(foo.avg(filter: foo.isBiggerOrEqualValue(3)),
generates('AVG(foo) FILTER (WHERE foo >= ?)', [3]));
});
test('max', () {

View File

@ -19,8 +19,8 @@ void main() {
tearDown(() => db.close());
Future<T> eval<T>(Expression<T> expr) {
final query = db.selectOnly(db.users)..addColumns([expr]);
Future<T> eval<T>(Expression<T> expr, {TableInfo? onTable}) {
final query = db.selectOnly(onTable ?? db.users)..addColumns([expr]);
return query.getSingle().then((row) => row.read(expr));
}
@ -73,6 +73,26 @@ void main() {
completion(DateTime(2021, 5, 10, 12)));
});
test('aggregate filters', () async {
await db.delete(db.users).go();
await db
.into(db.tableWithoutPK)
.insert(TableWithoutPKCompanion.insert(notReallyAnId: 3, someFloat: 7));
await db
.into(db.tableWithoutPK)
.insert(TableWithoutPKCompanion.insert(notReallyAnId: 2, someFloat: 1));
expect(
eval(
db.tableWithoutPK.someFloat
.sum(filter: db.tableWithoutPK.someFloat.isBiggerOrEqualValue(3)),
onTable: db.tableWithoutPK,
),
completion(7),
);
});
group('text', () {
test('contains', () {
const stringLiteral = Constant('Some sql string literal');

View File

@ -1,4 +1,4 @@
## 0.18.0
## 0.18.0-dev
- Fix unecessary errors around `fts5` tables
- Merge all moor-specific nodes into a single `visitMoorSpecific` visitor method