mirror of https://github.com/AMT-Cheif/drift.git
Refactor ordering - Add better Docs
This commit is contained in:
parent
1da0664c08
commit
46953c3a73
|
@ -21,7 +21,7 @@ class ComposerState<DB extends GeneratedDatabase, T extends Table>
|
|||
/// Get a random alias for a table
|
||||
String _getRandomAlias(TableInfo table) {
|
||||
var aliasName = '${table.actualTableName}__${Random().nextInt(4294967296)}';
|
||||
while (joinBuilders.aliasedNames.contains(aliasName)) {
|
||||
while (joinBuilders.map((e) => (e.aliasedName)).contains(aliasName)) {
|
||||
aliasName = '${table.actualTableName}__${Random().nextInt(4294967296)}';
|
||||
continue;
|
||||
}
|
||||
|
@ -44,10 +44,26 @@ class AliasedComposerBuilder<DB extends GeneratedDatabase, RT extends Table,
|
|||
);
|
||||
}
|
||||
|
||||
/// Base class for all query composers
|
||||
/// Base class for all composers
|
||||
///
|
||||
/// Any class that can be composed using the `&` or `|` operator is called a composable,
|
||||
/// and must implement the [HasJoinBuilders] interface. [ComposableFilter] and [ComposableOrdering] are examples of composable classes.
|
||||
///
|
||||
/// The [Composer] class is a top level manager for this operation.
|
||||
/// ```dart
|
||||
/// filter((f) => f.id.equals(1) & f.name.equals('Bob'));
|
||||
/// ```
|
||||
/// `f` in this example is a [Composer] object, and `f.id.equals(1) & f.name.equals('Bob')` is a [ComposableFilter] object.
|
||||
///
|
||||
/// The [Composer] class is responsible for creating joins between tables, and passing them down to the composable classes.
|
||||
/// This is done to ensure that duplicate joins are never created.
|
||||
///
|
||||
/// The [ComposerState] that is held in this class only holds temporary state, as the final state will be held in the composable classes.
|
||||
/// E.G. In the example above, the resulting [ComposableFilter] object is returned, and the [FilterComposer] is discarded.
|
||||
///
|
||||
@internal
|
||||
sealed class Composer<DB extends GeneratedDatabase, CT extends Table> {
|
||||
/// The state of the query composer
|
||||
/// The state of the composer
|
||||
final ComposerState<DB, CT> state;
|
||||
|
||||
Composer.withAliasedTable(AliasedComposerBuilder<DB, dynamic, CT> data)
|
||||
|
@ -55,9 +71,7 @@ sealed class Composer<DB extends GeneratedDatabase, CT extends Table> {
|
|||
data.state.db, data.aliasedTable, data.state.joinBuilders);
|
||||
Composer.empty(DB db, CT table) : state = ComposerState._(db, table, {});
|
||||
|
||||
/// Helper method for creaing an aliased join
|
||||
/// and adding it to the state and Composable object
|
||||
|
||||
/// Method for create a join between two tables
|
||||
B referenced<RT extends Table, QC extends Composer<DB, RT>,
|
||||
B extends HasJoinBuilders>({
|
||||
required GeneratedColumn Function(CT) getCurrentColumn,
|
||||
|
@ -107,8 +121,6 @@ sealed class Composer<DB extends GeneratedDatabase, CT extends Table> {
|
|||
// that state doesnt have, it is also possible that the result is missing
|
||||
// the `joinBuilder` we create above.
|
||||
// We will combine both sets and set it to `state.joinBuilders` and `result.joinBuilders`
|
||||
|
||||
// Add the joins that may have been created in the filterBuilder to state
|
||||
for (var joinBuilder in result.joinBuilders.union(state.joinBuilders)) {
|
||||
state.addJoinBuilder(joinBuilder);
|
||||
result.addJoinBuilder(joinBuilder);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
part of 'manager.dart';
|
||||
|
||||
/// Defines a class that can be used to compose filters for a column
|
||||
/// Defines a class which is used to wrap a column to only expose filter functions
|
||||
class ColumnFilters<T extends Object> {
|
||||
/// This class is a wrapper on top of the generated column class
|
||||
///
|
||||
|
@ -18,111 +18,99 @@ class ColumnFilters<T extends Object> {
|
|||
/// Column that this [ColumnFilters] wraps
|
||||
GeneratedColumn<T> column;
|
||||
|
||||
// ignore: public_member_api_docs
|
||||
/// Create a filter that checks if the column is null.
|
||||
ComposableFilter isNull() => ComposableFilter.simple(column.isNull());
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter that checks if the column is not null.
|
||||
ComposableFilter isNotNull() => ComposableFilter.simple(column.isNotNull());
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter that checks if the column equals a value.
|
||||
ComposableFilter equals(T value) =>
|
||||
ComposableFilter.simple(column.equals(value));
|
||||
|
||||
/// Create a filter that checks if the column equals a value.
|
||||
ComposableFilter call(T value) =>
|
||||
ComposableFilter.simple(column.equals(value));
|
||||
}
|
||||
|
||||
/// Built in filters for int/double columns
|
||||
extension NumFilters<T extends num> on ColumnFilters<T> {
|
||||
// ignore: public_member_api_docs
|
||||
/// Create a filter to check if the column is bigger than a value
|
||||
ComposableFilter isBiggerThan(T value) =>
|
||||
ComposableFilter.simple(column.isBiggerThanValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotBiggerThan(T value) => isBiggerThan(value)._reversed();
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is small than a value
|
||||
ComposableFilter isSmallerThan(T value) =>
|
||||
ComposableFilter.simple(column.isSmallerThanValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotSmallerThan(T value) =>
|
||||
isSmallerThan(value)._reversed();
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is bigger or equal to a value
|
||||
ComposableFilter isBiggerOrEqualTo(T value) =>
|
||||
ComposableFilter.simple(column.isBiggerOrEqualValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotBiggerOrEqualTo(T value) =>
|
||||
isBiggerOrEqualTo(value)._reversed();
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is small or equal to a value
|
||||
ComposableFilter isSmallerOrEqualTo(T value) =>
|
||||
ComposableFilter.simple(column.isSmallerOrEqualValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotSmallerOrEqualTo(T value) =>
|
||||
isSmallerOrEqualTo(value)._reversed();
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is between two values
|
||||
ComposableFilter isBetween(T lower, T higher) =>
|
||||
ComposableFilter.simple(column.isBetweenValues(lower, higher));
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is not between two values
|
||||
ComposableFilter isNotBetween(T lower, T higher) =>
|
||||
isBetween(lower, higher)._reversed();
|
||||
}
|
||||
|
||||
/// Built in filters for BigInt columns
|
||||
extension BigIntFilters<T extends BigInt> on ColumnFilters<T> {
|
||||
// ignore: public_member_api_docs
|
||||
/// Create a filter to check if the column is bigger than a value
|
||||
ComposableFilter isBiggerThan(T value) =>
|
||||
ComposableFilter.simple(column.isBiggerThanValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotBiggerThan(T value) => isBiggerThan(value)._reversed();
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is small than a value
|
||||
ComposableFilter isSmallerThan(T value) =>
|
||||
ComposableFilter.simple(column.isSmallerThanValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotSmallerThan(T value) =>
|
||||
isSmallerThan(value)._reversed();
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is bigger or equal to a value
|
||||
ComposableFilter isBiggerOrEqualTo(T value) =>
|
||||
ComposableFilter.simple(column.isBiggerOrEqualValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotBiggerOrEqualTo(T value) =>
|
||||
isBiggerOrEqualTo(value)._reversed();
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is small or equal to a value
|
||||
ComposableFilter isSmallerOrEqualTo(T value) =>
|
||||
ComposableFilter.simple(column.isSmallerOrEqualValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotSmallerOrEqualTo(T value) =>
|
||||
isSmallerOrEqualTo(value)._reversed();
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is between two values
|
||||
ComposableFilter isBetween(T lower, T higher) =>
|
||||
ComposableFilter.simple(column.isBetweenValues(lower, higher));
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is not between two values
|
||||
ComposableFilter isNotBetween(T lower, T higher) =>
|
||||
isBetween(lower, higher)._reversed();
|
||||
}
|
||||
|
||||
/// Built in filters for String columns
|
||||
extension DateFilters<T extends DateTime> on ColumnFilters<T> {
|
||||
// ignore: public_member_api_docs
|
||||
/// Create a filter to check if the column is after a [DateTime]
|
||||
ComposableFilter isAfter(T value) =>
|
||||
ComposableFilter.simple(column.isBiggerThanValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotAfter(T value) => isAfter(value)._reversed();
|
||||
|
||||
// ignore: public_member_api_docs
|
||||
/// Create a filter to check if the column is before a [DateTime]
|
||||
ComposableFilter isBefore(T value) =>
|
||||
ComposableFilter.simple(column.isSmallerThanValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotBefore(T value) => isBefore(value)._reversed();
|
||||
|
||||
// ignore: public_member_api_docs
|
||||
/// Create a filter to check if the column is on or after a [DateTime]
|
||||
ComposableFilter isAfterOrOn(T value) =>
|
||||
ComposableFilter.simple(column.isBiggerOrEqualValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotAfterOrOn(T value) => isAfterOrOn(value)._reversed();
|
||||
|
||||
// ignore: public_member_api_docs
|
||||
/// Create a filter to check if the column is before or on a [DateTime]
|
||||
ComposableFilter isBeforeOrOn(T value) =>
|
||||
ComposableFilter.simple(column.isSmallerOrEqualValue(value));
|
||||
// ignore: public_member_api_docs
|
||||
ComposableFilter isNotBeforeOrOn(T value) => isBeforeOrOn(value)._reversed();
|
||||
|
||||
// ignore: public_member_api_docs
|
||||
/// Create a filter to check if the column is between 2 [DateTime]s
|
||||
|
||||
ComposableFilter isBetween(T lower, T higher) =>
|
||||
ComposableFilter.simple(column.isBetweenValues(lower, higher));
|
||||
// ignore: public_member_api_docs
|
||||
|
||||
/// Create a filter to check if the column is not between 2 [DateTime]s
|
||||
ComposableFilter isNotBetween(T lower, T higher) =>
|
||||
isBetween(lower, higher)._reversed();
|
||||
}
|
||||
|
@ -186,12 +174,11 @@ class ComposableFilter implements HasJoinBuilders {
|
|||
}
|
||||
|
||||
/// The class that orchestrates the composition of filtering
|
||||
class FilterComposer<DB extends GeneratedDatabase, T extends TableInfo>
|
||||
class FilterComposer<DB extends GeneratedDatabase, T extends Table>
|
||||
extends Composer<DB, T> {
|
||||
/// Create a new filter composer from existing query state
|
||||
// FilterComposer(super.state);
|
||||
|
||||
/// Create a filter composer with an empty state
|
||||
FilterComposer.empty(super.db, super.table) : super.empty();
|
||||
|
||||
/// Create a filter composer using another composers state
|
||||
FilterComposer.withAliasedTable(super.data) : super.withAliasedTable();
|
||||
}
|
||||
|
|
|
@ -58,8 +58,3 @@ abstract interface class HasJoinBuilders {
|
|||
/// Add a join builder to this class
|
||||
void addJoinBuilder(JoinBuilder builder);
|
||||
}
|
||||
|
||||
/// Helper for getting all the aliased names of a set of join builders
|
||||
extension on Set<JoinBuilder> {
|
||||
List<String> get aliasedNames => map((e) => (e.aliasedName)).toList();
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@ part 'filter.dart';
|
|||
part 'join.dart';
|
||||
part 'ordering.dart';
|
||||
|
||||
/// Defines a class that holds the state for a table manager
|
||||
/// Defines a class that holds the state for a [BaseTableManager]
|
||||
class TableManagerState<
|
||||
DB extends GeneratedDatabase,
|
||||
T extends TableInfo,
|
||||
T extends Table,
|
||||
DT extends DataClass,
|
||||
FS extends FilterComposer<DB, T>,
|
||||
OS extends OrderingComposer<DB, T>> {
|
||||
|
@ -23,10 +23,12 @@ class TableManagerState<
|
|||
/// The expression that will be applied to the query
|
||||
final Expression<bool>? filter;
|
||||
|
||||
/// The orderings that will be applied to the query
|
||||
/// A set of [OrderingBuilder] which will be used to apply
|
||||
/// [OrderingTerm]s to the statement when it's eventually built
|
||||
final Set<OrderingBuilder> orderingBuilders;
|
||||
|
||||
/// The joins that will be applied to the query
|
||||
/// A set of [JoinBuilder] which will be used to create [Join]s
|
||||
/// that will be applied to the build statement
|
||||
final Set<JoinBuilder> joinBuilders;
|
||||
|
||||
/// Whether the query should return distinct results
|
||||
|
@ -38,15 +40,19 @@ class TableManagerState<
|
|||
/// If set, the number of rows that will be skipped
|
||||
final int? offset;
|
||||
|
||||
/// The composer for the filters
|
||||
/// The [FilterComposer] for this [TableManagerState]
|
||||
/// This class will be used to create filtering [Expression]s
|
||||
/// which will be applied to the statement when its eventually created
|
||||
final FS filteringComposer;
|
||||
|
||||
/// The composer for the orderings
|
||||
/// The [OrderingComposer] for this [TableManagerState]
|
||||
/// This class will be used to create [OrderingTerm]s
|
||||
/// which will be applied to the statement when its eventually created
|
||||
final OS orderingComposer;
|
||||
|
||||
/// Defines a class which holds the state for a table manager
|
||||
/// It contains the database instance, the table instance, and any filters/orderings that will be applied to the query
|
||||
/// This is held in a seperate class so that the state can be passed down from the root manager to the lower level managers
|
||||
/// This is held in a seperate class than the [BaseTableManager] so that the state can be passed down from the root manager to the lower level managers
|
||||
TableManagerState({
|
||||
required this.db,
|
||||
required this.table,
|
||||
|
@ -88,7 +94,8 @@ class TableManagerState<
|
|||
/// This is needed due to dart's limitations with generics
|
||||
TableInfo<T, DT> get _tableAsTableInfo => table as TableInfo<T, DT>;
|
||||
|
||||
/// Builds a joined select statement.
|
||||
/// Builds a joined select statement, should be used when joins are present
|
||||
/// Will order, filter, and limit the statement using the state
|
||||
JoinedSelectStatement _buildJoinedSelectStatement() {
|
||||
// Build the joins
|
||||
final joins = joinBuilders.map((e) => e.buildJoin()).toList();
|
||||
|
@ -112,9 +119,10 @@ class TableManagerState<
|
|||
return statement;
|
||||
}
|
||||
|
||||
/// Builds a simple select statement
|
||||
/// Builds a simple select statement, this should be used when there are no joins
|
||||
/// Will order, filter, and limit the statement using the state
|
||||
SimpleSelectStatement<T, DT> _buildSimpleSelectStatement() {
|
||||
// Create the joined statement
|
||||
// Create the statement
|
||||
final statement = db.select(_tableAsTableInfo, distinct: distinct ?? false);
|
||||
|
||||
// Apply the expression to the statement
|
||||
|
|
|
@ -1,5 +1,35 @@
|
|||
part of 'manager.dart';
|
||||
|
||||
/// Defines a class which is used to wrap a column to only expose ordering functions
|
||||
class ColumnOrderings<T extends Object> {
|
||||
/// This class is a wrapper on top of the generated column class
|
||||
///
|
||||
/// It's used to expose ordering functions for a column
|
||||
///
|
||||
/// ```dart
|
||||
/// extension on FilterComposer<DateTime>{
|
||||
/// FitlerBuilder after2000() => isAfter(DateTime(2000));
|
||||
///}
|
||||
/// ```
|
||||
ColumnOrderings(this.column);
|
||||
|
||||
/// Column that this [ColumnOrderings] wraps
|
||||
GeneratedColumn<T> column;
|
||||
|
||||
/// Sort this column in ascending order
|
||||
///
|
||||
/// 10 -> 1 | Z -> A | Dec 31 -> Jan 1
|
||||
ComposableOrdering asc() =>
|
||||
ComposableOrdering.simple({OrderingBuilder(OrderingMode.asc, column)});
|
||||
|
||||
/// Sort this column in descending order
|
||||
///
|
||||
/// 1 -> 10 | A -> Z | Jan 1 -> Dec 31
|
||||
ComposableOrdering desc() =>
|
||||
ComposableOrdering.simple({OrderingBuilder(OrderingMode.desc, column)});
|
||||
}
|
||||
|
||||
/// Defines a class which will hold the information needed to create an ordering
|
||||
class OrderingBuilder {
|
||||
/// The mode of the ordering
|
||||
final OrderingMode mode;
|
||||
|
@ -7,6 +37,7 @@ class OrderingBuilder {
|
|||
/// The column that the ordering is applied to
|
||||
final GeneratedColumn column;
|
||||
|
||||
/// Create a new ordering builder, will be used by the [TableManagerState] to create [OrderingTerm]s
|
||||
OrderingBuilder(this.mode, this.column);
|
||||
|
||||
@override
|
||||
|
@ -26,7 +57,11 @@ class OrderingBuilder {
|
|||
}
|
||||
|
||||
/// Defines a class that can be used to compose orderings for a column
|
||||
///
|
||||
/// Multiple orderings can be composed together using the `&` operator.
|
||||
/// The orderings will be executed from left to right.
|
||||
class ComposableOrdering implements HasJoinBuilders {
|
||||
/// The orderings that are being composed
|
||||
final Set<OrderingBuilder> orderingBuilders;
|
||||
@override
|
||||
final Set<JoinBuilder> joinBuilders;
|
||||
|
@ -35,11 +70,14 @@ class ComposableOrdering implements HasJoinBuilders {
|
|||
joinBuilders.add(builder);
|
||||
}
|
||||
|
||||
/// Create a new ordering for a column
|
||||
/// Create a new [ComposableOrdering] for a column without any joins
|
||||
ComposableOrdering.simple(this.orderingBuilders) : joinBuilders = {};
|
||||
|
||||
/// Create a new [ComposableOrdering] for a column with joins
|
||||
ComposableOrdering.withJoin(this.orderingBuilders, this.joinBuilders);
|
||||
|
||||
ComposableOrdering operator |(ComposableOrdering other) {
|
||||
/// Combine two orderings with THEN
|
||||
ComposableOrdering operator &(ComposableOrdering other) {
|
||||
return ComposableOrdering.withJoin(
|
||||
orderingBuilders.union(other.orderingBuilders),
|
||||
joinBuilders.union(other.joinBuilders));
|
||||
|
@ -52,12 +90,13 @@ class ComposableOrdering implements HasJoinBuilders {
|
|||
}
|
||||
|
||||
/// The class that orchestrates the composition of orderings
|
||||
///
|
||||
///
|
||||
class OrderingComposer<DB extends GeneratedDatabase, T extends Table>
|
||||
extends Composer<DB, T> {
|
||||
/// Create a new ordering composer from existing query state
|
||||
// OrderingComposer.fromComposer(super.state);
|
||||
|
||||
/// Create an ordering composer with an empty state
|
||||
OrderingComposer.empty(super.db, super.table) : super.empty();
|
||||
|
||||
/// Create an ordering composer using another composers state
|
||||
OrderingComposer.withAliasedTable(super.data) : super.withAliasedTable();
|
||||
}
|
||||
|
|
|
@ -29,9 +29,7 @@ class ManagerWriter {
|
|||
filters.writeln(
|
||||
"ColumnFilters get $getterName => ColumnFilters(state.table.${col.nameInDart});");
|
||||
orderings.writeln(
|
||||
"ComposableOrdering get ${getterName}Asc => ComposableOrdering.simple({OrderingBuilder( OrderingMode.asc, state.table.${col.nameInDart})});");
|
||||
orderings.writeln(
|
||||
"ComposableOrdering get ${getterName}Desc => ComposableOrdering.simple({OrderingBuilder( OrderingMode.desc, state.table.${col.nameInDart})});");
|
||||
"ColumnOrderings get $getterName => ColumnOrderings(state.table.${col.nameInDart});");
|
||||
|
||||
if (col.isForeignKey) {
|
||||
final referencedCol = col.constraints
|
||||
|
|
Loading…
Reference in New Issue