persist method
Implementation
Future<void> persist(PersistPayload payload) {
return _syncLock.run(() async {
final localResolver = payload.resolver;
final docs = payload.persistenceDocs;
Set<DualDataStore> dataStores = {};
Map<String, List<DualDataStore>> pathDataStores = {};
// Pre-calculate and hydrate all resolved file data stores relevant to the updated documents.
for (final doc in docs) {
final docPath = doc.path;
final docData = doc.data;
if (docData == null) {
final stores = pathDataStores[docPath] = _resolveDataStores(docPath);
dataStores.addAll(stores);
} else {
final prevDataStoreName = resolver.getNearest(docPath)!.$2;
final nextDataStoreName = localResolver.getNearest(docPath)!.$2;
final prevDataStore = index[prevDataStoreName] ??= DualDataStore(
prevDataStoreName,
factory: factory,
encrypter: encrypter,
);
final nextDataStore = index[nextDataStoreName] ??= DualDataStore(
nextDataStoreName,
factory: factory,
encrypter: encrypter,
);
dataStores.add(prevDataStore);
dataStores.add(nextDataStore);
}
}
await Future.wait(dataStores.map((dataStore) => dataStore.hydrate()));
for (final doc in docs) {
final docPath = doc.path;
final docData = doc.data;
final encrypted = doc.encrypted;
// If the document has been deleted, then clear its data recursively from each of its
// resolved data stores.
if (docData == null) {
resolver.deletePath(docPath);
for (final dataStore in pathDataStores[docPath]!) {
dataStore.recursiveDelete(docPath);
}
// Otherwise, write its associated data store with the updated document data.
} else {
final prevResult = resolver.getNearest(docPath)!;
final prevResolverPath = prevResult.$1;
final prevResolverValue = prevResult.$2;
final nextResult = localResolver.getNearest(docPath)!;
final nextResolverPath = nextResult.$1;
final nextResolverValue = nextResult.$2;
final prevDataStoreName = prevResolverValue;
final prevDataStore = index[prevDataStoreName]!;
final dataStoreName = nextResolverValue;
final dataStore = index[dataStoreName]!;
resolver.writePath(nextResolverPath, nextResolverValue);
// Scenario 1: A document's resolver value changes and its path stays the same.
//
// When a document is written with the same resolver path a__b__c and updated resolver value 2 from 1,
// then store 1 grafts *all* its data under resolver path a__b__c into store 2 at resolver path a__b__c.
if (nextResolverValue != prevResolverValue &&
nextResolverPath == prevResolverPath) {
dataStore.graft(
nextResolverPath,
prevResolverPath,
null,
prevDataStore,
);
}
// Scenario 2: A document's resolver path changes from a shorter path to a longer path.
//
// When a document is updated from previous resolver path a__b__c to descendant resolver path a__b__c__d,
// then data under path a__b__c__d in resolver path a__b__c of the previous store should be grafted into resolver path a__b__c__d of the next store.
//
if (nextResolverPath.length > prevResolverPath.length) {
dataStore.graft(
nextResolverPath,
prevResolverPath,
nextResolverPath,
prevDataStore,
);
}
// Scenario 3: A document's resolver path changes from a longer path to a shorter path.
//
// When a document is updated from previous resolver path a__b__c__d to resolver path a__b__c,
// then *all* data in resolver path a__b__c__d of the previous store should be grafted into the next store with resolver path a__b__c.
if (nextResolverPath.length < prevResolverPath.length) {
resolver.deletePath(prevResolverPath, recursive: false);
dataStore.graft(
nextResolverPath,
prevResolverPath,
null,
prevDataStore,
);
}
dataStore.writePath(
nextResolverPath,
docPath,
docData,
encrypted,
);
}
}
_scheduleSync();
});
}