diff --git a/drift/lib/src/runtime/manager/filter.dart b/drift/lib/src/runtime/manager/filter.dart index 71b9ba74..eaf9181c 100644 --- a/drift/lib/src/runtime/manager/filter.dart +++ b/drift/lib/src/runtime/manager/filter.dart @@ -38,18 +38,78 @@ class ColumnFilters { /// Shortcut for [equals] ComposableFilter call(T value) => ComposableFilter(column.equals(value)); +} - /// Nested column for filtering on the count of a column - ColumnFilters get count => ColumnFilters(column.count()); +enum _StringFilterTypes { contains, startsWith, endsWith } + +/// Built in filters for int/double columns +extension StringFilters on ColumnFilters { + /// This function helps handle case insensitivity in like expressions + /// This helps handle all the possible scenarios: + /// 1. If a user hasn't set the database to be case sensitive in like expressions + /// Then we are ok with having the query be performed on upper case values + /// 2. If a user has set the database to be case sensitive in like expressions + /// We still can perform a case insensitive search by default. We have all filters + /// use `{bool caseInsensitive = true}` which will perform a case insensitive search + /// 3. If a user has set the database to be case sensitive in like expressions and wan't + /// to perform a case sensitive search, they can pass `caseInsensitive = false` manually + /// + /// We are using the default of {bool caseInsensitive = true}, so that users who haven't set + /// the database to be case sensitive wont be confues why their like expressions are case insensitive + Expression _buildExpression( + _StringFilterTypes type, String value, bool caseInsensitive) { + final Expression column; + if (caseInsensitive) { + value = value.toUpperCase(); + column = this.column.upper(); + } else { + column = this.column; + } + switch (type) { + case _StringFilterTypes.contains: + return column.like('%$value%'); + case _StringFilterTypes.startsWith: + return column.like('$value%'); + case _StringFilterTypes.endsWith: + return column.like('%$value'); + } + } + + /// Create a filter to check if the this text column contains a substring + /// {@template drift.manager.likeCaseSensitivity} + /// Sqlite performs a case insensitive text search by default + /// + /// If you have set the database to use case sensitive in like expressions, you can + /// pass [caseInsensitive] as false to perform a case sensitive search + /// See https://www.sqlitetutorial.net/sqlite-like/ for more information + /// {@endtemplate} + ComposableFilter contains(T value, {bool caseInsensitive = true}) { + return ComposableFilter( + _buildExpression(_StringFilterTypes.contains, value, caseInsensitive)); + } + + /// Create a filter to check if the this text column starts with a substring + /// {@macro drift.manager.likeCaseSensitivity} + ComposableFilter startsWith(T value, {bool caseInsensitive = true}) { + return ComposableFilter(_buildExpression( + _StringFilterTypes.startsWith, value, caseInsensitive)); + } + + /// Create a filter to check if the this text column ends with a substring + /// {@macro drift.manager.likeCaseSensitivity} + ComposableFilter endsWith(T value, {bool caseInsensitive = true}) { + return ComposableFilter( + _buildExpression(_StringFilterTypes.endsWith, value, caseInsensitive)); + } } /// Built in filters for bool columns extension BoolFilters on ColumnFilters { /// Create a filter to check if the column is bigger than a value - ComposableFilter isTrue(bool value) => ComposableFilter(column.equals(true)); + ComposableFilter isTrue() => ComposableFilter(column.equals(true)); /// Create a filter to check if the column is small than a value - ComposableFilter isFalse(bool value) => ComposableFilter(column.equals(true)); + ComposableFilter isFalse() => ComposableFilter(column.equals(false)); } /// Built in filters for int/double columns @@ -71,27 +131,16 @@ extension NumFilters on ColumnFilters { ComposableFilter(column.isSmallerOrEqualValue(value)); /// Create a filter to check if the column is between two values + /// This is done inclusively, so the column can be equal to the lower or higher value + /// E.G isBetween(1, 3) will return true for 1, 2, and 3 ComposableFilter isBetween(T lower, T higher) => ComposableFilter(column.isBetweenValues(lower, higher)); /// Create a filter to check if the column is not between two values + /// This is done inclusively, so the column will not be equal to the lower or higher value + /// E.G isNotBetween(1, 3) will return false for 1, 2, and 3 ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._invert(); - - /// Nested column for filtering on the average of a column - ColumnFilters get avg => ColumnFilters(column.avg()); - - /// Nested column for filtering on the max item of a column - ColumnFilters get max => ColumnFilters(column.max()); - - /// Nested column for filtering on the min item of a column - ColumnFilters get min => ColumnFilters(column.min()); - - /// Nested column for filtering on the sum of a column - ColumnFilters get sum => ColumnFilters(column.sum()); - - /// Nested column for filtering on the total of a column - ColumnFilters get total => ColumnFilters(column.total()); } /// Built in filters for BigInt columns @@ -113,30 +162,19 @@ extension BigIntFilters on ColumnFilters { ComposableFilter(column.isSmallerOrEqualValue(value)); /// Create a filter to check if the column is between two values + /// This is done inclusively, so the column can be equal to the lower or higher value + /// E.G isBetween(1, 3) will return true for 1, 2, and 3 ComposableFilter isBetween(T lower, T higher) => ComposableFilter(column.isBetweenValues(lower, higher)); /// Create a filter to check if the column is not between two values + /// This is done inclusively, so the column will not be equal to the lower or higher value + /// E.G isNotBetween(1, 3) will return false for 1, 2, and 3 ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._invert(); - - /// Nested column for filtering on the average of a column - ColumnFilters get avg => ColumnFilters(column.avg()); - - /// Nested column for filtering on the max item of a column - ColumnFilters get max => ColumnFilters(column.max()); - - /// Nested column for filtering on the min item of a column - ColumnFilters get min => ColumnFilters(column.min()); - - /// Nested column for filtering on the sum of a column - ColumnFilters get sum => ColumnFilters(column.sum()); - - /// Nested column for filtering on the total of a column - ColumnFilters get total => ColumnFilters(column.total()); } -/// Built in filters for String columns +/// Built in filters for DateTime columns extension DateFilters on ColumnFilters { /// Create a filter to check if the column is after a [DateTime] ComposableFilter isAfter(T value) => @@ -155,11 +193,14 @@ extension DateFilters on ColumnFilters { ComposableFilter(column.isSmallerOrEqualValue(value)); /// Create a filter to check if the column is between 2 [DateTime]s - + /// This is done inclusively, so the column can be equal to the lower or higher value + /// E.G isBetween(1, 3) will return true for 1, 2, and 3 ComposableFilter isBetween(T lower, T higher) => ComposableFilter(column.isBetweenValues(lower, higher)); /// Create a filter to check if the column is not between 2 [DateTime]s + /// This is done inclusively, so the column will not be equal to the lower or higher value + /// E.G isNotBetween(1, 3) will return false for 1, 2, and 3 ComposableFilter isNotBetween(T lower, T higher) => isBetween(lower, higher)._invert(); } @@ -185,6 +226,14 @@ class ColumnWithTypeConverterFilters { /// Shortcut for [equals] ComposableFilter call(CUSTOM value) => ComposableFilter(column.equalsValue(value)); + + /// Create a filter that checks if the column is in a list of values. + ComposableFilter isIn(Iterable values) => + ComposableFilter(column.isInValues(values)); + + /// Create a filter that checks if the column is not in a list of values. + ComposableFilter isNotIn(Iterable values) => + ComposableFilter(column.isNotInValues(values)); } /// This class is wrapper on the expression class diff --git a/drift/test/generated/todos.dart b/drift/test/generated/todos.dart index a7fa13df..0b0a6c28 100644 --- a/drift/test/generated/todos.dart +++ b/drift/test/generated/todos.dart @@ -24,6 +24,9 @@ class TodosTable extends Table with AutoIncrement { @JsonKey('target_date') DateTimeColumn get targetDate => dateTime().nullable().unique()(); + RealColumn get someFloat => real().nullable()(); + Int64Column get someInt64 => int64().nullable()(); + IntColumn get category => integer().references(Categories, #id).nullable()(); TextColumn get status => textEnum().nullable()(); diff --git a/drift/test/generated/todos.g.dart b/drift/test/generated/todos.g.dart index 2849a4c6..154b5be7 100644 --- a/drift/test/generated/todos.g.dart +++ b/drift/test/generated/todos.g.dart @@ -305,6 +305,18 @@ class $TodosTableTable extends TodosTable type: DriftSqlType.dateTime, requiredDuringInsert: false, defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE')); + static const VerificationMeta _someFloatMeta = + const VerificationMeta('someFloat'); + @override + late final GeneratedColumn someFloat = GeneratedColumn( + 'some_float', aliasedName, true, + type: DriftSqlType.double, requiredDuringInsert: false); + static const VerificationMeta _someInt64Meta = + const VerificationMeta('someInt64'); + @override + late final GeneratedColumn someInt64 = GeneratedColumn( + 'some_int64', aliasedName, true, + type: DriftSqlType.bigInt, requiredDuringInsert: false); static const VerificationMeta _categoryMeta = const VerificationMeta('category'); @override @@ -322,7 +334,7 @@ class $TodosTableTable extends TodosTable .withConverter($TodosTableTable.$converterstatusn); @override List get $columns => - [id, title, content, targetDate, category, status]; + [id, title, content, targetDate, someFloat, someInt64, category, status]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -350,6 +362,14 @@ class $TodosTableTable extends TodosTable targetDate.isAcceptableOrUnknown( data['target_date']!, _targetDateMeta)); } + if (data.containsKey('some_float')) { + context.handle(_someFloatMeta, + someFloat.isAcceptableOrUnknown(data['some_float']!, _someFloatMeta)); + } + if (data.containsKey('some_int64')) { + context.handle(_someInt64Meta, + someInt64.isAcceptableOrUnknown(data['some_int64']!, _someInt64Meta)); + } if (data.containsKey('category')) { context.handle(_categoryMeta, category.isAcceptableOrUnknown(data['category']!, _categoryMeta)); @@ -377,6 +397,10 @@ class $TodosTableTable extends TodosTable .read(DriftSqlType.string, data['${effectivePrefix}content'])!, targetDate: attachedDatabase.typeMapping .read(DriftSqlType.dateTime, data['${effectivePrefix}target_date']), + someFloat: attachedDatabase.typeMapping + .read(DriftSqlType.double, data['${effectivePrefix}some_float']), + someInt64: attachedDatabase.typeMapping + .read(DriftSqlType.bigInt, data['${effectivePrefix}some_int64']), category: attachedDatabase.typeMapping .read(DriftSqlType.int, data['${effectivePrefix}category']), status: $TodosTableTable.$converterstatusn.fromSql(attachedDatabase @@ -403,6 +427,8 @@ class TodoEntry extends DataClass implements Insertable { final String? title; final String content; final DateTime? targetDate; + final double? someFloat; + final BigInt? someInt64; final int? category; final TodoStatus? status; const TodoEntry( @@ -410,6 +436,8 @@ class TodoEntry extends DataClass implements Insertable { this.title, required this.content, this.targetDate, + this.someFloat, + this.someInt64, this.category, this.status}); @override @@ -425,6 +453,12 @@ class TodoEntry extends DataClass implements Insertable { if (!nullToAbsent || targetDate != null) { map['target_date'] = Variable(targetDate); } + if (!nullToAbsent || someFloat != null) { + map['some_float'] = Variable(someFloat); + } + if (!nullToAbsent || someInt64 != null) { + map['some_int64'] = Variable(someInt64); + } if (!nullToAbsent || category != null) { map['category'] = Variable(category); } @@ -444,6 +478,12 @@ class TodoEntry extends DataClass implements Insertable { targetDate: targetDate == null && nullToAbsent ? const Value.absent() : Value(targetDate), + someFloat: someFloat == null && nullToAbsent + ? const Value.absent() + : Value(someFloat), + someInt64: someInt64 == null && nullToAbsent + ? const Value.absent() + : Value(someInt64), category: category == null && nullToAbsent ? const Value.absent() : Value(category), @@ -461,6 +501,8 @@ class TodoEntry extends DataClass implements Insertable { title: serializer.fromJson(json['title']), content: serializer.fromJson(json['content']), targetDate: serializer.fromJson(json['target_date']), + someFloat: serializer.fromJson(json['someFloat']), + someInt64: serializer.fromJson(json['someInt64']), category: serializer.fromJson(json['category']), status: $TodosTableTable.$converterstatusn .fromJson(serializer.fromJson(json['status'])), @@ -479,6 +521,8 @@ class TodoEntry extends DataClass implements Insertable { 'title': serializer.toJson(title), 'content': serializer.toJson(content), 'target_date': serializer.toJson(targetDate), + 'someFloat': serializer.toJson(someFloat), + 'someInt64': serializer.toJson(someInt64), 'category': serializer.toJson(category), 'status': serializer .toJson($TodosTableTable.$converterstatusn.toJson(status)), @@ -490,6 +534,8 @@ class TodoEntry extends DataClass implements Insertable { Value title = const Value.absent(), String? content, Value targetDate = const Value.absent(), + Value someFloat = const Value.absent(), + Value someInt64 = const Value.absent(), Value category = const Value.absent(), Value status = const Value.absent()}) => TodoEntry( @@ -497,6 +543,8 @@ class TodoEntry extends DataClass implements Insertable { title: title.present ? title.value : this.title, content: content ?? this.content, targetDate: targetDate.present ? targetDate.value : this.targetDate, + someFloat: someFloat.present ? someFloat.value : this.someFloat, + someInt64: someInt64.present ? someInt64.value : this.someInt64, category: category.present ? category.value : this.category, status: status.present ? status.value : this.status, ); @@ -507,6 +555,8 @@ class TodoEntry extends DataClass implements Insertable { ..write('title: $title, ') ..write('content: $content, ') ..write('targetDate: $targetDate, ') + ..write('someFloat: $someFloat, ') + ..write('someInt64: $someInt64, ') ..write('category: $category, ') ..write('status: $status') ..write(')')) @@ -514,8 +564,8 @@ class TodoEntry extends DataClass implements Insertable { } @override - int get hashCode => - Object.hash(id, title, content, targetDate, category, status); + int get hashCode => Object.hash( + id, title, content, targetDate, someFloat, someInt64, category, status); @override bool operator ==(Object other) => identical(this, other) || @@ -524,6 +574,8 @@ class TodoEntry extends DataClass implements Insertable { other.title == this.title && other.content == this.content && other.targetDate == this.targetDate && + other.someFloat == this.someFloat && + other.someInt64 == this.someInt64 && other.category == this.category && other.status == this.status); } @@ -533,6 +585,8 @@ class TodosTableCompanion extends UpdateCompanion { final Value title; final Value content; final Value targetDate; + final Value someFloat; + final Value someInt64; final Value category; final Value status; const TodosTableCompanion({ @@ -540,6 +594,8 @@ class TodosTableCompanion extends UpdateCompanion { this.title = const Value.absent(), this.content = const Value.absent(), this.targetDate = const Value.absent(), + this.someFloat = const Value.absent(), + this.someInt64 = const Value.absent(), this.category = const Value.absent(), this.status = const Value.absent(), }); @@ -548,6 +604,8 @@ class TodosTableCompanion extends UpdateCompanion { this.title = const Value.absent(), required String content, this.targetDate = const Value.absent(), + this.someFloat = const Value.absent(), + this.someInt64 = const Value.absent(), this.category = const Value.absent(), this.status = const Value.absent(), }) : content = Value(content); @@ -556,6 +614,8 @@ class TodosTableCompanion extends UpdateCompanion { Expression? title, Expression? content, Expression? targetDate, + Expression? someFloat, + Expression? someInt64, Expression? category, Expression? status, }) { @@ -564,6 +624,8 @@ class TodosTableCompanion extends UpdateCompanion { if (title != null) 'title': title, if (content != null) 'content': content, if (targetDate != null) 'target_date': targetDate, + if (someFloat != null) 'some_float': someFloat, + if (someInt64 != null) 'some_int64': someInt64, if (category != null) 'category': category, if (status != null) 'status': status, }); @@ -574,6 +636,8 @@ class TodosTableCompanion extends UpdateCompanion { Value? title, Value? content, Value? targetDate, + Value? someFloat, + Value? someInt64, Value? category, Value? status}) { return TodosTableCompanion( @@ -581,6 +645,8 @@ class TodosTableCompanion extends UpdateCompanion { title: title ?? this.title, content: content ?? this.content, targetDate: targetDate ?? this.targetDate, + someFloat: someFloat ?? this.someFloat, + someInt64: someInt64 ?? this.someInt64, category: category ?? this.category, status: status ?? this.status, ); @@ -601,6 +667,12 @@ class TodosTableCompanion extends UpdateCompanion { if (targetDate.present) { map['target_date'] = Variable(targetDate.value); } + if (someFloat.present) { + map['some_float'] = Variable(someFloat.value); + } + if (someInt64.present) { + map['some_int64'] = Variable(someInt64.value); + } if (category.present) { map['category'] = Variable(category.value); } @@ -618,6 +690,8 @@ class TodosTableCompanion extends UpdateCompanion { ..write('title: $title, ') ..write('content: $content, ') ..write('targetDate: $targetDate, ') + ..write('someFloat: $someFloat, ') + ..write('someInt64: $someInt64, ') ..write('category: $category, ') ..write('status: $status') ..write(')')) @@ -1890,6 +1964,8 @@ abstract class _$TodoDb extends GeneratedDatabase { title: row.readNullable('title'), content: row.read('content'), targetDate: row.readNullable('target_date'), + someFloat: row.readNullable('some_float'), + someInt64: row.readNullable('some_int64'), category: row.readNullable('category'), status: NullAwareTypeConverter.wrapFromSql( $TodosTableTable.$converterstatus, @@ -2070,6 +2146,8 @@ class $$TodosTableTableFilterComposer ColumnFilters get title => ColumnFilters($table.title); ColumnFilters get content => ColumnFilters($table.content); ColumnFilters get targetDate => ColumnFilters($table.targetDate); + ColumnFilters get someFloat => ColumnFilters($table.someFloat); + ColumnFilters get someInt64 => ColumnFilters($table.someInt64); ColumnFilters get categoryId => ColumnFilters($table.category); ComposableFilter category( ComposableFilter Function($$CategoriesTableFilterComposer f) f) { @@ -2096,6 +2174,8 @@ class $$TodosTableTableOrderingComposer ColumnOrderings get title => ColumnOrderings($table.title); ColumnOrderings get content => ColumnOrderings($table.content); ColumnOrderings get targetDate => ColumnOrderings($table.targetDate); + ColumnOrderings get someFloat => ColumnOrderings($table.someFloat); + ColumnOrderings get someInt64 => ColumnOrderings($table.someInt64); ColumnOrderings get categoryId => ColumnOrderings($table.category); ComposableOrdering category( ComposableOrdering Function($$CategoriesTableOrderingComposer o) o) { @@ -2130,6 +2210,8 @@ typedef $$TodosTableTableInsertCompanionBuilder = TodosTableCompanion Function({ Value title, required String content, Value targetDate, + Value someFloat, + Value someInt64, Value category, Value status, }); @@ -2138,6 +2220,8 @@ typedef $$TodosTableTableUpdateCompanionBuilder = TodosTableCompanion Function({ Value title, Value content, Value targetDate, + Value someFloat, + Value someInt64, Value category, Value status, }); @@ -2164,6 +2248,8 @@ class $$TodosTableTableTableManager extends RootTableManager< Value title = const Value.absent(), Value content = const Value.absent(), Value targetDate = const Value.absent(), + Value someFloat = const Value.absent(), + Value someInt64 = const Value.absent(), Value category = const Value.absent(), Value status = const Value.absent(), }) => @@ -2172,6 +2258,8 @@ class $$TodosTableTableTableManager extends RootTableManager< title: title, content: content, targetDate: targetDate, + someFloat: someFloat, + someInt64: someInt64, category: category, status: status, ), @@ -2180,6 +2268,8 @@ class $$TodosTableTableTableManager extends RootTableManager< Value title = const Value.absent(), required String content, Value targetDate = const Value.absent(), + Value someFloat = const Value.absent(), + Value someInt64 = const Value.absent(), Value category = const Value.absent(), Value status = const Value.absent(), }) => @@ -2188,6 +2278,8 @@ class $$TodosTableTableTableManager extends RootTableManager< title: title, content: content, targetDate: targetDate, + someFloat: someFloat, + someInt64: someInt64, category: category, status: status, ))); @@ -2532,6 +2624,8 @@ class AllTodosWithCategoryResult extends CustomResultSet { final String? title; final String content; final DateTime? targetDate; + final double? someFloat; + final BigInt? someInt64; final int? category; final TodoStatus? status; final RowId catId; @@ -2542,14 +2636,16 @@ class AllTodosWithCategoryResult extends CustomResultSet { this.title, required this.content, this.targetDate, + this.someFloat, + this.someInt64, this.category, this.status, required this.catId, required this.catDesc, }) : super(row); @override - int get hashCode => Object.hash( - id, title, content, targetDate, category, status, catId, catDesc); + int get hashCode => Object.hash(id, title, content, targetDate, someFloat, + someInt64, category, status, catId, catDesc); @override bool operator ==(Object other) => identical(this, other) || @@ -2558,6 +2654,8 @@ class AllTodosWithCategoryResult extends CustomResultSet { other.title == this.title && other.content == this.content && other.targetDate == this.targetDate && + other.someFloat == this.someFloat && + other.someInt64 == this.someInt64 && other.category == this.category && other.status == this.status && other.catId == this.catId && @@ -2569,6 +2667,8 @@ class AllTodosWithCategoryResult extends CustomResultSet { ..write('title: $title, ') ..write('content: $content, ') ..write('targetDate: $targetDate, ') + ..write('someFloat: $someFloat, ') + ..write('someInt64: $someInt64, ') ..write('category: $category, ') ..write('status: $status, ') ..write('catId: $catId, ') diff --git a/drift/test/manager/manager_filter_tests.dart b/drift/test/manager/manager_filter_tests.dart index cccf1770..0a649582 100644 --- a/drift/test/manager/manager_filter_tests.dart +++ b/drift/test/manager/manager_filter_tests.dart @@ -12,5 +12,459 @@ void main() { db = TodoDb(testInMemoryDatabase()); }); + // // Create a dataset for the tests + // schoolId = await db.managers.categories.create((o) => + // o(priority: Value(CategoryPriority.high), description: "School")); + // workId = await db.managers.categories.create( + // (o) => o(priority: Value(CategoryPriority.low), description: "Work")); + // homeId = await db.managers.categories.create((o) => + // o(priority: Value(CategoryPriority.medium), description: "Home")); + // otherId = await db.managers.categories.create( + // (o) => o(priority: Value(CategoryPriority.high), description: "Other")); + + // // School + // await db.managers.todosTable.create((o) => o( + // content: "Get that math homework done", + // title: Value("Math Homework"), + // category: Value(schoolId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1,seconds: 10))))); + // await db.managers.todosTable.create((o) => o( + // content: "Finish that report", + // title: Value("Report"), + // category: Value(workId), + // status: Value(TodoStatus.workInProgress), + // targetDate: Value(DateTime.now().add(Duration(days: 2,seconds: 10))))); + // await db.managers.todosTable.create((o) => o( + // content: "Get that english homework done", + // title: Value("English Homework"), + // category: Value(schoolId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1,seconds: 15))))); + // await db.managers.todosTable.create((o) => o( + // content: "Finish that Book report", + // title: Value("Book Report"), + // category: Value(workId), + // status: Value(TodoStatus.done), + // targetDate: Value(DateTime.now().subtract(Duration(days: 2,seconds: 15))))); + + // // Work + // await db.managers.todosTable.create((o) => o( + // content: "File those reports", + // title: Value("File Reports"), + // category: Value(workId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 20))));); + // await db.managers.todosTable.create((o) => o( + // content: "Clean the office", + // title: Value("Clean Office"), + // category: Value(workId), + // status: Value(TodoStatus.workInProgress), + // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 20))));); + // await db.managers.todosTable.create((o) => o( + // content: "Nail that presentation", + // title: Value("Presentation"), + // category: Value(workId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 25))))); + // await db.managers.todosTable.create((o) => o( + // content: "Take a break", + // title: Value("Break"), + // category: Value(workId), + // status: Value(TodoStatus.done), + // targetDate: Value(DateTime.now().subtract(Duration(days: 2, seconds: 25))))); + + // // Work + // await db.managers.todosTable.create((o) => o( + // content: "Take out the trash", + // title: Value("Trash"), + // category: Value(homeId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 30))));); + // await db.managers.todosTable.create((o) => o( + // content: "Mow the lawn", + // title: Value("Lawn"), + // category: Value(homeId), + // status: Value(TodoStatus.workInProgress), + // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 30))))); + // await db.managers.todosTable.create((o) => o( + // content: "Fix the sink", + // title: Value("Sink"), + // category: Value(homeId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 35))));); + // await db.managers.todosTable.create((o) => o( + // content: "Paint the walls", + // title: Value("Paint"), + // category: Value(homeId), + // status: Value(TodoStatus.done), + // targetDate: Value(DateTime.now().subtract(Duration(days: 2, seconds: 35))))); + + // // Other + // await db.managers.todosTable.create((o) => o( + // content: "Get groceries", + // title: Value("Groceries"), + // category: Value(otherId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 40))));); + // await db.managers.todosTable.create((o) => o( + // content: "Pick up the kids", + // title: Value("Kids"), + // category: Value(otherId), + // status: Value(TodoStatus.workInProgress), + // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 40))));); + // await db.managers.todosTable.create((o) => o( + // content: "Take the dog for a walk", + // title: Value("Dog"), + // category: Value(otherId), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 45))))); + + // // Items with no category + // await db.managers.todosTable.create((o) => o( + // content: "Get Whiteboard", + // title: Value("Whiteboard"), + // status: Value(TodoStatus.open), + // targetDate: Value(DateTime.now().add(Duration(days: 1, seconds: 50))));); + // await db.managers.todosTable.create((o) => o( + // content: "Drink Water", + // title: Value("Water"), + // status: Value(TodoStatus.workInProgress), + // targetDate: Value(DateTime.now().add(Duration(days: 2, seconds: 50))))); + // }); + tearDown(() => db.close()); + + test('manager - query generic', () async { + await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(5.0), + targetDate: Value(DateTime.now().add(Duration(days: 1))))); + await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 2))))); + await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(3.0), + targetDate: Value(DateTime.now().add(Duration(days: 3))))); + + // Equals + expect( + db.managers.todosTable.filter((f) => f.someFloat.equals(5.0)).count(), + completion(1)); + expect(db.managers.todosTable.filter((f) => f.someFloat(3.0)).count(), + completion(1)); + // In + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isIn([3.0, 5.0])) + .count(), + completion(2)); + + // Not In + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isNotIn([3.0, 5.0])) + .count(), + completion(0)); + + // Null check + expect(db.managers.todosTable.filter((f) => f.someFloat.isNull()).count(), + completion(1)); + expect( + db.managers.todosTable.filter((f) => f.someFloat.isNotNull()).count(), + completion(2)); + }); + + test('manager - query number', () async { + final objId1 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(5.0), + targetDate: Value(DateTime.now().add(Duration(days: 1))))); + final objId2 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 2))))); + final objId3 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(3.0), + targetDate: Value(DateTime.now().add(Duration(days: 3))))); + + // More than + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isBiggerThan(3.0)) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isBiggerOrEqualTo(3.0)) + .count(), + completion(2)); + + // Less than + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isSmallerThan(5.0)) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isSmallerOrEqualTo(5.0)) + .count(), + completion(2)); + + // Between + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isBetween(3.0, 5.0)) + .count(), + completion(2)); + expect( + db.managers.todosTable + .filter((f) => f.someFloat.isNotBetween(3.0, 5.0)) + .count(), + completion(0)); + }); + + test('manager - query string', () async { + await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + )); + await db.managers.todosTable.create((o) => o( + content: "That homework Done", + )); + await db.managers.todosTable.create((o) => o( + content: "that MATH homework", + status: Value(TodoStatus.open), + )); + + // StartsWith + expect( + db.managers.todosTable + .filter((f) => f.content.startsWith("that")) + .count(), + completion(2)); + + // EndsWith + expect( + db.managers.todosTable + .filter((f) => f.content.endsWith("done")) + .count(), + completion(2)); + + // Contains + expect( + db.managers.todosTable + .filter((f) => f.content.contains("math")) + .count(), + completion(2)); + + // Make the database case sensitive + await db.customStatement('PRAGMA case_sensitive_like = ON'); + + // StartsWith + expect( + db.managers.todosTable + .filter((f) => f.content.startsWith("that", caseInsensitive: false)) + .count(), + completion(1)); + + // EndsWith + expect( + db.managers.todosTable + .filter((f) => f.content.endsWith("done", caseInsensitive: false)) + .count(), + completion(1)); + + // Contains + expect( + db.managers.todosTable + .filter((f) => f.content.contains("math", caseInsensitive: false)) + .count(), + completion(1)); + }); + + test('manager - query int64', () async { + final objId1 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someInt64: Value(BigInt.from(5.0)), + targetDate: Value(DateTime.now().add(Duration(days: 1))))); + final objId2 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + targetDate: Value(DateTime.now().add(Duration(days: 2))))); + final objId3 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someInt64: Value(BigInt.from(3.0)), + targetDate: Value(DateTime.now().add(Duration(days: 3))))); + + // More than + expect( + db.managers.todosTable + .filter((f) => f.someInt64.isBiggerThan(BigInt.from(3.0))) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.someInt64.isBiggerOrEqualTo(BigInt.from(3.0))) + .count(), + completion(2)); + + // Less than + expect( + db.managers.todosTable + .filter((f) => f.someInt64.isSmallerThan(BigInt.from(5.0))) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.someInt64.isSmallerOrEqualTo(BigInt.from(5.0))) + .count(), + completion(2)); + + // Between + expect( + db.managers.todosTable + .filter((f) => + f.someInt64.isBetween(BigInt.from(3.0), BigInt.from(5.0))) + .count(), + completion(2)); + expect( + db.managers.todosTable + .filter((f) => + f.someInt64.isNotBetween(BigInt.from(3.0), BigInt.from(5.0))) + .count(), + completion(0)); + }); + + test('manager - query bool', () async { + final objId1 = await db.managers.users.create((o) => o( + name: "John Doe", + profilePicture: Uint8List(0), + isAwesome: Value(true), + creationTime: Value(DateTime.now().add(Duration(days: 1))))); + final objId2 = await db.managers.users.create((o) => o( + name: "Jane Doe1", + profilePicture: Uint8List(0), + isAwesome: Value(false), + creationTime: Value(DateTime.now().add(Duration(days: 2))))); + final objId3 = await db.managers.users.create((o) => o( + name: "Jane Doe2", + profilePicture: Uint8List(0), + isAwesome: Value(true), + creationTime: Value(DateTime.now().add(Duration(days: 2))))); + + // False + expect(db.managers.users.filter((f) => f.isAwesome.isFalse()).count(), + completion(1)); + // True + expect(db.managers.users.filter((f) => f.isAwesome.isTrue()).count(), + completion(2)); + }); + + test('manager - query datetime', () async { + final day1 = DateTime.now().add(Duration(days: 1)); + final day2 = DateTime.now().add(Duration(days: 2)); + final day3 = DateTime.now().add(Duration(days: 3)); + final objId1 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(5.0), + targetDate: Value(day1))); + final objId2 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + targetDate: Value(day2))); + final objId3 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open), + someFloat: Value(3.0), + targetDate: Value(day3))); + + // More than + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isAfter(day2)) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isAfterOrOn(day2)) + .count(), + completion(2)); + + // Less than + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isBefore(day2)) + .count(), + completion(1)); + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isBeforeOrOn(day2)) + .count(), + completion(2)); + + // Between + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isBetween(day1, day2)) + .count(), + completion(2)); + expect( + db.managers.todosTable + .filter((f) => f.targetDate.isNotBetween(day1, day2)) + .count(), + completion(1)); + }); + + test('manager - query custom column', () async { + final objId1 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open))); + final objId2 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.open))); + final objId3 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.workInProgress))); + final objId4 = await db.managers.todosTable.create((o) => o( + content: "Get that math homework done", + status: Value(TodoStatus.done))); + + // Equals + expect( + db.managers.todosTable + .filter((f) => f.status.equals(TodoStatus.open)) + .count(), + completion(2)); + expect( + db.managers.todosTable.filter((f) => f.status(TodoStatus.open)).count(), + completion(2)); + + // In + expect( + db.managers.todosTable + .filter((f) => + f.status.isIn([TodoStatus.open, TodoStatus.workInProgress])) + .count(), + completion(3)); + + // Not In + expect( + db.managers.todosTable + .filter((f) => + f.status.isNotIn([TodoStatus.open, TodoStatus.workInProgress])) + .count(), + completion(1)); + }); }