← back to Handbag Auth Nextjs

scripts/match-handbag-names.js

176 lines

/**
 * Match Japanese handbag model names to English catalog
 * Maps "エルメス バーキン" → "Birkin" etc.
 */

const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()

// Japanese to English model name mapping
const MODEL_MAPPINGS = {
  // Hermès
  'バーキン': 'Birkin',
  'ケリー': 'Kelly',
  'コンスタンス': 'Constance',
  'エブリン': 'Evelyne',
  'ピコタン': 'Picotin',
  'リンディ': 'Lindy',
  'ボリード': 'Bolide',
  'ガーデンパーティ': 'Garden Party',
  'エールバッグ': 'Herbag',

  // Chanel
  'クラシックフラップ': 'Classic Flap',
  'ボーイバッグ': 'Boy Bag',
  'マトラッセ': 'Classic Flap',
  'デヴィル': 'Deauville',
  'ガブリエル': 'Gabrielle',
  'ココハンドル': 'Coco Handle',
  'チェーンウォレット': 'Wallet on Chain',
  'カンボン': 'Cambon',

  // Louis Vuitton
  'ネヴァーフル': 'Neverfull',
  'スピーディ': 'Speedy',
  'ポシェット': 'Pochette',
  'アルマ': 'Alma',
  'ネオノエ': 'NeoNoe',
  'カプシーヌ': 'Capucines',
  'キーポル': 'Keepall',
  'ツイスト': 'Twist',
  'ドーフィーヌ': 'Dauphine',

  // Goyard
  'サンルイ': 'Saint Louis',
  'アルトワ': 'Artois',
  'ベルヴェデール': 'Belvedere',
  'アンジュー': 'Anjou',
  'サイゴン': 'Saigon',

  // Celine
  'ラゲージ': 'Luggage',
  'ベルトバッグ': 'Belt Bag',
  'トリオンフ': 'Triomphe',
  'クラシックボックス': 'Classic Box',
  'カバ': 'Cabas',

  // Prada
  'ガレリア': 'Galleria',
  'サフィアーノ': 'Saffiano',
  'カナパ': 'Canapa',
  'ダブルバッグ': 'Double Bag',
  'カイエ': 'Cahier',

  // Add more as needed...
}

// Brand name normalization
const BRAND_MAPPINGS = {
  'エルメス': 'HERMÈS',
  'シャネル': 'CHANEL',
  'ルイヴィトン': 'LOUIS VUITTON',
  'ルイ・ヴィトン': 'LOUIS VUITTON',
  'ゴヤール': 'GOYARD',
  'セリーヌ': 'CELINE',
  'プラダ': 'PRADA',
  'ディオール': 'DIOR',
  'ボッテガヴェネタ': 'BOTTEGA VENETA',
  'ボッテガ・ヴェネタ': 'BOTTEGA VENETA',
  'サンローラン': 'SAINT LAURENT',
  'イヴサンローラン': 'SAINT LAURENT',
  'ロエベ': 'LOEWE',
  'バレンシアガ': 'BALENCIAGA',
  'ミュウミュウ': 'MIU MIU',
  'フェンディ': 'FENDI',
  'ジバンシー': 'GIVENCHY',
  'ジバンシィ': 'GIVENCHY',
  'ヴァレンティノ': 'VALENTINO',
  'バーバリー': 'BURBERRY',
  'コーチ': 'COACH',
  'ケイトスペード': 'KATE SPADE',
  'トリーバーチ': 'TORY BURCH',
  'マルベリー': 'MULBERRY',
}

async function matchHandbagNames() {
  console.log('🔄 Starting handbag name matching...\n')

  try {
    // Get all listings
    const listings = await prisma.listing.findMany({
      select: {
        id: true,
        brand: true,
        model: true,
        title: true,
      },
    })

    console.log(`📊 Found ${listings.length.toLocaleString()} total listings\n`)

    let updated = 0
    let brandMatched = 0
    let modelMatched = 0

    for (const listing of listings) {
      let needsUpdate = false
      const updates = {}

      // Match brand name
      if (listing.brand) {
        for (const [japanese, english] of Object.entries(BRAND_MAPPINGS)) {
          if (listing.brand.includes(japanese)) {
            updates.brand = english
            needsUpdate = true
            brandMatched++
            break
          }
        }
      }

      // Match model name in model field or title
      const searchText = `${listing.model || ''} ${listing.title || ''}`
      for (const [japanese, english] of Object.entries(MODEL_MAPPINGS)) {
        if (searchText.includes(japanese)) {
          // Extract size if present (e.g., "25", "30", "35")
          const sizeMatch = searchText.match(/(\d{2,3})(cm|センチ)?/)
          const size = sizeMatch ? ` ${sizeMatch[1]}` : ''

          updates.model = english + size
          needsUpdate = true
          modelMatched++
          break
        }
      }

      // Update if changes were made
      if (needsUpdate) {
        await prisma.listing.update({
          where: { id: listing.id },
          data: updates,
        })
        updated++

        if (updated % 1000 === 0) {
          console.log(`✅ Updated ${updated.toLocaleString()} listings...`)
        }
      }
    }

    console.log('\n✅ Name matching complete!')
    console.log(`   📝 Total listings: ${listings.length.toLocaleString()}`)
    console.log(`   🔄 Updated: ${updated.toLocaleString()}`)
    console.log(`   🏷️  Brand matches: ${brandMatched.toLocaleString()}`)
    console.log(`   📦 Model matches: ${modelMatched.toLocaleString()}`)

  } catch (error) {
    console.error('❌ Error:', error)
    throw error
  } finally {
    await prisma.$disconnect()
  }
}

// Run the script
matchHandbagNames()