From cd25324f44d1ac7fc49f9edb1b46c11d4c59c723 Mon Sep 17 00:00:00 2001 From: Timm Preetz Date: Wed, 6 Nov 2024 15:20:33 +0100 Subject: [PATCH] Add `contains` functions for `String` index columns --- CHANGELOG.md | 4 +++ example/pubspec.lock | 2 +- lib/src/index_column.dart | 15 +++++++++ lib/src/query.dart | 37 ++++++++++++++++++++++ pubspec.yaml | 2 +- test/indexed_entity_store_test.dart | 48 +++++++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 118a00a..5e80f68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.7 + +* Add `contains` functions for `String` index columns + ## 1.4.6 * Re-enable foreign key constraint for every session diff --git a/example/pubspec.lock b/example/pubspec.lock index 5ea3386..89deb41 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -105,7 +105,7 @@ packages: path: ".." relative: true source: path - version: "1.4.6" + version: "1.4.7" leak_tracker: dependency: transitive description: diff --git a/lib/src/index_column.dart b/lib/src/index_column.dart index b3b4eff..23dae75 100644 --- a/lib/src/index_column.dart +++ b/lib/src/index_column.dart @@ -158,6 +158,21 @@ class IndexColumn { // Query operator <=(I value) { // return lessThanOrEqual(value); // } + + Query contains(dynamic value, {bool caseInsensitive = false}) { + if (value is! String || value is! I) { + throw Exception( + 'Can not build query as field "$_field" needs a value of type $String, but got ${value.runtimeType}.', + ); + } + + return _ContainsStringQuery( + _entity, + _field, + value, + caseInsensitive: caseInsensitive, + ); + } } bool _typeEqual() => T == Y; diff --git a/lib/src/query.dart b/lib/src/query.dart index 20c0754..56fd8e7 100644 --- a/lib/src/query.dart +++ b/lib/src/query.dart @@ -122,3 +122,40 @@ class _LessThanQuery extends Query { ); } } + +class _ContainsStringQuery extends Query { + _ContainsStringQuery( + this.entity, + this.field, + this.value, { + required this.caseInsensitive, + }); + + final String entity; + final String field; + final String value; + final bool caseInsensitive; + + @override + (String, List) _entityKeysQuery() { + if (caseInsensitive) { + return ( + "SELECT `entity` FROM `index` WHERE `type` = ? AND `field` = ? AND `value` LIKE ?", + [ + entity, + field, + '%$value%', + ], + ); + } + + return ( + "SELECT `entity` FROM `index` WHERE `type` = ? AND `field` = ? AND `value` GLOB ?", + [ + entity, + field, + '*$value*', + ], + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 18e7292..b4cbea3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: indexed_entity_store description: A fast, simple, and synchronous entity store for Flutter applications. -version: 1.4.6 +version: 1.4.7 repository: https://github.com/LunaONE/indexed_entity_store environment: diff --git a/test/indexed_entity_store_test.dart b/test/indexed_entity_store_test.dart index cc28dd6..e9c66a3 100644 --- a/test/indexed_entity_store_test.dart +++ b/test/indexed_entity_store_test.dart @@ -27,6 +27,8 @@ void main() { expect(fooStore.getAllOnce(), hasLength(1)); expect(fooStore.queryOnce((cols) => cols['a'].equals('a')), hasLength(1)); + // equals is case sensitive + expect(fooStore.queryOnce((cols) => cols['a'].equals('A')), isEmpty); expect(fooStore.queryOnce((cols) => cols['a'].equals('b')), hasLength(0)); expect(fooStore.queryOnce((cols) => cols['b'].equals(2)), hasLength(1)); @@ -785,6 +787,52 @@ void main() { store.queryOnce((cols) => cols['float'].greaterThanOrEqual(1000.0)), hasLength(1), ); + + // String contains + expect( + store.queryOnce((cols) => cols['string'].contains('def')), + hasLength(1), + ); + expect( + store.queryOnce((cols) => cols['string'].contains('fau')), + hasLength(1), + ); + expect( + store.queryOnce((cols) => cols['string'].contains('lt')), + hasLength(1), + ); + expect( + store.queryOnce((cols) => cols['string'].contains('default')), + hasLength(1), + ); + expect( + store.queryOnce((cols) => cols['string'].contains('FAU')), + isEmpty, // does not match, as it's case sensitive by default + ); + expect( + store.queryOnce( + (cols) => cols['string'].contains('FAU', caseInsensitive: true), + ), + hasLength(1), // now matches, as we disabled case-sensitivity + ); + expect( + // `null`-able & unsued field + store.queryOnce((cols) => cols['stringOpt'].contains('def')), + isEmpty, + ); + expect( + // `null` string field allows the query, but does not have a match yet + store.queryOnce((cols) => cols['stringOpt'].contains('def')), + isEmpty, + ); + store.insert( + _AllSupportedIndexTypes.defaultIfNull(stringOpt: 'xxxx'), + ); + expect( + // `null` string field with value has a match now + store.queryOnce((cols) => cols['stringOpt'].contains('x')), + hasLength(1), + ); }); test('Limit', () async {