Add `Expression.and` and `.or` (#2642)

This commit is contained in:
Simon Binder 2023-09-27 22:27:14 +02:00
parent 4eb44f9623
commit f8836c42ba
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
4 changed files with 91 additions and 0 deletions

View File

@ -49,6 +49,16 @@ select(animals)..where((a) => a.isMammal.not() & a.amountOfLegs.equals(4));
select(animals)..where((a) => a.isMammal | a.amountOfLegs.equals(2)); select(animals)..where((a) => a.isMammal | a.amountOfLegs.equals(2));
``` ```
If you have a list of predicates for which one or all need to match, you can use
`Expression.or` and `Expression.and`, respectively:
```dart
Expression.and([
a.isMammal,
a.amountOfLegs().equals(4),
])
```
## Arithmetic ## Arithmetic
For `int` and `double` expressions, you can use the `+`, `-`, `*` and `/` operators. To For `int` and `double` expressions, you can use the `+`, `-`, `*` and `/` operators. To

View File

@ -1,3 +1,8 @@
## 2.13.0-dev
- Add `Expression.and` and `Expression.or` to create disjunctions and conjunctions
of sub-predicates.
## 2.12.1 ## 2.12.1
- Fix `readWithConverter` throwing an exception for null values in non- - Fix `readWithConverter` throwing an exception for null values in non-

View File

@ -271,6 +271,38 @@ abstract class Expression<D extends Object> implements FunctionParameter {
/// The supported [DriftSqlType] backing this expression. /// The supported [DriftSqlType] backing this expression.
DriftSqlType<D> get driftSqlType => DriftSqlType.forType(); DriftSqlType<D> get driftSqlType => DriftSqlType.forType();
/// Chains all [predicates] together into a single expression that will
/// evaluate to `true` iff any of the [predicates] evaluates to `true`.
///
/// The [ifEmpty] value will be used when no predicates have been passed to
/// [or]. By default, `false` is returned.
static Expression<bool> or(
Iterable<Expression<bool>> predicates, {
Expression<bool> ifEmpty = const Constant(false),
}) {
if (predicates.isEmpty) {
return ifEmpty;
}
return predicates.reduce((value, element) => value | element);
}
/// Chains all [predicates] together into a single expression that will
/// evaluate to `true` iff all of the [predicates] evaluates to `true`.
///
/// The [ifEmpty] value will be used when no predicates have been passed to
/// [or]. By default, `true` is returned.
static Expression<bool> and(
Iterable<Expression<bool>> predicates, {
Expression<bool> ifEmpty = const Constant(true),
}) {
if (predicates.isEmpty) {
return ifEmpty;
}
return predicates.reduce((value, element) => value & element);
}
} }
/// Used to order the precedence of sql expressions so that we can avoid /// Used to order the precedence of sql expressions so that we can avoid

View File

@ -131,4 +131,48 @@ void main() {
expect(a.isExp(b), generates('a IS b')); expect(a.isExp(b), generates('a IS b'));
expect(b.isNotExp(a), generates('b IS NOT a')); expect(b.isNotExp(a), generates('b IS NOT a'));
}); });
test('Expression.and', () {
expect(
Expression.and([
for (var i = 0; i < 5; i++)
CustomExpression<bool>('e$i', precedence: Precedence.primary)
]),
generates('e0 AND e1 AND e2 AND e3 AND e4'),
);
expect(Expression.and(const []), generates('1'));
expect(Expression.and(const [], ifEmpty: const Constant(false)),
generates('0'));
});
test('Expression.or', () {
expect(
Expression.or([
for (var i = 0; i < 5; i++)
CustomExpression<bool>('e$i', precedence: Precedence.primary)
]),
generates('e0 OR e1 OR e2 OR e3 OR e4'),
);
expect(Expression.or(const []), generates('0'));
expect(
Expression.or(const [], ifEmpty: const Constant(true)), generates('1'));
});
test('and and or', () {
expect(
Expression.and([
Expression.or([
const CustomExpression<bool>('a', precedence: Precedence.primary),
const CustomExpression<bool>('b', precedence: Precedence.primary),
]),
Expression.and([
const CustomExpression<bool>('c', precedence: Precedence.primary),
const CustomExpression<bool>('d', precedence: Precedence.primary),
]),
]),
generates('(a OR b) AND c AND d'),
);
});
} }