Selaa lähdekoodia

fix: simplify product cache tables to minimal schema #111

- Remove redundant *_products_with_exclusion views
- Drop unnecessary columns (price, currency, description, images, raw_data, etc.)
- Rename platform-specific product_id columns to unified product_id
- Update sync functions to store only minimal data:
  * id, store_id, product_id, inner_id, name, sku, categories, excluded, last_synced_at
- Update API endpoints to query tables directly instead of views
- Compute exclusion status in API by checking excluded_categories table
- Database now ONLY for sync tracking and exclusion management
- All product data cached in Qdrant only

Related migrations:
- simplify_product_cache_tables.sql
- update_product_id_constraints.sql
Claude 4 kuukautta sitten
vanhempi
sitoutus
2c3fca2374

+ 48 - 41
supabase/functions/api/index.ts

@@ -1250,19 +1250,19 @@ serve(async (req) => {
       try {
       try {
         const platform = store.platform_name
         const platform = store.platform_name
 
 
-        // Use the view with computed exclusion status
-        const viewName = platform === 'woocommerce'
-          ? 'woocommerce_products_with_exclusion'
+        // Use the cache table directly
+        const tableName = platform === 'woocommerce'
+          ? 'woocommerce_products_cache'
           : platform === 'shopify'
           : platform === 'shopify'
-          ? 'shopify_products_with_exclusion'
-          : 'shoprenter_products_with_exclusion'
+          ? 'shopify_products_cache'
+          : 'shoprenter_products_cache'
 
 
         // Build query
         // Build query
         let query = supabase
         let query = supabase
-          .from(viewName)
+          .from(tableName)
           .select('*')
           .select('*')
           .eq('store_id', storeId)
           .eq('store_id', storeId)
-          .order('created_at', { ascending: false })
+          .order('last_synced_at', { ascending: false })
 
 
         // Apply search filter
         // Apply search filter
         if (search) {
         if (search) {
@@ -1298,19 +1298,42 @@ serve(async (req) => {
           )
           )
         }
         }
 
 
+        // Get excluded categories for this store
+        const { data: excludedCategories } = await supabase
+          .from('excluded_categories')
+          .select('category_id')
+          .eq('store_id', storeId)
+          .eq('platform', platform)
+
+        const excludedCategoryIds = new Set(excludedCategories?.map(ec => ec.category_id) || [])
+
+        // Check if product is excluded by category
+        const isExcludedByCategory = (categories: any[]): boolean => {
+          if (!categories || categories.length === 0) return false
+          return categories.some(cat => {
+            const catId = cat.id || cat.category_id || cat
+            return excludedCategoryIds.has(catId.toString())
+          })
+        }
+
         // Transform to response format
         // Transform to response format
-        let results = products.map(p => ({
-          id: p[platform === 'woocommerce' ? 'wc_product_id' : platform === 'shopify' ? 'shopify_product_id' : 'shoprenter_product_id'],
-          name: p.name || '',
-          sku: p.sku || '',
-          price: p.price || '0',
-          currency: p.currency || 'USD',
-          categories: p.categories || [],
-          enabled_in_context: !p.effectively_excluded, // Inverted logic: enabled = NOT excluded
-          excluded_by_individual: p.excluded,
-          excluded_by_category: p.exclusion_reason === 'category',
-          exclusion_reason: p.exclusion_reason
-        }))
+        let results = products.map(p => {
+          const excludedByCategory = isExcludedByCategory(p.categories || [])
+          const excludedByIndividual = p.excluded === true
+          const effectivelyExcluded = excludedByIndividual || excludedByCategory
+
+          return {
+            id: p.product_id,
+            inner_id: p.inner_id, // For ShopRenter
+            name: p.name || '',
+            sku: p.sku || '',
+            categories: p.categories || [],
+            enabled_in_context: !effectivelyExcluded, // Inverted logic: enabled = NOT excluded
+            excluded_by_individual: excludedByIndividual,
+            excluded_by_category: excludedByCategory,
+            exclusion_reason: excludedByIndividual ? 'individual' : excludedByCategory ? 'category' : null
+          }
+        })
 
 
         // Apply enabled filter if specified
         // Apply enabled filter if specified
         if (enabledFilter !== null) {
         if (enabledFilter !== null) {
@@ -1383,22 +1406,15 @@ serve(async (req) => {
           ? 'shopify_products_cache'
           ? 'shopify_products_cache'
           : 'shoprenter_products_cache'
           : 'shoprenter_products_cache'
 
 
-        const idColumn = platform === 'woocommerce'
-          ? 'wc_product_id'
-          : platform === 'shopify'
-          ? 'shopify_product_id'
-          : 'shoprenter_product_id'
-
         // Update excluded column directly in cache table
         // Update excluded column directly in cache table
         // Note: enabled in UI = NOT excluded in DB
         // Note: enabled in UI = NOT excluded in DB
         const { error: updateError } = await supabase
         const { error: updateError } = await supabase
           .from(tableName)
           .from(tableName)
           .update({
           .update({
-            excluded: !enabled, // Inverted logic
-            updated_at: new Date().toISOString()
+            excluded: !enabled // Inverted logic
           })
           })
           .eq('store_id', store_id)
           .eq('store_id', store_id)
-          .eq(idColumn, itemId)
+          .eq('product_id', itemId)
 
 
         if (updateError) {
         if (updateError) {
           console.error('Error updating product:', updateError)
           console.error('Error updating product:', updateError)
@@ -1460,22 +1476,15 @@ serve(async (req) => {
           ? 'shopify_products_cache'
           ? 'shopify_products_cache'
           : 'shoprenter_products_cache'
           : 'shoprenter_products_cache'
 
 
-        const idColumn = platform === 'woocommerce'
-          ? 'wc_product_id'
-          : platform === 'shopify'
-          ? 'shopify_product_id'
-          : 'shoprenter_product_id'
-
         // Update all products in bulk
         // Update all products in bulk
         for (const itemId of item_ids) {
         for (const itemId of item_ids) {
           await supabase
           await supabase
             .from(tableName)
             .from(tableName)
             .update({
             .update({
-              excluded: !enabled, // Inverted logic
-              updated_at: new Date().toISOString()
+              excluded: !enabled // Inverted logic
             })
             })
             .eq('store_id', store_id)
             .eq('store_id', store_id)
-            .eq(idColumn, itemId)
+            .eq('product_id', itemId)
         }
         }
 
 
         return new Response(
         return new Response(
@@ -1534,8 +1543,7 @@ serve(async (req) => {
         const { error: updateError } = await supabase
         const { error: updateError } = await supabase
           .from(tableName)
           .from(tableName)
           .update({
           .update({
-            excluded: false,
-            updated_at: new Date().toISOString()
+            excluded: false
           })
           })
           .eq('store_id', store_id)
           .eq('store_id', store_id)
 
 
@@ -1610,8 +1618,7 @@ serve(async (req) => {
         const { error: updateError } = await supabase
         const { error: updateError } = await supabase
           .from(tableName)
           .from(tableName)
           .update({
           .update({
-            excluded: true,
-            updated_at: new Date().toISOString()
+            excluded: true
           })
           })
           .eq('store_id', store_id)
           .eq('store_id', store_id)
 
 

+ 3 - 9
supabase/functions/shopify-sync/index.ts

@@ -570,17 +570,11 @@ async function syncProducts(
 
 
       return {
       return {
         store_id: storeId,
         store_id: storeId,
-        shopify_product_id: product.id.toString(),
+        product_id: product.id.toString(),
         name: product.title, // Map title to name for consistency
         name: product.title, // Map title to name for consistency
         sku: primaryVariant?.sku || null,
         sku: primaryVariant?.sku || null,
-        price: primaryVariant ? parseFloat(primaryVariant.price) : 0,
-        currency: currency,
         categories: categories,
         categories: categories,
-        product_type: product.product_type || null,
-        vendor: product.vendor || null,
-        tags: product.tags ? product.tags.split(',').map(t => t.trim()) : [],
-        description: product.body_html || null,
-        raw_data: product,
+        excluded: false, // Will be set if in exclusion list
         last_synced_at: new Date().toISOString()
         last_synced_at: new Date().toISOString()
       }
       }
     })
     })
@@ -593,7 +587,7 @@ async function syncProducts(
       const { error: upsertError } = await supabaseAdmin
       const { error: upsertError } = await supabaseAdmin
         .from('shopify_products_cache')
         .from('shopify_products_cache')
         .upsert(chunk, {
         .upsert(chunk, {
-          onConflict: 'store_id,shopify_product_id'
+          onConflict: 'store_id,product_id'
         })
         })
 
 
       if (upsertError) {
       if (upsertError) {

+ 3 - 5
supabase/functions/shoprenter-sync/index.ts

@@ -943,21 +943,19 @@ serve(wrapHandler('shoprenter-sync', async (req) => {
 
 
             const productsToCache = productsData.items.map((product: any) => ({
             const productsToCache = productsData.items.map((product: any) => ({
               store_id: storeId,
               store_id: storeId,
-              shoprenter_product_id: product.id,
+              product_id: product.id,
               inner_id: product.innerId,
               inner_id: product.innerId,
               name: product.name,
               name: product.name,
               sku: product.sku,
               sku: product.sku,
-              price: product.price,
-              currency: product.currency || 'HUF',
               categories: product.productCategory || [],
               categories: product.productCategory || [],
-              product_data: product,
+              excluded: false, // Will be set if in exclusion list
               last_synced_at: new Date().toISOString()
               last_synced_at: new Date().toISOString()
             }))
             }))
 
 
             const { error: upsertError } = await supabaseAdmin
             const { error: upsertError } = await supabaseAdmin
               .from('shoprenter_products_cache')
               .from('shoprenter_products_cache')
               .upsert(productsToCache, {
               .upsert(productsToCache, {
-                onConflict: 'store_id,shoprenter_product_id'
+                onConflict: 'store_id,product_id'
               })
               })
 
 
             if (upsertError) {
             if (upsertError) {

+ 4 - 12
supabase/functions/woocommerce-sync/index.ts

@@ -590,29 +590,21 @@ async function syncProducts(
       // Collect all products for Qdrant sync
       // Collect all products for Qdrant sync
       allProducts.push(...products)
       allProducts.push(...products)
 
 
-      // Map and upsert products to SQL cache
+      // Map and upsert products to SQL cache (minimal data for sync tracking only)
       const productsToCache = products.map((product: WooCommerceProduct) => ({
       const productsToCache = products.map((product: WooCommerceProduct) => ({
         store_id: storeId,
         store_id: storeId,
-        wc_product_id: product.id.toString(),
+        product_id: product.id.toString(),
         name: product.name,
         name: product.name,
         sku: product.sku || null,
         sku: product.sku || null,
-        price: parseFloat(product.price) || 0,
-        currency: 'USD', // Default, should be from store settings
-        description: product.description || null,
-        short_description: product.short_description || null,
-        stock_quantity: product.stock_quantity,
-        stock_status: product.stock_status,
-        type: product.type || 'simple',
         categories: product.categories || [],
         categories: product.categories || [],
-        images: product.images || [],
-        raw_data: product,
+        excluded: false, // Will be set if in exclusion list
         last_synced_at: new Date().toISOString()
         last_synced_at: new Date().toISOString()
       }))
       }))
 
 
       const { error: upsertError } = await supabaseAdmin
       const { error: upsertError } = await supabaseAdmin
         .from('woocommerce_products_cache')
         .from('woocommerce_products_cache')
         .upsert(productsToCache, {
         .upsert(productsToCache, {
-          onConflict: 'store_id,wc_product_id'
+          onConflict: 'store_id,product_id'
         })
         })
 
 
       if (upsertError) {
       if (upsertError) {