Closed flutter-painter closed 3 years ago
Write operations should be grouped in transactions for consistency and performance You can read/add/delete/update/write in the same transaction.
// Let's assume a store with the following products
var store = intMapStoreFactory.store('product');
await store.addAll(db, [
{'name': 'Lamp', 'price': 10, 'id': 'lamp'},
{'name': 'Chair', 'price': 100, 'id': 'chair'},
{'name': 'Table', 'price': 250, 'id': 'table'}
]);
Let's assume you want a function to update all of your products
await updateProducts(
[
{'name': 'Lamp', 'price': 17, 'id': 'lamp'}, // Price modified
{'name': 'Bike', 'price': 999, 'id': 'bike'}, // Added
{'name': 'Chair', 'price': 100, 'id': 'chair'}, // Unchanged
// Product 'table' had been removed
],
);
A first basic implementation would be (but could be improved as in fact 2 transactions are created here)
// Update without using transactions
Future<void> updateProducts(
List<Map<String, Object?>> products) async {
// Delete all existing products first.
// One transaction is created here
await store.delete(db);
// Add all products
// One transaction is created here
await store.addAll(db, products);
}
A small improvment would be to use a transaction:
// Update in a transaction
Future<void> updateProducts(
List<Map<String, Object?>> products) async {
await db.transaction((transaction) async {
// Delete all
await store.delete(transaction);
// Add all
await store.addAll(transaction, products);
});
}
However we are still deleting everything first, so even if a product does not change, a write is performed.
A better optimized version would check for deleted/updated/added items to only perform the necessary writes:
/// Read products by ids and return a map
Future<Map<String, RecordSnapshot<int, Map<String, Object?>>>>
getProductsByIds(DatabaseClient db, List<String> ids) async {
var snapshots = await store.find(db,
finder: Finder(
filter: Filter.or(
ids.map((e) => Filter.equals('id', e)).toList())));
return <String, RecordSnapshot<int, Map<String, Object?>>>{
for (var snapshot in snapshots)
snapshot.value['id']!.toString(): snapshot
};
}
/// Update products
///
/// - Unmodified records remain untouched
/// - Modified records are updated
/// - New records are added.
/// - Missing one are deleted
Future<void> updateProducts(
List<Map<String, Object?>> products) async {
await db.transaction((transaction) async {
var productIds =
products.map((map) => map['id'] as String).toList();
var map = await getProductsByIds(db, productIds);
// Watch for deleted item
var keysToDelete = (await store.findKeys(transaction)).toList();
for (var product in products) {
var snapshot = map[product['id'] as String];
if (snapshot != null) {
// The record current key
var key = snapshot.key;
// Remove from deletion list
keysToDelete.remove(key);
// Don't update if no change
if (const DeepCollectionEquality()
.equals(snapshot.value, product)) {
// no changes
continue;
} else {
// Update product
await store.record(key).put(transaction, product);
}
} else {
// Add missing product
await store.add(transaction, product);
}
}
// Delete the one not present any more
await store.records(keysToDelete).delete(transaction);
});
}
Thank you so much for this comprehensive example, I'll aim at the last one !
Hi Alex ! Thanks again for the great work on sembast.
Please could you enlighten on transactions, From the documentation it seems they are only for bulks add operations :
Would transaction make any sense for bulk deletions ? I fail to see what is the most appropriate way for bulk updates and often resort to deleteAll(), addAll().