Skip to content

Commit

Permalink
Add delete(where:)
Browse files Browse the repository at this point in the history
  • Loading branch information
tp committed Dec 2, 2024
1 parent 751075a commit e7a76b1
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Add tests for internal schema migrations
* Speed up `writeMany` by defaulting to use a single statement for all inserts (as opposed to a single transactions with many individual inserts)
* One can revert to the previous behavior by setting `singleStatement: false` in the call
* Add `delete(where: )` to delete all rows matching a specific index-query

## 2.0.0

Expand Down
55 changes: 51 additions & 4 deletions lib/src/index_entity_store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -321,30 +321,43 @@ class IndexedEntityStore<T, K> {
final Iterable<T>? entities,
final K? key,
final Iterable<K>? keys,
// TODO(tp): QueryBuilder? where,
final QueryBuilder? where,
final bool? all,
}) {
assert(
entity != null ||
entities != null ||
key != null ||
keys != null ||
where != null ||
all != null,
);
assert(
all == null ||
(entity == null && entities == null && key == null && keys == null),
(entity == null &&
entities == null &&
key == null &&
keys == null &&
where == null),
);

if (all == true) {
_deleteAll();
} else {
_deleteManyByKey({
final combinedKeys = {
if (entity != null) _connector.getPrimaryKey(entity),
...?entities?.map(_connector.getPrimaryKey),
if (key != null) key,
...?keys,
});
};

if (combinedKeys.isNotEmpty) {
_deleteManyByKey(combinedKeys);
}

if (where != null) {
_deleteWhere(where);
}
}
}

Expand Down Expand Up @@ -386,6 +399,40 @@ class IndexedEntityStore<T, K> {
_handleUpdate(keys);
}

/// Deletes entities matching the query
void _deleteWhere(QueryBuilder query) {
final Set<K> keys;

try {
_database.execute('BEGIN');

final whereClause = query(_indexColumns)._entityKeysQuery();

final result = _database.select(
'DELETE FROM `entity` '
' WHERE `type` = ? '
' AND `entity`.`key` IN ( ${whereClause.$1} ) '
' RETURNING `key`',
[
_entityKey,
...whereClause.$2,
],
);

keys = {
for (final row in result) row['key']! as K,
};

_database.execute('COMMIT');
} catch (e) {
_database.execute('ROLLBACK');

rethrow;
}

_handleUpdate(keys);
}

void _assertNoMoreIndexEntries(K key) {
assert(
_database.select(
Expand Down
70 changes: 70 additions & 0 deletions test/indexed_entity_store_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,71 @@ void main() {
expect(listSubscription.value, isEmpty);
expect(fooStore.queryOnce(), isEmpty);
});

test(
'delete(where: )',
() async {
final path =
'/tmp/index_entity_store_test_${FlutterTimeline.now}.sqlite3';

final db = IndexedEntityDabase.open(path);

final valueWrappingConnector =
IndexedEntityConnector<_ValueWrapper, int, String>(
entityKey: 'value_wrapper',
getPrimaryKey: (f) => f.key,
getIndices: (index) {
index((e) => e.value, as: 'value');
},
serialize: (f) => jsonEncode(f.toJSON()),
deserialize: (s) => _ValueWrapper.fromJSON(
jsonDecode(s) as Map<String, dynamic>,
),
);

final valueStore = db.entityStore(valueWrappingConnector);

valueStore.writeMany(
[
for (var i = 0; i < 10; i++)
_ValueWrapper(FlutterTimeline.now + i, '$i'),
],
singleStatement: false,
);

final allEntities = valueStore.query();
final entityValue2 = valueStore.single(
where: (cols) => cols['value'].equals('2'),
);

expect(allEntities.value, hasLength(10));
expect(entityValue2.value, isNotNull);

// no match
valueStore.delete(where: (cols) => cols['value'].equals('11'));
expect(allEntities.value, hasLength(10));
expect(entityValue2.value, isNotNull);

// no match for complete query
valueStore.delete(
where: (cols) => cols['value'].equals('11') & cols['value'].equals('2'),
);
expect(allEntities.value, hasLength(10));
expect(entityValue2.value, isNotNull);

// match delete 2 entries
valueStore.delete(
where: (cols) => cols['value'].equals('2') | cols['value'].equals('7'),
);
expect(allEntities.value, hasLength(8));
expect(entityValue2.value, isNull);

allEntities.dispose();
entityValue2.dispose();

expect(valueStore.subscriptionCount, 0);
},
);
}

class _FooEntity {
Expand Down Expand Up @@ -1399,4 +1464,9 @@ class _ValueWrapper {
json['value'],
);
}

@override
String toString() {
return '_ValueWrapper($key, $value)';
}
}

0 comments on commit e7a76b1

Please sign in to comment.