remoteSearch method
Search within a scope using a normalized query spec. Implementations should apply soft-delete semantics and only support operators and fields that are natively available on the backend. Unsupported operators/fields must throw ArgumentError with a clear message.
Implementation
@override
Future<List<T>> remoteSearch(SyncScope scope, QuerySpec spec) async {
final plan = buildSupabaseRemoteSearchRequest(
scope: scope,
spec: spec,
idColumn: config.idColumn,
updatedAtColumn: config.updatedAtColumn,
deletedAtColumn: config.deletedAtColumn,
scopeNameColumn: config.scopeNameColumn,
scopeKeysColumn: config.scopeKeysColumn,
);
// Test hook: execute via injected runner when provided
if (config.searchRunner != null) {
final rows = await config.searchRunner!(plan);
return rows
.map((e) => config.fromJson(Map<String, dynamic>.from(e)))
.toList(growable: false);
}
dynamic q = _table().select();
// Apply filters
for (final op in plan.filters) {
switch (op.method) {
case 'eq':
q = q.eq(op.column, op.value);
break;
case 'neq':
q = q.neq(op.column, op.value);
break;
case 'gt':
q = q.gt(op.column, op.value);
break;
case 'gte':
q = q.gte(op.column, op.value);
break;
case 'lt':
q = q.lt(op.column, op.value);
break;
case 'lte':
q = q.lte(op.column, op.value);
break;
case 'in':
q = q.inFilter(op.column, List.from(op.value as List));
break;
case 'like':
q = q.like(op.column, op.value as String);
break;
case 'contains':
q = q.contains(op.column, op.value);
break;
case 'isNull':
q = q.filter(op.column, 'is', null);
break;
default:
throw ArgumentError('Unsupported method in plan: ${op.method}');
}
}
// Apply orders
for (final o in plan.orders) {
q = q.order(o.column, ascending: o.ascending);
}
// Apply pagination
if (plan.limit != null) {
q = q.limit(plan.limit!);
}
if (plan.rangeFrom != null && plan.rangeTo != null) {
q = q.range(plan.rangeFrom!, plan.rangeTo!);
}
final res = await q;
return (res as List)
.map((e) => config.fromJson(Map<String, dynamic>.from(e)))
.toList(growable: false);
}