bep40 commited on
Commit
280e522
·
verified ·
1 Parent(s): 0c1d8c2

Upload ai-search.js

Browse files
Files changed (1) hide show
  1. ai-search.js +138 -0
ai-search.js ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * AI Search - Semantic search with budget extraction
3
+ * Provides natural language product search for Vietnamese queries
4
+ */
5
+
6
+ window.aiSearch = async function(query, products, token) {
7
+ if (!products || !products.length) {
8
+ return { results: [], aiAnswer: null, categories: [], constraints: [], budget: 0, combos: [] };
9
+ }
10
+
11
+ const norm = (s) => String(s || '').toLowerCase()
12
+ .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
13
+ .replace(/[đĐ]/g, 'd');
14
+
15
+ const q = norm(query);
16
+ let budget = 0;
17
+ let categories = [];
18
+ let constraints = [];
19
+ let combos = [];
20
+
21
+ // Extract budget constraint (triệu/nghìn/k/đồng)
22
+ const budgetPatterns = [
23
+ /(dưới|duoi)\s*([\d,.]+)\s*(triệu|nghin|nghìn|k|đồng)/gi,
24
+ /(tối đa)\s*([\d,.]+)\s*(triệu|nghin|nghìn|k|đồng)/gi,
25
+ /([\d,.]+)\s*(triệu|nghin|nghìn|k|đồng)/gi
26
+ ];
27
+
28
+ for (const pattern of budgetPatterns) {
29
+ const match = query.replace(/[đĐ]/g, 'd').match(pattern);
30
+ if (match) {
31
+ const amt = parseFloat(match[0].replace(/[^\d,.]/g, '').replace(',', '.'));
32
+ const unit = match[0].toLowerCase().match(/(triệu|nghin|nghìn|k|đồng)/g);
33
+ if (unit) {
34
+ if (unit[0] === 'triệu') budget = amt * 1000000;
35
+ else if (unit[0] === 'nghìn' || unit[0] === 'nghin' || unit[0] === 'k') budget = amt * 1000;
36
+ else budget = amt;
37
+ }
38
+ constraints.push({ label: 'Ngân sách: ' + match[0].trim() });
39
+ break;
40
+ }
41
+ }
42
+
43
+ // Category keywords
44
+ const catMap = {
45
+ 'bep': 'Bếp',
46
+ 'bep-tu': 'Bếp từ',
47
+ 'bep-gas': 'Bếp gas',
48
+ 'hut-mui': 'Máy hút mùi',
49
+ 'hut-khoi': 'Máy hút khói',
50
+ 'chau-rua': 'Chậu rửa',
51
+ 'voi-rua': 'Vòi rửa',
52
+ 'lo-nuong': 'Lò nướng',
53
+ 'tu-lanh': 'Tủ lạnh',
54
+ 'may-rua-chen': 'Máy rửa chén',
55
+ 'may-lam-sach': 'Máy làm sạch',
56
+ 'gia-bo': 'Kệ bếp',
57
+ 'gia-dung': 'Gia đụng'
58
+ };
59
+
60
+ Object.keys(catMap).forEach(key => {
61
+ if (q.includes(key) || query.toLowerCase().includes(key)) {
62
+ categories.push(catMap[key]);
63
+ }
64
+ });
65
+
66
+ // Search logic with scoring
67
+ let results = [];
68
+ for (let i = 0; i < products.length && results.length < 30; i++) {
69
+ const p = products[i];
70
+ let score = 0;
71
+ const name = norm(p.name || '');
72
+ const sku = norm(p.sku || p.mod || '');
73
+ const brand = norm(p.brand || '');
74
+ const cat = norm(p.cat || '');
75
+ const summary = norm(p.summary || '');
76
+
77
+ // Exact/partial matches
78
+ if (name.includes(q)) score += 10;
79
+ if (sku.includes(q)) score += 8;
80
+ if (brand.includes(q)) score += 6;
81
+ if (cat.includes(q)) score += 4;
82
+ if (summary.includes(q)) score += 3;
83
+
84
+ // Budget filter
85
+ if (budget > 0 && p.priceNum && p.priceNum > budget) {
86
+ score = 0;
87
+ }
88
+
89
+ // Word-by-word matching
90
+ const words = q.split(/\s+/).filter(w => w.length > 1);
91
+ words.forEach(w => {
92
+ if (name.includes(w)) score += 2;
93
+ if (sku.includes(w)) score += 1;
94
+ if (brand.includes(w)) score += 1;
95
+ if (cat.includes(w)) score += 1;
96
+ });
97
+
98
+ if (score > 0) {
99
+ const labels = [];
100
+ if (p.priceNum && budget > 0 && p.priceNum <= budget) {
101
+ labels.push('Trong ngân sách');
102
+ }
103
+ results.push({ p, idx: i, score, labels });
104
+ }
105
+ }
106
+
107
+ // Sort by score desc
108
+ results.sort((a, b) => b.score - a.score);
109
+
110
+ // Generate combos based on budget
111
+ if (budget > 0 && results.length >= 2) {
112
+ let comboTotal = 0;
113
+ let comboItems = [];
114
+ for (let i = 0; i < results.length && comboTotal <= budget; i++) {
115
+ comboItems.push({ p: results[i].p, idx: results[i].idx });
116
+ }
117
+ if (comboItems.length >= 2) {
118
+ combos.push({
119
+ items: comboItems,
120
+ total: comboItems.reduce((s, it) => s + (it.p.priceNum || 0), 0)
121
+ });
122
+ }
123
+ }
124
+
125
+ // Generate AI response
126
+ let aiAnswer = '';
127
+ if (results.length > 0) {
128
+ aiAnswer = `Tìm thấy ${results.length} sản phẩm phù hợp.`;
129
+ if (categories.length) aiAnswer += ` Danh mục: ${categories.join(', ')}.`;
130
+ if (budget > 0) aiAnswer += ` Ngân sách tối đa: ${(budget/1000000).toFixed(1)} triệu.`;
131
+ } else {
132
+ aiAnswer = 'Không tìm thấy sản phẩm nào. Vui lòng thử từ khóa khác như: "bếp từ", "máy hút mùi", "Malloca", "Eurogold".';
133
+ }
134
+
135
+ return { results, aiAnswer, categories, constraints, budget, combos };
136
+ };
137
+
138
+ console.log('AI Search loaded');