@@ -21,12 +21,14 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable {
2121      'encoded_gzipped_json' ;
2222  static  const  String  _TABLE_PRODUCT_COLUMN_LAST_UPDATE  =  'last_update' ;
2323  static  const  String  _TABLE_PRODUCT_COLUMN_LANGUAGE  =  'lc' ;
24+   static  const  String  _TABLE_PRODUCT_COLUMN_PRODUCT_TYPE  =  'product_type' ;
2425
2526  static  const  List <String > _columns =  < String > [
2627    _TABLE_PRODUCT_COLUMN_BARCODE ,
2728    _TABLE_PRODUCT_COLUMN_GZIPPED_JSON ,
2829    _TABLE_PRODUCT_COLUMN_LAST_UPDATE ,
2930    _TABLE_PRODUCT_COLUMN_LANGUAGE ,
31+     _TABLE_PRODUCT_COLUMN_PRODUCT_TYPE ,
3032  ];
3133
3234  static  FutureOr <void > onUpgrade (
@@ -50,6 +52,12 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable {
5052        '$_TABLE_PRODUCT_COLUMN_LANGUAGE  TEXT' ,
5153      );
5254    }
55+     if  (oldVersion <  8 ) {
56+       await  db.execute (
57+         'alter table $_TABLE_PRODUCT  add column ' 
58+         '$_TABLE_PRODUCT_COLUMN_PRODUCT_TYPE  TEXT' ,
59+       );
60+     }
5361  }
5462
5563  /// Returns the [Product]  that matches the [barcode] , or null. 
@@ -177,7 +185,7 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable {
177185    }
178186    await  localDatabase.database.transaction (
179187      (final  Transaction  transaction) async  => 
180-           _bulkReplaceLoop (transaction, products, language),
188+           _bulkReplaceLoop (transaction, products, language, productType ),
181189    );
182190  }
183191
@@ -208,6 +216,7 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable {
208216    final  DatabaseExecutor  databaseExecutor,
209217    final  Iterable <Product > products,
210218    final  OpenFoodFactsLanguage  language,
219+     final  ProductType  productType,
211220  ) async  {
212221    final  int  lastUpdate =  LocalDatabase .nowInMillis ();
213222    final  BulkManager  bulkManager =  BulkManager ();
@@ -221,6 +230,7 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable {
221230      );
222231      insertParameters.add (lastUpdate);
223232      insertParameters.add (language.offTag);
233+       insertParameters.add (product.productType? .offTag ??  productType.offTag);
224234    }
225235    await  bulkManager.insert (
226236      bulkInsertable:  this ,
@@ -344,24 +354,38 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable {
344354
345355    const  String  tableJoin = 
346356        'p.$_TABLE_PRODUCT_COLUMN_BARCODE  = a.${DaoProductLastAccess .COLUMN_BARCODE }' ;
357+     const  String  columns = 
358+         'p.$_TABLE_PRODUCT_COLUMN_GZIPPED_JSON ' 
359+         ',p.$_TABLE_PRODUCT_COLUMN_BARCODE ' 
360+         ',p.$_TABLE_PRODUCT_COLUMN_PRODUCT_TYPE ' ;
361+     // we want rows with a different language - or a null language 
347362    final  String  languageCondition = 
348363        ' (' 
349364        'p.$_TABLE_PRODUCT_COLUMN_LANGUAGE  is null ' 
350365        "or p.$_TABLE_PRODUCT_COLUMN_LANGUAGE  != '${language .offTag }'" 
351366        ') ' ;
367+     // we want rows with that type - or a null type 
368+     final  String  productTypeCondition = 
369+         ' (' 
370+         'p.$_TABLE_PRODUCT_COLUMN_PRODUCT_TYPE  is null ' 
371+         "or p.$_TABLE_PRODUCT_COLUMN_PRODUCT_TYPE  = '${productType .offTag }'" 
372+         ') ' ;
352373
374+     // Listing the rows with a last access, ordered by last access (desc). 
353375    final  String  queryWithLastAccess = 
354-         'select p.$ _TABLE_PRODUCT_COLUMN_GZIPPED_JSON  ' 
376+         'select $ columns  ' 
355377        'from' 
356378        ' $_TABLE_PRODUCT  p ' 
357379        ' inner join ${DaoProductLastAccess .TABLE } a' 
358380        '  on $tableJoin  ' 
359381        'where' 
360382        ' $languageCondition  ' 
383+         ' and $productTypeCondition  ' 
361384        'order by a.${DaoProductLastAccess .COLUMN_LAST_ACCESS } desc' ;
362385
386+     // Listing the rows without a last access. 
363387    final  String  queryWithoutLastAccess = 
364-         'select p.$ _TABLE_PRODUCT_COLUMN_GZIPPED_JSON  ' 
388+         'select $ columns  ' 
365389        'from' 
366390        ' $_TABLE_PRODUCT  p ' 
367391        'where' 
@@ -370,8 +394,27 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable {
370394        '  from ${DaoProductLastAccess .TABLE } a ' 
371395        '  where $tableJoin  ' 
372396        ' ) ' 
397+         ' and $productTypeCondition  ' 
373398        ' and $languageCondition ' ;
374399
400+     final  Map <String , String > updates =  < String , String > {};
401+ 
402+     /// Updates products that didn't have a product_type *in the table column*. 
403+     /// 
404+     /// This way, we lazily populate the database. 
405+     /// After one "language refresh", all products will have a populated 
406+     /// product_type column, so this method will do nothing afterwards. 
407+      Future <void > updateUnknownProductTypes () async  {
408+       for  (final  MapEntry <String , String > entry in  updates.entries) {
409+         await  localDatabase.database.update (
410+           _TABLE_PRODUCT ,
411+           < String , String > {_TABLE_PRODUCT_COLUMN_PRODUCT_TYPE :  entry.value},
412+           where:  '$_TABLE_PRODUCT_COLUMN_BARCODE  = ?' ,
413+           whereArgs:  < String > [entry.key],
414+         );
415+       }
416+     }
417+ 
375418    // optimization: using 2 more simple queries than a "left join" that proved 
376419    // more expensive (less than .1s for each simple query, .5s for "left join") 
377420    final  List <String > queries =  < String > [
@@ -384,21 +427,36 @@ class DaoProduct extends AbstractSqlDao implements BulkDeletable {
384427      final  QueryCursor  queryCursor =  await  localDatabase.database
385428          .rawQueryCursor (query, null );
386429      while  (await  queryCursor.moveNext ()) {
387-         final  Product  product  =   _getProductFromQueryResult (queryCursor.current); 
388-         final   String  barcode  =  product.barcode ! ;
430+         final  String  barcode  = 
431+             queryCursor.current[ _TABLE_PRODUCT_COLUMN_BARCODE ] !   as   String ;
389432        if  (excludeBarcodes.contains (barcode)) {
390433          continue ;
391434        }
392-         if  ((product.productType ??  ProductType .food) !=  productType) {
435+         String ?  foundProductType = 
436+             queryCursor.current[_TABLE_PRODUCT_COLUMN_PRODUCT_TYPE ] as  String ? ;
437+         if  (foundProductType ==  null ) {
438+           final  Product  product =  _getProductFromQueryResult (
439+             queryCursor.current,
440+           );
441+           foundProductType =  product.productType? .offTag;
442+           if  (foundProductType !=  null ) {
443+             updates[barcode] =  foundProductType;
444+           }
445+         }
446+         if  ((foundProductType ??  ProductType .food.offTag) != 
447+             productType.offTag) {
393448          continue ;
394449        }
395450        result.add (barcode);
396451        if  (result.length ==  limit) {
452+           await  queryCursor.close ();
453+           await  updateUnknownProductTypes ();
397454          return  result;
398455        }
399456      }
400457    }
401458
459+     await  updateUnknownProductTypes ();
402460    return  result;
403461  }
404462
0 commit comments