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

View File

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

View File

@ -19,8 +19,8 @@ void main() {
tearDown(() => db.close()); tearDown(() => db.close());
Future<T> eval<T>(Expression<T> expr) { Future<T> eval<T>(Expression<T> expr, {TableInfo? onTable}) {
final query = db.selectOnly(db.users)..addColumns([expr]); final query = db.selectOnly(onTable ?? db.users)..addColumns([expr]);
return query.getSingle().then((row) => row.read(expr)); return query.getSingle().then((row) => row.read(expr));
} }
@ -73,6 +73,26 @@ void main() {
completion(DateTime(2021, 5, 10, 12))); 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', () { group('text', () {
test('contains', () { test('contains', () {
const stringLiteral = Constant('Some sql string literal'); 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 - Fix unecessary errors around `fts5` tables
- Merge all moor-specific nodes into a single `visitMoorSpecific` visitor method - Merge all moor-specific nodes into a single `visitMoorSpecific` visitor method