increment method
Adds by (which may be negative) to the PnCounter field at fieldPath
of the document identified by documentId.
This is the only way to change a counter's value. Each device accumulates its own deltas, so concurrent increments on different devices all count: after convergence the counter is the sum of every device's contributions.
The document must exist (throws StateError otherwise); its existence register is refreshed like put does, so an increment competes with a concurrent delete last-write-wins. The field must be typed PnCounter by the collection's MergeStrategy — incrementing a field that holds anything else throws ArgumentError. Awaits the initial storage load before writing.
Implementation
Future<void> increment(String documentId, String fieldPath,
{num by = 1}) async {
_checkNoSeparator(documentId, 'document id');
_checkNoSeparator(fieldPath, 'field path');
final declared = _merge.typeAt(fieldPath);
if (declared is! PnCounter) {
// Without a strategy that types the field as a counter, the delta would
// be written to a record no read ever looks at: the increment would
// vanish silently.
throw ArgumentError.value(
fieldPath,
'fieldPath',
'is typed ${declared.runtimeType} by this collection\'s merge '
'strategy, not PnCounter, so an increment would be lost. Create '
'the collection with a SchemaMergeStrategy that types it '
'(annotated models: the generated _\$<Model>MergeStrategy)',
);
}
await _ready;
await _serialize(() async {
if (await _engine.value(_name, documentId) == null) {
throw StateError(
'Cannot increment "$fieldPath": no document "$documentId" in '
'"$_name".',
);
}
await _engine.put(_name, documentId, true);
await _engine.increment(_name, '$documentId$_sep$fieldPath', by);
});
_onLocalChange?.call();
}