increment method

Future<void> increment(
  1. String documentId,
  2. String fieldPath, {
  3. num by = 1,
})

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();
}