Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 5933 - less greedy language refresh #6038

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,22 @@ class BackgroundTaskLanguageRefresh extends BackgroundTask {
);

static Future<void> addTask(
final LocalDatabase localDatabase,
) async {
for (final ProductType productType in ProductType.values) {
await _addTask(
localDatabase,
excludeBarcodes: <String>[],
productType: productType,
);
}
}

static Future<void> _addTask(
final LocalDatabase localDatabase, {
final List<String> excludeBarcodes = const <String>[],
final ProductType? productType,
required final List<String> excludeBarcodes,
required final ProductType productType,
}) async {
if (productType == null) {
for (final ProductType item in ProductType.values) {
await addTask(
localDatabase,
excludeBarcodes: excludeBarcodes,
productType: item,
);
}
return;
}
final String uniqueId = await _operationType.getNewKey(
localDatabase,
productType: productType,
Expand Down Expand Up @@ -167,9 +169,10 @@ class BackgroundTaskLanguageRefresh extends BackgroundTask {
for (final Product product in searchResult.products!) {
newExcludeBarcodes.remove(product.barcode);
}
await addTask(
await _addTask(
localDatabase,
excludeBarcodes: newExcludeBarcodes,
productType: productType,
);
}
}
33 changes: 15 additions & 18 deletions packages/smooth_app/lib/background/operation_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,33 +116,30 @@ enum OperationType {
return int.parse(keyItems[1]);
}

static String getBarcode(final String key) {
final List<String> keyItems = key.split(_transientHeaderSeparator);
return keyItems[2];
}
static String getBarcode(final String key) => _getNthParameter(key, 2)!;

static int? getTotalSize(final String key) {
final List<String> keyItems = key.split(_transientHeaderSeparator);
if (keyItems.length <= 3) {
return null;
}
return int.tryParse(keyItems[3]);
}
static int? getTotalSize(final String key) => _getNthIntParameter(key, 3);

static int? getSoFarSize(final String key) => _getNthIntParameter(key, 4);

static String? getWork(final String key) => _getNthParameter(key, 5);

static int? getSoFarSize(final String key) {
static String? getProductType(final String key) => _getNthParameter(key, 6);

static String? _getNthParameter(final String key, final int index) {
final List<String> keyItems = key.split(_transientHeaderSeparator);
if (keyItems.length <= 4) {
if (keyItems.length <= index) {
return null;
}
return int.tryParse(keyItems[4]);
return keyItems[index];
}

static String? getWork(final String key) {
final List<String> keyItems = key.split(_transientHeaderSeparator);
if (keyItems.length <= 5) {
static int? _getNthIntParameter(final String key, final int index) {
final String? parameter = _getNthParameter(key, index);
if (parameter == null) {
return null;
}
return keyItems[5];
return int.tryParse(parameter);
}

static OperationType? getOperationType(final String key) {
Expand Down
96 changes: 53 additions & 43 deletions packages/smooth_app/lib/database/dao_product.dart
Original file line number Diff line number Diff line change
Expand Up @@ -345,54 +345,64 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable {
required final List<String> excludeBarcodes,
required final ProductType productType,
}) async {
/// Unfortunately, some SQFlite implementations don't support "nulls last"
String getRawQuery(final bool withNullsLast) =>
final List<String> result = <String>[];

const String tableJoin =
'p.$_TABLE_PRODUCT_COLUMN_BARCODE = a.${DaoProductLastAccess.COLUMN_BARCODE}';
final String languageCondition = ' ('
'p.$_TABLE_PRODUCT_COLUMN_LANGUAGE is null '
"or p.$_TABLE_PRODUCT_COLUMN_LANGUAGE != '${language.offTag}'"
') ';

final String queryWithLastAccess =
'select p.$_TABLE_PRODUCT_COLUMN_GZIPPED_JSON '
'from'
' $_TABLE_PRODUCT p'
' left outer join ${DaoProductLastAccess.TABLE} a'
' on p.$_TABLE_PRODUCT_COLUMN_BARCODE = a.${DaoProductLastAccess.COLUMN_BARCODE} '
' $_TABLE_PRODUCT p '
' inner join ${DaoProductLastAccess.TABLE} a'
' on $tableJoin '
'where'
' p.$_TABLE_PRODUCT_COLUMN_LANGUAGE is null'
' or p.$_TABLE_PRODUCT_COLUMN_LANGUAGE != ? '
'order by a.${DaoProductLastAccess.COLUMN_LAST_ACCESS} desc ${withNullsLast ? 'nulls last' : ''} ';

List<Map<String, dynamic>> queryResults = <Map<String, dynamic>>[];
try {
queryResults = await localDatabase.database.rawQuery(
getRawQuery(true),
<Object>[
language.offTag,
],
);
} catch (e) {
if (!e.toString().startsWith(
'DatabaseException(near "nulls": syntax error (code 1 SQLITE_ERROR',
)) {
rethrow;
}
queryResults = await localDatabase.database.rawQuery(
getRawQuery(false),
<Object>[
language.offTag,
],
);
}
' $languageCondition '
'order by a.${DaoProductLastAccess.COLUMN_LAST_ACCESS} desc';

final List<String> result = <String>[];
final String queryWithoutLastAccess =
'select p.$_TABLE_PRODUCT_COLUMN_GZIPPED_JSON '
'from'
' $_TABLE_PRODUCT p '
'where'
' not exists('
' select null'
' from ${DaoProductLastAccess.TABLE} a '
' where $tableJoin '
' ) '
' and $languageCondition';

for (final Map<String, dynamic> row in queryResults) {
final Product product = _getProductFromQueryResult(row);
final String barcode = product.barcode!;
if (excludeBarcodes.contains(barcode)) {
continue;
}
if ((product.productType ?? ProductType.food) != productType) {
continue;
}
result.add(barcode);
if (result.length == limit) {
break;
// optimization: using 2 more simple queries than a "left join" that proved
// more expensive (less than .1s for each simple query, .5s for "left join")
final List<String> queries = <String>[
queryWithLastAccess,
queryWithoutLastAccess,
];
for (final String query in queries) {
// optimization: using a cursor, as we don't want all the rows,
// and we don't know how many rows we'll need.
final QueryCursor queryCursor =
await localDatabase.database.rawQueryCursor(
query,
null,
);
while (await queryCursor.moveNext()) {
final Product product = _getProductFromQueryResult(queryCursor.current);
final String barcode = product.barcode!;
if (excludeBarcodes.contains(barcode)) {
continue;
}
if ((product.productType ?? ProductType.food) != productType) {
continue;
}
result.add(barcode);
if (result.length == limit) {
return result;
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion packages/smooth_app/lib/pages/offline_tasks_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ class _OfflineTaskState extends State<OfflineTaskPage> {
info = '';
}
final BackgroundTaskQueue queue = queues[taskId]!;
final String? productType =
OperationType.getProductType(taskId);
return ListTile(
leading: Icon(queue.iconData),
onTap: () async {
Expand Down Expand Up @@ -110,7 +112,8 @@ class _OfflineTaskState extends State<OfflineTaskPage> {
'$info'
'(${OperationType.getOperationType(taskId)?.getLabel(
appLocalizations,
) ?? appLocalizations.background_task_operation_unknown})',
) ?? appLocalizations.background_task_operation_unknown})'
'${productType == null ? '' : ' ($productType)'}',
),
subtitle: Text(_getMessage(status, appLocalizations)),
trailing: const Icon(Icons.clear),
Expand Down
Loading