Skip to content

Commit 7a55b33

Browse files
committed
rewrite watcher tests, fixes, wip
1 parent 5ed73d8 commit 7a55b33

File tree

13 files changed

+327
-562
lines changed

13 files changed

+327
-562
lines changed

lib/src/adapter/adapter.dart

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,14 @@ abstract class _BaseAdapter<T extends DataModelMixin<T>> with _Lifecycle {
172172
if (model._key == null) {
173173
throw Exception("Model must be initialized:\n\n$model");
174174
}
175-
final key = model._key!.detypifyKey();
175+
final key = model._key!.detypifyKey()!;
176176
final map = serialize(model, withRelationships: false);
177177
final data = jsonEncode(map);
178178
db.execute(
179179
'REPLACE INTO $internalType (key, data) VALUES (?, ?)', [key, data]);
180+
if (notify) {
181+
core._notify([model._key!], type: DataGraphEventType.updateNode);
182+
}
180183
return model;
181184
}
182185

@@ -259,13 +262,17 @@ abstract class _BaseAdapter<T extends DataModelMixin<T>> with _Lifecycle {
259262
}
260263

261264
/// Deletes all models of type [T] in local storage.
262-
Future<void> clearLocal() async {
263-
// TODO SELECT name FROM sqlite_master WHERE type='table' AND name='your_table_name';
265+
Future<void> clearLocal({bool notify = true}) async {
266+
// print(db.select('SELECT name FROM sqlite_master WHERE type=?', ['table']));
267+
// TODO should also clear edges?
268+
264269
// leave async in case some impls need to remove files
265270
for (final adapter in adapters.values) {
266271
db.execute('DELETE FROM ${adapter.internalType}');
267272
}
268-
core._notify([internalType], type: DataGraphEventType.clear);
273+
if (notify) {
274+
core._notify([internalType], type: DataGraphEventType.clear);
275+
}
269276
}
270277

271278
/// Counts all models of type [T] in local storage.
@@ -378,11 +385,11 @@ abstract class _BaseAdapter<T extends DataModelMixin<T>> with _Lifecycle {
378385
return model;
379386
}
380387

381-
Set<String> _edgesFor(String fromKey, String name) {
388+
Set<String> _keysFor(String key, String name) {
382389
final result = db.select(
383390
'SELECT src, dest FROM _edges WHERE (src = ? AND name = ?) OR (dest = ? AND inverse = ?)',
384-
[fromKey, name, fromKey, name]);
385-
return {for (final r in result) r['dest']};
391+
[key, name, key, name]);
392+
return {for (final r in result) r['src'] == key ? r['dest'] : r['src']};
386393
}
387394

388395
void _initializeRelationships(T model, {String? fromKey}) {
@@ -393,7 +400,7 @@ abstract class _BaseAdapter<T extends DataModelMixin<T>> with _Lifecycle {
393400
// if rel was omitted, fill with info of previous key
394401
// TODO optimize: put outside loop and query edgesFor just once
395402
if (fromKey != null && relationship._uninitializedKeys == null) {
396-
relationship._uninitializedKeys = _edgesFor(fromKey, metadata.name);
403+
relationship._uninitializedKeys = _keysFor(fromKey, metadata.name);
397404
}
398405
relationship.initialize(
399406
ownerKey: model._key!,

lib/src/adapter/remote_adapter.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ mixin _RemoteAdapter<T extends DataModelMixin<T>> on _SerializationAdapter<T> {
8383
label: label,
8484
onSuccess: (data, label) async {
8585
if (syncLocal) {
86-
clearLocal();
86+
await clearLocal(notify: false);
8787
}
8888
onSuccess ??= (data, label, _) async {
8989
final result = await this.onSuccess<List<T>>(data, label);

lib/src/adapter/watch_adapter.dart

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ mixin _WatchAdapter<T extends DataModelMixin<T>> on _RemoteAdapter<T> {
130130

131131
notifier._reloadFn = () async {
132132
if (!notifier.mounted) {
133+
// TODO || remote = false
133134
return;
134135
}
135136

@@ -143,11 +144,6 @@ mixin _WatchAdapter<T extends DataModelMixin<T>> on _RemoteAdapter<T> {
143144
headers: headers,
144145
syncLocal: syncLocal,
145146
label: label,
146-
// onSuccess: (response, label, _) async {
147-
// response;
148-
// print('got $response');
149-
// return [];
150-
// },
151147
onError: (e, label, _) async {
152148
try {
153149
await onError<List<T>>(e, label);
@@ -231,6 +227,7 @@ mixin _WatchAdapter<T extends DataModelMixin<T>> on _RemoteAdapter<T> {
231227
DataRequestLabel? label,
232228
}) {
233229
final id = core.getIdForKey(key);
230+
// print('_won got $key / id $id');
234231

235232
final maybeFinder = _internalHolder?.finders[finder]?.call(this);
236233
final finderFn = maybeFinder is DataFinderOne<T> ? maybeFinder : findOne;
@@ -259,6 +256,7 @@ mixin _WatchAdapter<T extends DataModelMixin<T>> on _RemoteAdapter<T> {
259256
.nonNulls
260257
.expand((_) => _)
261258
};
259+
// print('also watch $alsoWatchPairs (type $internalType)');
262260
} else {
263261
// if there is no model nothing should be watched, reset pairs
264262
alsoWatchPairs = {};
@@ -438,18 +436,20 @@ mixin _WatchAdapter<T extends DataModelMixin<T>> on _RemoteAdapter<T> {
438436
Iterable<List<String>> _getPairsForMeta(
439437
RelationshipMeta? meta, String ownerKey) {
440438
if (meta == null) return {};
441-
print('--- [read] _getPairsForMeta');
442439

443-
final relationshipKeys = _edgesFor(ownerKey, meta.name);
440+
final relationshipKeys = _keysFor(ownerKey, meta.name);
441+
// print('relkeys $relationshipKeys (edge $ownerKey ${meta.name})');
444442

445-
return {
443+
final pm = {
446444
// include key pairs of (owner, key)
447445
for (final key in relationshipKeys) [ownerKey, key],
448446
// recursively include key pairs for other requested relationships
449447
// TODO do not include if these are blank
450448
for (final childKey in relationshipKeys)
451449
_getPairsForMeta(meta.child, childKey).expand((_) => _).toList()
452450
};
451+
// print('pairs for meta $pm (edge $ownerKey ${meta.name})');
452+
return pm;
453453
}
454454

455455
// providers
@@ -498,6 +498,7 @@ mixin _WatchAdapter<T extends DataModelMixin<T>> on _RemoteAdapter<T> {
498498
String? finder,
499499
DataRequestLabel? label,
500500
}) {
501+
// print('in provider getting ${model}');
501502
final key = core.getKeyForModelOrId(internalType, model);
502503

503504
final relationshipMetas = alsoWatch

lib/src/model/relationship/belongs_to.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class BelongsTo<E extends DataModelMixin<E>> extends Relationship<E, E?> {
4444
set value(E? newValue) {
4545
if (value == null && newValue != null) {
4646
// addition
47-
super._addAll([newValue]);
47+
super._addAll({newValue._key!});
4848
return;
4949
}
5050
if (value != null && newValue != null) {

lib/src/model/relationship/has_many.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ class HasMany<E extends DataModelMixin<E>> extends Relationship<E, Set<E>> {
3939
///
4040
/// Attempting to add an existing [value] has no effect as this is a [Set]
4141
bool add(E value) {
42-
_addAll([value]);
42+
_addAll({value._key!});
4343
return true;
4444
}
4545

4646
void addAll(Iterable<E> values) {
47-
_addAll(values);
47+
_addAll(values.map((e) => e._key!).toSet());
4848
}
4949

5050
bool contains(E element) {

lib/src/model/relationship/relationship.dart

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,8 @@ sealed class Relationship<E extends DataModelMixin<E>, N> with EquatableMixin {
5353

5454
// setting up from scratch, remove all and add keys
5555

56-
db.execute(
57-
'DELETE FROM _edges WHERE (src = ? AND name = ?) OR (dest = ? AND inverse = ?)',
58-
[_ownerKey!, _name!, _ownerKey!, _name!]);
59-
60-
final ps = db.prepare(
61-
'INSERT INTO _edges (src, name, dest, inverse) VALUES (?, ?, ?, ?)');
62-
63-
for (final key in _uninitializedKeys!) {
64-
ps.execute([_ownerKey!, _name, key, _inverseName]);
65-
}
66-
ps.dispose();
56+
_removeAll(notify: false);
57+
_addAll(_uninitializedKeys!, notify: false);
6758

6859
_uninitializedKeys!.clear();
6960

@@ -72,42 +63,80 @@ sealed class Relationship<E extends DataModelMixin<E>, N> with EquatableMixin {
7263

7364
// implement collection-like methods
7465

75-
void _addAll(Iterable<E> values) {
66+
void _addAll(Iterable<String> keys, {bool notify = true}) {
7667
final ps = db.prepare(
7768
'REPLACE INTO _edges (src, name, dest, inverse) VALUES (?, ?, ?, ?)');
7869
final additions = [];
79-
for (final value in values) {
80-
ps.execute([ownerKey, name, value._key!, inverseName]);
81-
additions.add(value._key!);
70+
for (final key in keys) {
71+
final order = ownerKey.compareTo(key);
72+
final args = order == -1
73+
? [ownerKey, name, key, inverseName]
74+
: [key, inverseName, ownerKey, name];
75+
// TODO if we dont have inverseName?
76+
ps.execute(args);
77+
additions.add(key);
8278
}
8379
ps.dispose();
8480

85-
_adapter.core._notify(
86-
[ownerKey, ...additions],
87-
metadata: _name,
88-
type: DataGraphEventType.addEdge,
89-
);
81+
if (notify) {
82+
_adapter.core._notify(
83+
[ownerKey, ...additions],
84+
metadata: _name,
85+
type: DataGraphEventType.addEdge,
86+
);
87+
}
9088
}
9189

9290
bool _contains(E? element) {
9391
return _iterable.contains(element);
9492
}
9593

9694
bool _update(E value, E newValue) {
97-
db.execute(
98-
'UPDATE _edges SET dest = ? WHERE src = ? AND name = ? AND dest = ?',
99-
[newValue._key!, ownerKey, name, value._key!]);
95+
// -1 is ascending
96+
final currentKey = value._key!;
97+
final newKey = newValue._key!;
98+
final order = ownerKey.compareTo(currentKey);
99+
final args = [newKey, ownerKey, name, currentKey];
100+
101+
if (order == -1) {
102+
db.execute(
103+
'UPDATE _edges SET dest = ? WHERE src = ? AND name = ? AND dest = ?',
104+
args);
105+
} else {
106+
db.execute(
107+
'UPDATE _edges SET src = ? WHERE dest = ? AND inverse = ? AND src = ?',
108+
args);
109+
}
110+
100111
_adapter.core._notify(
101-
[ownerKey, newValue._key!],
112+
[ownerKey, newKey],
102113
metadata: _name,
103114
type: DataGraphEventType.updateEdge,
104115
);
105116
return true;
106117
}
107118

119+
void _removeAll({bool notify = true}) {
120+
db.execute(
121+
'DELETE FROM _edges WHERE (src = ? AND name = ?) OR (dest = ? AND inverse = ?)',
122+
[_ownerKey!, _name!, _ownerKey!, _name!]);
123+
}
124+
108125
bool _remove(E value) {
109-
db.execute('DELETE FROM _edges WHERE src = ? AND name = ? AND dest = ?',
110-
[ownerKey, name, value._key!]);
126+
// -1 is ascending
127+
final currentKey = value._key!;
128+
final order = ownerKey.compareTo(currentKey);
129+
final args = [ownerKey, name, currentKey];
130+
131+
if (order == -1) {
132+
db.execute(
133+
'DELETE FROM _edges WHERE src = ? AND name = ? AND dest = ?', args);
134+
} else {
135+
db.execute(
136+
'DELETE FROM _edges WHERE dest = ? AND inverse = ? AND src = ?',
137+
args);
138+
}
139+
111140
_adapter.core._notify(
112141
[ownerKey, value._key!],
113142
metadata: _name,
@@ -140,17 +169,9 @@ sealed class Relationship<E extends DataModelMixin<E>, N> with EquatableMixin {
140169
return _adapter.findManyLocal(_keys);
141170
}
142171

143-
Set<String> _keysFor(String key, String name) {
144-
final result = _adapter.storage.db.select(
145-
'SELECT src, dest FROM _edges WHERE (src = ? AND name = ?) OR (dest = ? AND inverse = ?)',
146-
[key, name, key, name]);
147-
// final edges = _adapter.storage.edgesFor([(key, name)]);
148-
return {for (final r in result) r['src'] == key ? r['dest'] : r['src']};
149-
}
150-
151172
Set<String> get _keys {
152173
if (_ownerKey == null) return {};
153-
return _keysFor(ownerKey, _name!);
174+
return _adapter._keysFor(ownerKey, _name!);
154175
}
155176

156177
DelayedStateNotifier<DataGraphEvent> get _relationshipEventNotifier {

lib/src/storage/local_storage.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ class LocalStorage {
2727
await destroy();
2828
}
2929

30-
print('opening in path $path');
3130
db = sqlite3.open(path);
3231

3332
db.execute('''
@@ -38,7 +37,8 @@ class LocalStorage {
3837
src INTEGER NOT NULL,
3938
name TEXT NOT NULL,
4039
dest INTEGER NOT NULL,
41-
inverse TEXT
40+
inverse TEXT,
41+
PRIMARY KEY (src, dest)
4242
);
4343
CREATE INDEX IF NOT EXISTS src_name_idx ON _edges(src, name);
4444
CREATE INDEX IF NOT EXISTS dest_inverse_idx ON _edges(dest, inverse);

0 commit comments

Comments
 (0)