diff --git a/src/domain/entities/base-product-tag.ts b/src/domain/entities/base-product-tag.ts index 960ac8a..29b0437 100644 --- a/src/domain/entities/base-product-tag.ts +++ b/src/domain/entities/base-product-tag.ts @@ -16,5 +16,5 @@ export abstract class BaseProductTag { product: Product; @Property() - obsolete = false; + obsolete? = false; } diff --git a/src/domain/entities/product-ingredient.ts b/src/domain/entities/product-ingredient.ts index 0e811d1..6ce256b 100644 --- a/src/domain/entities/product-ingredient.ts +++ b/src/domain/entities/product-ingredient.ts @@ -47,5 +47,5 @@ export class ProductIngredient { ingredients = new Collection(this); @Property() - obsolete = false; + obsolete? = false; } diff --git a/src/domain/entities/product.ts b/src/domain/entities/product.ts index 7b8a298..c68dc53 100644 --- a/src/domain/entities/product.ts +++ b/src/domain/entities/product.ts @@ -35,9 +35,9 @@ export class Product { @Property() ingredientsCount?: number; - // The followign fields are populated by the query service + // The following fields are populated by the query service @Property() - obsolete = false; + obsolete? = false; @Property({ type: 'uuid', index: true }) lastUpdateId?: string; diff --git a/src/domain/services/import.service.spec.ts b/src/domain/services/import.service.spec.ts index 0e380a1..d61b6f1 100644 --- a/src/domain/services/import.service.spec.ts +++ b/src/domain/services/import.service.spec.ts @@ -41,21 +41,25 @@ jest.mock('mongodb', () => { MongoClient: jest.fn(() => ({ connect: jest.fn(), db: () => { - let index = 0; return { - collection: () => ({ - find: (...args: any) => { - findCalls.push(args); - return { - next: async () => { - return index++ <= mockedProducts.length - ? mockedProducts[index - 1] - : null; - }, - close: jest.fn(), - }; - }, - }), + collection: (collectionName) => { + let index = 0; + const productList = + collectionName === 'products' ? mockedProducts : []; + return { + find: (...args: any) => { + findCalls.push(args); + return { + next: async () => { + return index++ <= productList.length + ? productList[index - 1] + : null; + }, + close: jest.fn(), + }; + }, + }; + }, }; }, close: jest.fn(), @@ -392,4 +396,53 @@ describe('importWithFilter', () => { await Promise.all(imports); }); }); + + it('should flag products not in mongodb as deleted', async () => { + await createTestingModule([DomainModule], async (app) => { + const importService = app.get(ImportService); + + // GIVEN: An existing product that doesn't exist in MongoDB + const em = app.get(EntityManager); + const productIdToDelete = randomCode(); + const productToDelete = em.create(Product, { + code: productIdToDelete, + source: ProductSource.FULL_LOAD, + lastUpdated: new Date(2023, 1, 1), + lastModified: new Date(lastModified * 1000), + }); + em.create(ProductIngredientsTag, { + product: productToDelete, + value: 'old_ingredient', + }); + await em.flush(); + + const beforeImport = Date.now(); + // WHEN: Doing an incremental import from MongoDB where the id is mentioned + const { products, productIdExisting, productIdNew } = testProducts(); + mockMongoDB(products); + await importService.importWithFilter( + { code: { $in: [productIdExisting, productIdNew, productIdToDelete] } }, + ProductSource.EVENT, + ); + + // THEN: Obsolete flag should get set to null + const deletedProduct = await em.findOne(Product, { + code: productIdToDelete, + }); + const updatedProduct = await em.findOne(Product, { + code: productIdExisting, + }); + expect(deletedProduct.lastUpdateId).toBe(updatedProduct.lastUpdateId); + expect(deletedProduct.lastUpdated.getTime()).toBeGreaterThanOrEqual( + beforeImport, + ); + expect(deletedProduct.source).toBe(ProductSource.EVENT); + expect(updatedProduct.obsolete).toBe(false); + + const deletedTag = await em.findOne(ProductIngredientsTag, { + product: deletedProduct, + }); + expect(deletedTag.obsolete).toBeNull(); + }); + }); }); diff --git a/src/domain/services/import.service.ts b/src/domain/services/import.service.ts index 91351aa..b35f499 100644 --- a/src/domain/services/import.service.ts +++ b/src/domain/services/import.service.ts @@ -99,6 +99,9 @@ export class ImportService { // Flush mikro-orm before switching to native SQL await this.em.flush(); + const inputCodes = filter.code?.$in; + const foundCodes = []; + // Now using postgres to help with transactions const connection = await sql.reserve(); await connection`CREATE TEMP TABLE product_temp (id int PRIMARY KEY, last_modified timestamptz, data jsonb)`; @@ -121,6 +124,8 @@ export class ImportService { this.logger.debug(`Fetched ${i}`); } + if (source === ProductSource.EVENT) foundCodes.push(data.code); + // Find the product if it exists let results = await connection`select id, last_modified from product where code = ${data.code}`; @@ -182,6 +187,27 @@ export class ImportService { } await client.close(); + // If doing an event import flag all products that weren't found in MongoDB as deleted (obsolete = null) + if (source === ProductSource.EVENT) { + const missingProducts = inputCodes.filter( + (code) => !foundCodes.includes(code), + ); + if (missingProducts.length) { + const deletedProducts = await connection`UPDATE product SET + obsolete = NULL, + last_update_id = ${updateId}, + last_updated = ${new Date()}, + source = ${source} + WHERE code IN ${sql(missingProducts)} + RETURNING id`; + + await this.deleteProductTags( + connection, + deletedProducts.map((p) => p.id), + ); + } + } + // If doing a full import delete all products that weren't updated and flag all tags as imported if (source === ProductSource.FULL_LOAD) { await this.tagService.addLoadedTags( @@ -330,9 +356,32 @@ export class ImportService { } async deleteOtherProducts(connection: ReservedSql, updateId: string) { - const deleted = - await connection`delete from product where last_update_id != ${updateId} OR last_update_id IS NULL`; - this.logger.debug(`${deleted.count} Products deleted`); + const deletedProducts = await connection`UPDATE product SET + obsolete = NULL, + last_update_id = ${updateId}, + last_updated = ${new Date()}, + source = ${ProductSource.FULL_LOAD} + WHERE last_update_id != ${updateId} OR last_update_id IS NULL + RETURNING id`; + this.logger.debug(`${deletedProducts.count} Products deleted`); + + await this.deleteProductTags( + connection, + deletedProducts.map((p) => p.id), + ); + } + + private async deleteProductTags( + connection: ReservedSql, + deletedProductIds: any[], + ) { + for (const entity of Object.values(ProductTagMap.MAPPED_TAGS)) { + const tableName = this.em.getMetadata(entity).tableName; + await connection`UPDATE ${sql(tableName)} SET obsolete = NULL + where product_id in ${sql(deletedProductIds)}`; + } + await connection`UPDATE product_ingredient SET obsolete = NULL + where product_id in ${sql(deletedProductIds)}`; } // Make sure to pause redis before calling this diff --git a/src/migrations/.snapshot-query.json b/src/migrations/.snapshot-query.json index ca0ca0f..67a63df 100644 --- a/src/migrations/.snapshot-query.json +++ b/src/migrations/.snapshot-query.json @@ -113,7 +113,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" }, @@ -233,7 +233,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -304,7 +304,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -375,7 +375,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -446,7 +446,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -517,7 +517,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -588,7 +588,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -659,7 +659,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -730,7 +730,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -801,7 +801,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -872,7 +872,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -943,7 +943,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1014,7 +1014,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1085,7 +1085,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1156,7 +1156,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1227,7 +1227,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1298,7 +1298,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1369,7 +1369,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1440,7 +1440,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1511,7 +1511,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1582,7 +1582,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1653,7 +1653,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1724,7 +1724,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1795,7 +1795,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -1956,7 +1956,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2043,7 +2043,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2114,7 +2114,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2185,7 +2185,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2256,7 +2256,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2327,7 +2327,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2398,7 +2398,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2469,7 +2469,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2540,7 +2540,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2611,7 +2611,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2682,7 +2682,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2753,7 +2753,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2824,7 +2824,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2895,7 +2895,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -2966,7 +2966,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3037,7 +3037,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3108,7 +3108,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3179,7 +3179,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3250,7 +3250,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3321,7 +3321,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3392,7 +3392,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3463,7 +3463,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3534,7 +3534,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3605,7 +3605,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3676,7 +3676,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3747,7 +3747,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3818,7 +3818,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3889,7 +3889,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -3960,7 +3960,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4031,7 +4031,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4102,7 +4102,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4173,7 +4173,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4244,7 +4244,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4315,7 +4315,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4386,7 +4386,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4457,7 +4457,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4528,7 +4528,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4599,7 +4599,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4670,7 +4670,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4741,7 +4741,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4812,7 +4812,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } @@ -4883,7 +4883,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, + "nullable": true, "default": "false", "mappedType": "boolean" } diff --git a/src/migrations/Migration20240913113703.ts b/src/migrations/Migration20240913113703.ts new file mode 100644 index 0000000..36c68fb --- /dev/null +++ b/src/migrations/Migration20240913113703.ts @@ -0,0 +1,405 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20240913113703 extends Migration { + + async up(): Promise { + this.addSql('alter table "query"."product" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_additives_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_additives_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_allergens_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_allergens_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_amino_acids_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_amino_acids_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_brands_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_brands_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_categories_properites_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_categories_properites_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_categories_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_categories_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_checkers_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_checkers_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_cities_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_cities_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_codes_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_codes_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_correctors_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_correctors_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_countries_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_countries_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_data_quality_bugs_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_data_quality_bugs_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_data_quality_errors_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_data_quality_errors_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_data_quality_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_data_quality_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_data_quality_warnings_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_data_quality_warnings_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_data_sources_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_data_sources_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_debug_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_debug_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_ecoscore_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ecoscore_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_editors_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_editors_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_emb_codes_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_emb_codes_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_entry_dates_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_entry_dates_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_food_groups_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_food_groups_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_informers_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_informers_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_ingredient" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredient" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_ingredients_analysis_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_analysis_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_ingredients_from_palm_oil_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_from_palm_oil_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_ingredients_ntag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_ntag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_ingredients_original_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_original_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_ingredients_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_ingredients_that_may_be_from_palm_oil_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_that_may_be_from_palm_oil_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_keywords_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_keywords_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_labels_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_labels_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_languages_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_languages_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_last_check_dates_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_last_check_dates_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_last_edit_dates_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_last_edit_dates_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_latest_image_dates_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_latest_image_dates_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_manufacturing_places_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_manufacturing_places_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_minerals_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_minerals_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_misc_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_misc_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_nova_groups_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nova_groups_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_nucleotides_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nucleotides_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_nutrient_levels_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nutrient_levels_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_nutriscore2021tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nutriscore2021tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_nutriscore2023tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nutriscore2023tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_nutriscore_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nutriscore_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_nutrition_grades_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nutrition_grades_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_origins_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_origins_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_other_nutritional_substances_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_other_nutritional_substances_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_packaging_materials_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_packaging_materials_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_packaging_recycling_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_packaging_recycling_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_packaging_shapes_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_packaging_shapes_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_packaging_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_packaging_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_periods_after_opening_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_periods_after_opening_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_photographers_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_photographers_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_pnns_groups1tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_pnns_groups1tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_pnns_groups2tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_pnns_groups2tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_popularity_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_popularity_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_purchase_places_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_purchase_places_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_states_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_states_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_stores_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_stores_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_teams_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_teams_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_traces_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_traces_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_unknown_nutrients_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_unknown_nutrients_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_vitamins_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_vitamins_tag" alter column "obsolete" drop not null;'); + + this.addSql('alter table "query"."product_weighers_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_weighers_tag" alter column "obsolete" drop not null;'); + } + + async down(): Promise { + this.addSql('alter table "query"."product" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_additives_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_additives_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_allergens_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_allergens_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_amino_acids_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_amino_acids_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_brands_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_brands_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_categories_properites_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_categories_properites_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_categories_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_categories_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_checkers_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_checkers_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_cities_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_cities_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_codes_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_codes_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_correctors_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_correctors_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_countries_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_countries_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_data_quality_bugs_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_data_quality_bugs_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_data_quality_errors_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_data_quality_errors_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_data_quality_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_data_quality_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_data_quality_warnings_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_data_quality_warnings_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_data_sources_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_data_sources_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_debug_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_debug_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_ecoscore_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ecoscore_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_editors_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_editors_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_emb_codes_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_emb_codes_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_entry_dates_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_entry_dates_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_food_groups_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_food_groups_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_informers_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_informers_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_ingredient" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredient" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_ingredients_analysis_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_analysis_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_ingredients_from_palm_oil_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_from_palm_oil_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_ingredients_ntag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_ntag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_ingredients_original_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_original_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_ingredients_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_ingredients_that_may_be_from_palm_oil_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_ingredients_that_may_be_from_palm_oil_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_keywords_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_keywords_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_labels_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_labels_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_languages_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_languages_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_last_check_dates_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_last_check_dates_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_last_edit_dates_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_last_edit_dates_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_latest_image_dates_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_latest_image_dates_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_manufacturing_places_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_manufacturing_places_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_minerals_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_minerals_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_misc_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_misc_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_nova_groups_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nova_groups_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_nucleotides_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nucleotides_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_nutrient_levels_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nutrient_levels_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_nutriscore2021tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nutriscore2021tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_nutriscore2023tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nutriscore2023tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_nutriscore_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nutriscore_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_nutrition_grades_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_nutrition_grades_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_origins_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_origins_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_other_nutritional_substances_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_other_nutritional_substances_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_packaging_materials_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_packaging_materials_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_packaging_recycling_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_packaging_recycling_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_packaging_shapes_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_packaging_shapes_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_packaging_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_packaging_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_periods_after_opening_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_periods_after_opening_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_photographers_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_photographers_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_pnns_groups1tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_pnns_groups1tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_pnns_groups2tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_pnns_groups2tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_popularity_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_popularity_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_purchase_places_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_purchase_places_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_states_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_states_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_stores_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_stores_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_teams_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_teams_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_traces_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_traces_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_unknown_nutrients_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_unknown_nutrients_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_vitamins_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_vitamins_tag" alter column "obsolete" set not null;'); + + this.addSql('alter table "query"."product_weighers_tag" alter column "obsolete" type boolean using ("obsolete"::boolean);'); + this.addSql('alter table "query"."product_weighers_tag" alter column "obsolete" set not null;'); + } + +}